Merge pull request #971 from bigbluebutton/v2.5-alpha

Release v2.5
This commit is contained in:
Fred Dixon 2020-03-02 11:48:17 -05:00 committed by GitHub
commit 2af1f7242f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
82 changed files with 2148 additions and 361 deletions

View File

@ -13,6 +13,7 @@ tmp
/db/**/*.sqlite3 /db/**/*.sqlite3
/db/**/*.sqlite3-journal /db/**/*.sqlite3-journal
/db/production /db/production
/db/production-postgres
public/assets public/assets
public/b public/b
coverage/ coverage/

1
.gitignore vendored
View File

@ -19,6 +19,7 @@ vendor/bundle
# Ignore production paths. # Ignore production paths.
/db/production /db/production
/db/production-postgres
# Ignore all logfiles and tempfiles. # Ignore all logfiles and tempfiles.
/log/* /log/*

View File

@ -1,34 +1,58 @@
FROM ruby:2.5 FROM ruby:2.5.1-alpine AS base
# Install app dependencies. # Set a variable for the install location.
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev curl ARG RAILS_ROOT=/usr/src/app
# Set Rails environment.
ADD https://dl.yarnpkg.com/debian/pubkey.gpg /tmp/yarn-pubkey.gpg ENV RAILS_ENV production
ENV BUNDLE_APP_CONFIG="$RAILS_ROOT/.bundle"
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
# Make the directory and set as working. # Make the directory and set as working.
RUN mkdir -p $RAILS_ROOT RUN mkdir -p $RAILS_ROOT
WORKDIR $RAILS_ROOT WORKDIR $RAILS_ROOT
# Set Rails environment. ARG BUILD_PACKAGES="build-base curl-dev git"
ENV RAILS_ENV production ARG DEV_PACKAGES="postgresql-dev sqlite-libs sqlite-dev yaml-dev zlib-dev nodejs yarn"
ARG RUBY_PACKAGES="tzdata"
# Install app dependencies.
RUN apk update \
&& apk upgrade \
&& apk add --update --no-cache $BUILD_PACKAGES $DEV_PACKAGES $RUBY_PACKAGES
COPY Gemfile* ./ COPY Gemfile* ./
RUN bundle install --without development test --deployment --clean COPY Gemfile Gemfile.lock $RAILS_ROOT/
RUN bundle config --global frozen 1 \
&& bundle install --deployment --without development:test:assets -j4 --path=vendor/bundle \
&& rm -rf vendor/bundle/ruby/2.5.0/cache/*.gem \
&& find vendor/bundle/ruby/2.5.0/gems/ -name "*.c" -delete \
&& find vendor/bundle/ruby/2.5.0/gems/ -name "*.o" -delete
# Adding project files. # Adding project files.
COPY . . COPY . .
# Precompile assets # Remove folders not needed in resulting image
RUN SECRET_KEY_BASE="$(bundle exec rake secret)" bundle exec rake assets:clean RUN rm -rf tmp/cache spec
RUN SECRET_KEY_BASE="$(bundle exec rake secret)" bundle exec rake assets:precompile
############### Build step done ###############
FROM ruby:2.5.1-alpine
# Set a variable for the install location.
ARG RAILS_ROOT=/usr/src/app
ARG PACKAGES="tzdata curl postgresql-client sqlite-libs yarn nodejs bash"
ENV RAILS_ENV=production
ENV BUNDLE_APP_CONFIG="$RAILS_ROOT/.bundle"
WORKDIR $RAILS_ROOT
RUN apk update \
&& apk upgrade \
&& apk add --update --no-cache $PACKAGES
COPY --from=base $RAILS_ROOT $RAILS_ROOT
# Expose port 80. # Expose port 80.
EXPOSE 80 EXPOSE 80

View File

@ -23,7 +23,7 @@ gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.2' gem 'coffee-rails', '~> 4.2'
# See https://github.com/rails/execjs#readme for more supported runtimes # See https://github.com/rails/execjs#readme for more supported runtimes
gem 'mini_racer', platforms: :ruby # gem 'mini_racer', platforms: :ruby
# Use jquery as the JavaScript library # Use jquery as the JavaScript library
gem 'jquery-rails', '~> 4.3.3' gem 'jquery-rails', '~> 4.3.3'
@ -77,6 +77,7 @@ gem 'cancancan', '~> 2.0'
group :production do group :production do
# Use a postgres database in production. # Use a postgres database in production.
gem 'pg', '~> 0.18' gem 'pg', '~> 0.18'
gem 'sequel'
# For a better logging library in production # For a better logging library in production
gem "lograge" gem "lograge"
@ -121,7 +122,7 @@ end
gem 'remote_syslog_logger' gem 'remote_syslog_logger'
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem 'tzinfo-data'
gem 'coveralls', require: false gem 'coveralls', require: false

View File

@ -153,7 +153,6 @@ GEM
railties (>= 3.2.16) railties (>= 3.2.16)
json (2.2.0) json (2.2.0)
jwt (2.2.1) jwt (2.2.1)
libv8 (7.3.492.27.1)
listen (3.0.8) listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4) rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7) rb-inotify (~> 0.9, >= 0.9.7)
@ -173,8 +172,6 @@ GEM
mimemagic (0.3.3) mimemagic (0.3.3)
mini_mime (1.0.2) mini_mime (1.0.2)
mini_portile2 (2.4.0) mini_portile2 (2.4.0)
mini_racer (0.2.6)
libv8 (>= 6.9.411)
minitest (5.11.3) minitest (5.11.3)
msgpack (1.3.0) msgpack (1.3.0)
multi_json (1.13.1) multi_json (1.13.1)
@ -295,6 +292,7 @@ GEM
sprockets (> 3.0) sprockets (> 3.0)
sprockets-rails sprockets-rails
tilt tilt
sequel (5.29.0)
shoulda-matchers (3.1.3) shoulda-matchers (3.1.3)
activesupport (>= 4.0.0) activesupport (>= 4.0.0)
simplecov (0.16.1) simplecov (0.16.1)
@ -326,6 +324,8 @@ GEM
turbolinks-source (5.2.0) turbolinks-source (5.2.0)
tzinfo (1.2.5) tzinfo (1.2.5)
thread_safe (~> 0.1) thread_safe (~> 0.1)
tzinfo-data (1.2019.3)
tzinfo (>= 1.0.0)
uglifier (4.1.20) uglifier (4.1.20)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
unicode-display_width (1.6.0) unicode-display_width (1.6.0)
@ -369,7 +369,6 @@ DEPENDENCIES
jquery-ui-rails jquery-ui-rails
listen (~> 3.0.5) listen (~> 3.0.5)
lograge lograge
mini_racer
net-ldap net-ldap
omniauth omniauth
omniauth-bn-launcher! omniauth-bn-launcher!
@ -389,6 +388,7 @@ DEPENDENCIES
rspec-rails (~> 3.7) rspec-rails (~> 3.7)
rubocop rubocop
sassc-rails sassc-rails
sequel
shoulda-matchers (~> 3.1) shoulda-matchers (~> 3.1)
spring spring
spring-watcher-listen (~> 2.0.0) spring-watcher-listen (~> 2.0.0)

View File

@ -41,6 +41,49 @@ $(document).on('turbolinks:load', function(){
updateTabParams(this.id) updateTabParams(this.id)
}) })
$('.selectpicker').selectpicker({
liveSearchPlaceholder: getLocalizedString('javascript.search.start')
});
// Fixes turbolinks issue with bootstrap select
$(window).trigger('load.bs.select.data-api');
// Display merge accounts modal with correct info
$(".merge-user").click(function() {
// Update the path of save button
$("#merge-save-access").attr("data-path", $(this).data("path"))
let userInfo = $(this).data("info")
$("#merge-to").html("<span>" + userInfo.name + "</span>" + "<span class='text-muted d-block'>" + userInfo.email + "</span>" + "<span class='text-muted d-block'>" + userInfo.uid + "</span>")
})
$("#mergeUserModal").on("show.bs.modal", function() {
$(".selectpicker").selectpicker('val','')
})
$(".bootstrap-select").on("click", function() {
$(".bs-searchbox").siblings().hide()
})
$(".bs-searchbox input").on("input", function() {
if ($(".bs-searchbox input").val() == '' || $(".bs-searchbox input").val().length < 3) {
$(".bs-searchbox").siblings().hide()
} else {
$(".bs-searchbox").siblings().show()
}
})
// User selects an option from the Room Access dropdown
$(".bootstrap-select").on("changed.bs.select", function(){
// Get the uid of the selected user
let user = $(".selectpicker").selectpicker('val')
if (user != "") {
userInfo = JSON.parse(user)
$("#merge-from").html("<span>" + userInfo.name + "</span>" + "<span class='text-muted d-block'>" + userInfo.email + "</span>" + "<span id='from-uid' class='text-muted d-block'>" + userInfo.uid + "</span>")
}
})
} }
else if(action == "site_settings"){ else if(action == "site_settings"){
loadColourSelectors() loadColourSelectors()
@ -79,6 +122,11 @@ function changeBrandingImage(path) {
$.post(path, {value: url}) $.post(path, {value: url})
} }
function mergeUsers() {
let userToMerge = $("#from-uid").text()
$.post($("#merge-save-access").data("path"), {merge: userToMerge})
}
// Filters by role // Filters by role
function filterRole(role) { function filterRole(role) {
var search = new URL(location.href).searchParams.get('search') var search = new URL(location.href).searchParams.get('search')

View File

@ -34,4 +34,5 @@
//= require jquery-ui/widget //= require jquery-ui/widget
//= require jquery-ui/widgets/sortable //= require jquery-ui/widgets/sortable
//= require pickr.min.js //= require pickr.min.js
//= require bootstrap-select.min.js
//= require_tree . //= require_tree .

View File

@ -49,7 +49,16 @@ $(document).on('turbolinks:load', function(){
$("#create-room-block").click(function(){ $("#create-room-block").click(function(){
showCreateRoom(this) showCreateRoom(this)
}) })
}
// Autofocus on the Room Name label when creating a room only
$('#createRoomModal').on('shown.bs.modal', function (){
if ($(".create-only").css("display") == "block"){
$('#create-room-name').focus()
}
})
if (controller == "rooms" && action == "show" || controller == "admins" && action == "server_rooms"){
// 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
$(".update-room").click(function(){ $(".update-room").click(function(){
showUpdateRoom(this) showUpdateRoom(this)
@ -58,6 +67,69 @@ $(document).on('turbolinks:load', function(){
$(".delete-room").click(function() { $(".delete-room").click(function() {
showDeleteRoom(this) showDeleteRoom(this)
}) })
$('.selectpicker').selectpicker({
liveSearchPlaceholder: getLocalizedString('javascript.search.start')
});
// Fixes turbolinks issue with bootstrap select
$(window).trigger('load.bs.select.data-api');
$(".share-room").click(function() {
// Update the path of save button
$("#save-access").attr("data-path", $(this).data("path"))
// Get list of users shared with and display them
displaySharedUsers($(this).data("users-path"))
})
$("#shareRoomModal").on("show.bs.modal", function() {
$(".selectpicker").selectpicker('val','')
})
$(".bootstrap-select").on("click", function() {
$(".bs-searchbox").siblings().hide()
})
$(".bs-searchbox input").on("input", function() {
if ($(".bs-searchbox input").val() == '' || $(".bs-searchbox input").val().length < 3) {
$(".bs-searchbox").siblings().hide()
} else {
$(".bs-searchbox").siblings().show()
}
})
$(".remove-share-room").click(function() {
$("#remove-shared-confirm").parent().attr("action", $(this).data("path"))
})
// User selects an option from the Room Access dropdown
$(".bootstrap-select").on("changed.bs.select", function(){
// Get the uid of the selected user
let uid = $(".selectpicker").selectpicker('val')
// If the value was changed to blank, ignore it
if (uid == "") return
let currentListItems = $("#user-list li").toArray().map(user => $(user).data("uid"))
// Check to make sure that the user is not already there
if (!currentListItems.includes(uid)) {
// Create the faded list item and display it
let option = $("option[value='" + uid + "']")
let listItem = document.createElement("li")
listItem.setAttribute('class', 'list-group-item text-left not-saved add-access');
listItem.setAttribute("data-uid", uid)
let spanItem = "<span class='avatar float-left mr-2'>" + option.text().charAt(0) + "</span> <span class='shared-user'>" +
option.text() + " <span class='text-muted'>" + option.data("subtext") + "</span></span>" +
"<span class='text-primary float-right shared-user cursor-pointer' onclick='removeSharedUser(this)'><i class='fas fa-times'></i></span>"
listItem.innerHTML = spanItem
$("#user-list").append(listItem)
}
})
} }
}); });
@ -88,9 +160,10 @@ function showCreateRoom(target) {
function showUpdateRoom(target) { function showUpdateRoom(target) {
var modal = $(target) var modal = $(target)
var room_block_uid = modal.closest("#room-block").data("room-uid") var update_path = modal.closest("#room-block").data("path")
$("#create-room-name").val(modal.closest("tbody").find("#room-name h4").text()) var settings_path = modal.data("settings-path")
$("#createRoomModal form").attr("action", room_block_uid + "/update_settings") $("#create-room-name").val(modal.closest("#room-block").find("#room-name-text").text())
$("#createRoomModal form").attr("action", update_path)
//show all elements & their children with a update-only class //show all elements & their children with a update-only class
$(".update-only").each(function() { $(".update-only").each(function() {
@ -104,7 +177,7 @@ function showUpdateRoom(target) {
if($(this).children().length > 0) { $(this).children().attr('style',"display:none !important") } if($(this).children().length > 0) { $(this).children().attr('style',"display:none !important") }
}) })
updateCurrentSettings(modal.closest("#room-block").data("room-settings")) updateCurrentSettings(settings_path)
var accessCode = modal.closest("#room-block").data("room-access-code") var accessCode = modal.closest("#room-block").data("room-access-code")
@ -123,12 +196,15 @@ function showDeleteRoom(target) {
} }
//Update the createRoomModal to show the correct current settings //Update the createRoomModal to show the correct current settings
function updateCurrentSettings(settings){ function updateCurrentSettings(settings_path){
//set checkbox // Get current room settings and set checkbox
$("#room_mute_on_join").prop("checked", settings.muteOnStart) $.get(settings_path, function(room_settings) {
$("#room_require_moderator_approval").prop("checked", settings.requireModeratorApproval) var settings = JSON.parse(room_settings)
$("#room_anyone_can_start").prop("checked", settings.anyoneCanStart) $("#room_mute_on_join").prop("checked", settings.muteOnStart)
$("#room_all_join_moderator").prop("checked", settings.joinModerator) $("#room_require_moderator_approval").prop("checked", settings.requireModeratorApproval)
$("#room_anyone_can_start").prop("checked", settings.anyoneCanStart)
$("#room_all_join_moderator").prop("checked", settings.joinModerator)
})
} }
function generateAccessCode(){ function generateAccessCode(){
@ -148,3 +224,44 @@ function ResetAccessCode(){
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code_placeholder")) $("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code_placeholder"))
$("#room_access_code").val(null) $("#room_access_code").val(null)
} }
function saveAccessChanges() {
let listItemsToAdd = $("#user-list li:not(.remove-shared)").toArray().map(user => $(user).data("uid"))
$.post($("#save-access").data("path"), {add: listItemsToAdd})
}
// Get list of users shared with and display them
function displaySharedUsers(path) {
$.get(path, function(users) {
// Create list element and add to user list
var user_list_html = ""
users.forEach(function(user) {
user_list_html += "<li class='list-group-item text-left' data-uid='" + user.uid + "'>"
if (user.image) {
user_list_html += "<img id='user-image' class='avatar float-left mr-2' src='" + user.image + "'></img>"
} else {
user_list_html += "<span class='avatar float-left mr-2'>" + user.name.charAt(0) + "</span>"
}
user_list_html += "<span class='shared-user'>" + user.name + "<span class='text-muted ml-1'>" + user.uid + "</span></span>"
user_list_html += "<span class='text-primary float-right shared-user cursor-pointer' onclick='removeSharedUser(this)'><i class='fas fa-times'></i></span>"
user_list_html += "</li>"
})
$("#user-list").html(user_list_html)
});
}
// Removes the user from the list of shared users
function removeSharedUser(target) {
let parentLI = target.closest("li")
if (parentLI.classList.contains("not-saved")) {
parentLI.parentNode.removeChild(parentLI)
} else {
parentLI.removeChild(target)
parentLI.classList.add("remove-shared")
}
}

View File

@ -23,7 +23,8 @@ $(document).on('turbolinks:load', function(){
(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")) { (controller == "admins" && action == "server_recordings") ||
(controller == "admins" && action == "server_rooms")) {
// Submit search if the user hits enter // Submit search if the user hits enter
$("#search-input").keypress(function(key) { $("#search-input").keypress(function(key) {
if (key.which == 13) { if (key.which == 13) {

View File

@ -52,15 +52,12 @@ $(document).on('turbolinks:load', function(){
// Modify the ui for the tables // Modify the ui for the tables
var configure_order = function(header_elem){ var configure_order = function(header_elem){
if(header_elem.data('order') === 'asc'){ // asc if(header_elem.data('order') === 'asc'){ // asc
header_elem.text(header_elem.data("header") + " ↓");
header_elem.data('order', 'desc'); header_elem.data('order', 'desc');
} }
else if(header_elem.data('order') === 'desc'){ // desc else if(header_elem.data('order') === 'desc'){ // desc
header_elem.text(header_elem.data("header"));
header_elem.data('order', 'none'); header_elem.data('order', 'none');
} }
else{ // none else{ // none
header_elem.text(header_elem.data("header") + " ↑");
header_elem.data('order', 'asc'); header_elem.data('order', 'asc');
} }
} }

View File

@ -88,4 +88,12 @@
&:hover { &:hover {
cursor: pointer; cursor: pointer;
} }
}
#merge-account-arrow {
position: absolute;
top: 47%;
right: 47%;
z-index: 999;
background: white;
} }

View File

@ -36,14 +36,16 @@
@import "tabler-custom"; @import "tabler-custom";
@import "font-awesome-sprockets"; @import "font-awesome-sprockets";
@import "font-awesome"; @import "font-awesome";
@import "monolith.min.scss";
@import "bootstrap-select.min";
@import "utilities/variables"; @import "utilities/variables";
@import "admins"; @import "admins";
@import "main"; @import "main";
@import "rooms"; @import "rooms";
@import "sessions"; @import "sessions";
@import "monolith.min.scss";
@import "utilities/fonts"; @import "utilities/fonts";
@import "users";
* { * {
outline: none !important; outline: none !important;

View File

@ -83,3 +83,20 @@
margin-top: -6rem; margin-top: -6rem;
font-size: 5rem; font-size: 5rem;
} }
.bootstrap-select .dropdown-menu li.active small.text-muted{
color: #9aa0ac !important
}
.not-saved {
color: grey;
background: rgba(0, 40, 100, 0.12);
}
.dropdown-menu.show {
min-height: 0px !important;
}
.remove-shared {
text-decoration: line-through;
}

View File

@ -21,4 +21,12 @@
.user-role-tag{ .user-role-tag{
color: white !important; color: white !important;
}
.shared-user {
line-height: 30px;
}
.bootstrap-select {
border: 1px solid rgba(0, 40, 100, 0.12);
} }

View File

@ -18,11 +18,6 @@
class WaitingChannel < ApplicationCable::Channel class WaitingChannel < ApplicationCable::Channel
def subscribed def subscribed
Rails.logger.info "subscribed [#{params[:useruid]}:#{params[:roomuid]}]"
stream_from "#{params[:roomuid]}_waiting_channel" stream_from "#{params[:roomuid]}_waiting_channel"
end end
def unsubscribed
Rails.logger.info "unsubscribed [#{params[:useruid]}:#{params[:roomuid]}]"
end
end end

View File

@ -51,6 +51,7 @@ class AccountActivationsController < ApplicationController
flash[:alert] = I18n.t("verify.already_verified") flash[:alert] = I18n.t("verify.already_verified")
else else
# Resend # Resend
@user.create_activation_token
send_activation_email(@user) send_activation_email(@user)
end end
@ -60,14 +61,10 @@ class AccountActivationsController < ApplicationController
private private
def find_user def find_user
@user = User.find_by!(email: params[:email], provider: @user_domain) @user = User.find_by!(activation_digest: User.digest(params[:token]), provider: @user_domain)
end end
def ensure_unauthenticated def ensure_unauthenticated
redirect_to current_user.main_room if current_user redirect_to current_user.main_room if current_user
end end
def email_params
params.require(:email).permit(:email, :token)
end
end end

View File

@ -22,8 +22,9 @@ class AdminsController < ApplicationController
include Emailer include Emailer
include Recorder include Recorder
include Rolify include Rolify
include Populator
manage_users = [:edit_user, :promote, :demote, :ban_user, :unban_user, :approve, :reset] manage_users = [:edit_user, :promote, :demote, :ban_user, :unban_user, :approve, :reset, :merge_user]
manage_deleted_users = [:undelete] manage_deleted_users = [:undelete]
authorize_resource class: false authorize_resource class: false
before_action :find_user, only: manage_users before_action :find_user, only: manage_users
@ -40,7 +41,9 @@ class AdminsController < ApplicationController
@role = params[:role] ? Role.find_by(name: params[:role], provider: @user_domain) : nil @role = params[:role] ? Role.find_by(name: params[:role], provider: @user_domain) : nil
@tab = params[:tab] || "active" @tab = params[:tab] || "active"
@pagy, @users = pagy(user_list) @user_list = merge_user_list
@pagy, @users = pagy(manage_users_list)
end end
# GET /admins/site_settings # GET /admins/site_settings
@ -49,11 +52,7 @@ class AdminsController < ApplicationController
# GET /admins/server_recordings # GET /admins/server_recordings
def server_recordings def server_recordings
server_rooms = if Rails.configuration.loadbalanced_configuration server_rooms = rooms_list_for_recordings
Room.includes(:owner).where(users: { provider: @user_domain }).pluck(:bbb_id)
else
Room.pluck(:bbb_id)
end
@search, @order_column, @order_direction, recs = @search, @order_column, @order_direction, recs =
all_recordings(server_rooms, params.permit(:search, :column, :direction), true, true) all_recordings(server_rooms, params.permit(:search, :column, :direction), true, true)
@ -61,24 +60,40 @@ class AdminsController < ApplicationController
@pagy, @recordings = pagy_array(recs) @pagy, @recordings = pagy_array(recs)
end end
# GET /admins/rooms
def server_rooms
@search = params[:search] || ""
@order_column = params[:column] && params[:direction] != "none" ? params[:column] : "created_at"
@order_direction = params[:direction] && params[:direction] != "none" ? params[:direction] : "DESC"
@running_room_bbb_ids = all_running_meetings[:meetings].pluck(:meetingID)
@user_list = shared_user_list if shared_access_allowed
@pagy, @rooms = pagy_array(server_rooms_list)
end
# MANAGE USERS # MANAGE USERS
# GET /admins/edit/:user_uid # GET /admins/edit/:user_uid
def edit_user def edit_user
session[:prev_url] = request.referer if request.referer.present?
end end
# POST /admins/ban/:user_uid # POST /admins/ban/:user_uid
def ban_user def ban_user
@user.roles = [] @user.roles = []
@user.add_role :denied @user.add_role :denied
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.banned") }
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.banned") }
end end
# POST /admins/unban/:user_uid # POST /admins/unban/:user_uid
def unban_user def unban_user
@user.remove_role :denied @user.remove_role :denied
@user.add_role :user @user.add_role :user
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.unbanned") }
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.unbanned") }
end end
# POST /admins/approve/:user_uid # POST /admins/approve/:user_uid
@ -87,7 +102,7 @@ class AdminsController < ApplicationController
send_user_approved_email(@user) send_user_approved_email(@user)
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.approved") } redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.approved") }
end end
# POST /admins/approve/:user_uid # POST /admins/approve/:user_uid
@ -96,7 +111,7 @@ class AdminsController < ApplicationController
@user.undelete! @user.undelete!
@user.rooms.deleted.each(&:undelete!) @user.rooms.deleted.each(&:undelete!)
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.restored") } redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.restored") }
end end
# POST /admins/invite # POST /admins/invite
@ -118,8 +133,54 @@ class AdminsController < ApplicationController
send_password_reset_email(@user) send_password_reset_email(@user)
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.reset_password") } if session[:prev_url].present?
redirect_path = session[:prev_url]
session.delete(:prev_url)
else
redirect_path = admins_path
end
redirect_to redirect_path, flash: { success: I18n.t("administrator.flash.reset_password") }
end end
# POST /admins/merge/:user_uid
def merge_user
begin
# Get uid of user that will be merged into the other account
uid_to_merge = params[:merge]
logger.info "#{current_user.uid} is attempting to merge #{uid_to_merge} into #{@user.uid}"
# Check to make sure the 2 users are unique
raise "Can not merge the user into themself" if uid_to_merge == @user.uid
# Find user to merge
user_to_merge = User.find_by(uid: uid_to_merge)
# Move over user's rooms
user_to_merge.rooms.each do |room|
room.owner = @user
room.name = "(#{I18n.t('merged')}) #{room.name}"
room.save!
end
# Reload user to update merge rooms
user_to_merge.reload
# Delete merged user
user_to_merge.destroy(true)
rescue => e
logger.info "Failed to merge #{uid_to_merge} into #{@user.uid}: #{e}"
flash[:alert] = I18n.t("administrator.flash.merge_fail")
else
logger.info "#{current_user.uid} successfully merged #{uid_to_merge} into #{@user.uid}"
flash[:success] = I18n.t("administrator.flash.merge_success")
end
redirect_back fallback_location: admins_path
end
# SITE SETTINGS # SITE SETTINGS
# POST /admins/update_settings # POST /admins/update_settings
@ -158,6 +219,13 @@ class AdminsController < ApplicationController
end end
end end
# POST /admins/clear_auth
def clear_auth
User.include_deleted.where(provider: @user_domain).update_all(social_uid: nil)
redirect_to admin_site_settings_path, flash: { success: I18n.t("administrator.flash.settings") }
end
# POST /admins/clear_cache # POST /admins/clear_cache
def clear_cache def clear_cache
Rails.cache.delete("#{@user_domain}/getUser") Rails.cache.delete("#{@user_domain}/getUser")
@ -166,6 +234,13 @@ class AdminsController < ApplicationController
redirect_to admin_site_settings_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/log_level
def log_level
Rails.logger.level = params[:value].to_i
redirect_to admin_site_settings_path, flash: { success: I18n.t("administrator.flash.settings") }
end
# ROLES # ROLES
# GET /admins/roles # GET /admins/roles
@ -224,11 +299,11 @@ class AdminsController < ApplicationController
private private
def find_user def find_user
@user = User.where(uid: params[:user_uid]).includes(:roles).first @user = User.find_by(uid: params[:user_uid])
end end
def find_deleted_user def find_deleted_user
@user = User.deleted.where(uid: params[:user_uid]).includes(:roles).first @user = User.deleted.find_by(uid: params[:user_uid])
end end
# Verifies that admin is an administrator of the user in the action # Verifies that admin is an administrator of the user in the action
@ -237,37 +312,6 @@ class AdminsController < ApplicationController
flash: { alert: I18n.t("administrator.flash.unauthorized") } unless current_user.admin_of?(@user) flash: { alert: I18n.t("administrator.flash.unauthorized") } unless current_user.admin_of?(@user)
end end
# Gets the list of users based on your configuration
def user_list
current_role = @role
initial_user = case @tab
when "active"
User.without_role(:pending).without_role(:denied)
when "deleted"
User.deleted
else
User
end
current_role = Role.find_by(name: @tab, provider: @user_domain) if @tab == "pending" || @tab == "denied"
initial_list = if current_user.has_role? :super_admin
initial_user.where.not(id: current_user.id)
else
initial_user.without_role(:super_admin).where.not(id: current_user.id)
end
if Rails.configuration.loadbalanced_configuration
initial_list.where(provider: @user_domain)
.admins_search(@search, current_role)
.admins_order(@order_column, @order_direction)
else
initial_list.admins_search(@search, current_role)
.admins_order(@order_column, @order_direction)
end
end
# Creates the invite if it doesn't exist, or updates the updated_at time if it does # Creates the invite if it doesn't exist, or updates the updated_at time if it does
def create_or_update_invite(email) def create_or_update_invite(email)
invite = Invitation.find_by(email: email, provider: @user_domain) invite = Invitation.find_by(email: email, provider: @user_domain)

View File

@ -29,7 +29,7 @@ class ApplicationController < ActionController::Base
# Retrieves the current user. # Retrieves the current user.
def current_user def current_user
@current_user ||= User.where(id: session[:user_id]).includes(:roles).first @current_user ||= User.includes(:roles, :main_room).find_by(id: session[:user_id])
if Rails.configuration.loadbalanced_configuration if Rails.configuration.loadbalanced_configuration
if @current_user && !@current_user.has_role?(:super_admin) && if @current_user && !@current_user.has_role?(:super_admin) &&
@ -67,7 +67,7 @@ class ApplicationController < ActionController::Base
# Sets the settinfs variable # Sets the settinfs variable
def set_user_settings def set_user_settings
@settings = Setting.find_or_create_by(provider: @user_domain) @settings = Setting.includes(:features).find_or_create_by(provider: @user_domain)
end end
# Redirects the user to a Maintenance page if turned on # Redirects the user to a Maintenance page if turned on
@ -172,6 +172,12 @@ class ApplicationController < ActionController::Base
end end
helper_method :configured_providers helper_method :configured_providers
# Indicates whether users are allowed to share rooms
def shared_access_allowed
@settings.get_value("Shared Access") == "true"
end
helper_method :shared_access_allowed
# Parses the url for the user domain # Parses the url for the user domain
def parse_user_domain(hostname) def parse_user_domain(hostname)
return hostname.split('.').first if Rails.configuration.url_host.empty? return hostname.split('.').first if Rails.configuration.url_host.empty?
@ -194,7 +200,23 @@ class ApplicationController < ActionController::Base
# Manually deal with 401 errors # Manually deal with 401 errors
rescue_from CanCan::AccessDenied do |_exception| rescue_from CanCan::AccessDenied do |_exception|
render "errors/greenlight_error" if current_user
render "errors/greenlight_error"
else
# Store the current url as a cookie to redirect to after sigining in
cookies[:return_to] = request.url
# Get the correct signin path
path = if allow_greenlight_accounts?
signin_path
elsif Rails.configuration.loadbalanced_configuration
omniauth_login_url(:bn_launcher)
else
signin_path
end
redirect_to path
end
end end
private private
@ -214,6 +236,7 @@ class ApplicationController < ActionController::Base
logger.error "Error in retrieve provider info: #{e}" logger.error "Error in retrieve provider info: #{e}"
# Use the default site settings # Use the default site settings
@user_domain = "greenlight" @user_domain = "greenlight"
@settings = Setting.find_or_create_by(provider: @user_domain)
if e.message.eql? "No user with that id exists" if e.message.eql? "No user with that id exists"
render "errors/greenlight_error", locals: { message: I18n.t("errors.not_found.user_not_found.message"), render "errors/greenlight_error", locals: { message: I18n.t("errors.not_found.user_not_found.message"),

View File

@ -68,6 +68,18 @@ module Authenticator
session.delete(:user_id) if current_user session.delete(:user_id) if current_user
end end
# Check if the user is using local accounts
def auth_changed_to_local?(user)
Rails.configuration.loadbalanced_configuration && user.social_uid.present? && allow_greenlight_accounts?
end
# Check if the user exists under the same email with no social uid and that social accounts are allowed
def auth_changed_to_social?(email)
Rails.configuration.loadbalanced_configuration &&
User.exists?(email: email, provider: @user_domain, social_uid: nil) &&
!allow_greenlight_accounts?
end
private private
# Migrates all of the twitter users rooms to the new account # Migrates all of the twitter users rooms to the new account

View File

@ -29,6 +29,11 @@ module BbbServer
bbb_server.is_meeting_running?(bbb_id) bbb_server.is_meeting_running?(bbb_id)
end end
# Returns a list of all running meetings
def all_running_meetings
bbb_server.get_meetings
end
def get_recordings(meeting_id) def get_recordings(meeting_id)
bbb_server.get_recordings(meetingID: meeting_id) bbb_server.get_recordings(meetingID: meeting_id)
end end

View File

@ -101,7 +101,8 @@ module Emailer
return unless Rails.configuration.enable_email_verification return unless Rails.configuration.enable_email_verification
admin_emails = admin_emails() admin_emails = admin_emails()
UserMailer.approval_user_signup(user, admins_url, admin_emails, @settings).deliver_now unless admin_emails.empty? UserMailer.approval_user_signup(user, admins_url(tab: "pending"),
admin_emails, @settings).deliver_now unless admin_emails.empty?
rescue => e rescue => e
logger.error "Support: Error in email delivery: #{e}" logger.error "Support: Error in email delivery: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error")) flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
@ -124,7 +125,7 @@ module Emailer
# Returns the link the user needs to click to verify their account # Returns the link the user needs to click to verify their account
def user_verification_link(user) def user_verification_link(user)
edit_account_activation_url(token: user.activation_token, email: user.email) edit_account_activation_url(token: user.activation_token)
end end
def admin_emails def admin_emails
@ -139,7 +140,7 @@ module Emailer
end end
def reset_link(user) def reset_link(user)
edit_password_reset_url(user.reset_token, email: user.email) edit_password_reset_url(user.reset_token)
end end
def invitation_link(token) def invitation_link(token)

View File

@ -0,0 +1,96 @@
# 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/>.
module Populator
extend ActiveSupport::Concern
# Returns a list of users that are in the same context of the current user
def manage_users_list
current_role = @role
initial_user = case @tab
when "active"
User.includes(:roles).without_role(:pending).without_role(:denied)
when "deleted"
User.includes(:roles).deleted
else
User.includes(:roles)
end
current_role = Role.find_by(name: @tab, provider: @user_domain) if @tab == "pending" || @tab == "denied"
initial_list = if current_user.has_role? :super_admin
initial_user.where.not(id: current_user.id)
else
initial_user.without_role(:super_admin).where.not(id: current_user.id)
end
if Rails.configuration.loadbalanced_configuration
initial_list.where(provider: @user_domain)
.admins_search(@search, current_role)
.admins_order(@order_column, @order_direction)
else
initial_list.admins_search(@search, current_role)
.admins_order(@order_column, @order_direction)
end
end
# Returns a list of rooms that are in the same context of the current user
def server_rooms_list
if Rails.configuration.loadbalanced_configuration
Room.includes(:owner).where(users: { provider: @user_domain })
.admins_search(@search)
.admins_order(@order_column, @order_direction)
else
Room.includes(:owner).all.admins_search(@search).admins_order(@order_column, @order_direction)
end
end
# Returns list of rooms needed to get the recordings on the server
def rooms_list_for_recordings
if Rails.configuration.loadbalanced_configuration
Room.includes(:owner).where(users: { provider: @user_domain }).pluck(:bbb_id)
else
Room.pluck(:bbb_id)
end
end
# Returns a list of users that are in the same context of the current user
def shared_user_list
roles_can_appear = []
Role.where(provider: @user_domain).each do |role|
roles_can_appear << role.name if role.get_permission("can_appear_in_share_list") && role.priority >= 0
end
initial_list = User.where.not(uid: current_user.uid)
.without_role(:pending)
.without_role(:denied)
.with_highest_priority_role(roles_can_appear)
return initial_list unless Rails.configuration.loadbalanced_configuration
initial_list.where(provider: @user_domain)
end
# Returns a list of users that can merged into another user
def merge_user_list
initial_list = User.where.not(uid: current_user.uid).without_role(:super_admin)
return initial_list unless Rails.configuration.loadbalanced_configuration
initial_list.where(provider: @user_domain)
end
end

View File

@ -119,17 +119,32 @@ module Rolify
return false if role.priority <= current_user_role.priority || role.provider != @user_domain return false if role.priority <= current_user_role.priority || role.provider != @user_domain
end end
# Update the roles priority including the user role # Get the priority of the current user's role and start with 1 higher
top_priority = 0 new_priority = [current_user_role.priority, 0].max + 1
role_to_update.each_with_index do |id, index| begin
new_priority = index + [current_user_role.priority, 0].max + 1 # Save the old priorities incase something fails
top_priority = new_priority old_priority = Role.where(id: role_to_update).select(:id, :priority).index_by(&:id)
Role.where(id: id).update_all(priority: new_priority)
# Set all the priorities to nil to avoid unique column issues
Role.where(id: role_to_update).update_all(priority: nil)
# Starting at the starting priority, increase by 1 every time
role_to_update.each_with_index do |id, index|
Role.find(id).update_attribute(:priority, new_priority + index)
end
true
rescue => e
# Reset to old prorities
role_to_update.each_with_index do |id, _index|
Role.find(id).update_attribute(:priority, old_priority[id.to_i].priority)
end
logger.error "#{current_user} failed to update role priorities: #{e}"
false
end end
user_role.priority = top_priority + 1
user_role.save!
end end
# Update Permissions # Update Permissions
@ -141,7 +156,8 @@ module Rolify
role_params = params.require(:role).permit(:name) role_params = params.require(:role).permit(:name)
permission_params = params.require(:role).permit(:can_create_rooms, :send_promoted_email, permission_params = params.require(:role).permit(:can_create_rooms, :send_promoted_email,
:send_demoted_email, :can_edit_site_settings, :can_edit_roles, :can_manage_users, :colour) :send_demoted_email, :can_edit_site_settings, :can_edit_roles, :can_manage_users,
:can_manage_rooms_recordings, :can_appear_in_share_list, :colour)
permission_params.transform_values! do |v| permission_params.transform_values! do |v|
if v == "0" if v == "0"

View File

@ -56,6 +56,8 @@ class PasswordResetsController < ApplicationController
# Password does not match password confirmation # Password does not match password confirmation
flash.now[:alert] = I18n.t("password_different_notice") flash.now[:alert] = I18n.t("password_different_notice")
elsif @user.update_attributes(user_params) elsif @user.update_attributes(user_params)
# Clear the user's social uid if they are switching from a social to a local account
@user.update_attribute(:social_uid, nil) if @user.social_uid.present?
# Successfully reset password # Successfully reset password
return redirect_to root_path, flash: { success: I18n.t("password_reset_success") } return redirect_to root_path, flash: { success: I18n.t("password_reset_success") }
end end
@ -66,7 +68,7 @@ class PasswordResetsController < ApplicationController
private private
def find_user def find_user
@user = User.find_by(email: params[:email]) @user = User.find_by(reset_digest: User.digest(params[:id]), provider: @user_domain)
end end
def user_params def user_params

View File

@ -20,14 +20,18 @@ class RoomsController < ApplicationController
include Pagy::Backend include Pagy::Backend
include Recorder include Recorder
include Joiner include Joiner
include Populator
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],
unless: -> { !Rails.configuration.enable_email_verification } unless: -> { !Rails.configuration.enable_email_verification }
before_action :find_room, except: [:create, :join_specific_room] before_action :find_room, except: [:create, :join_specific_room]
before_action :verify_room_ownership, only: [:destroy, :start, :update_settings] before_action :verify_room_ownership_or_admin_or_shared, only: [:start, :shared_access]
before_action :verify_room_ownership_or_admin, only: [:update_settings, :destroy]
before_action :verify_room_ownership_or_shared, only: [:remove_shared_access]
before_action :verify_room_owner_verified, only: [:show, :join], before_action :verify_room_owner_verified, only: [:show, :join],
unless: -> { !Rails.configuration.enable_email_verification } unless: -> { !Rails.configuration.enable_email_verification }
before_action :verify_room_owner_valid, only: [:show, :join]
before_action :verify_user_not_admin, only: [:show] before_action :verify_user_not_admin, only: [:show]
# POST / # POST /
@ -60,14 +64,17 @@ class RoomsController < ApplicationController
def show def show
@anyone_can_start = JSON.parse(@room[:room_settings])["anyoneCanStart"] @anyone_can_start = JSON.parse(@room[:room_settings])["anyoneCanStart"]
@room_running = room_running?(@room.bbb_id) @room_running = room_running?(@room.bbb_id)
@shared_room = room_shared_with_user
# If its the current user's room # If its the current user's room
if current_user && @room.owned_by?(current_user) if current_user && (@room.owned_by?(current_user) || @shared_room)
if current_user.highest_priority_role.get_permission("can_create_rooms") if current_user.highest_priority_role.get_permission("can_create_rooms")
# User is allowed to have rooms # User is allowed to have rooms
@search, @order_column, @order_direction, recs = @search, @order_column, @order_direction, recs =
recordings(@room.bbb_id, params.permit(:search, :column, :direction), true) recordings(@room.bbb_id, params.permit(:search, :column, :direction), true)
@user_list = shared_user_list if shared_access_allowed
@pagy, @recordings = pagy_array(recs) @pagy, @recordings = pagy_array(recs)
else else
# Render view for users that cant create rooms # Render view for users that cant create rooms
@ -112,10 +119,21 @@ class RoomsController < ApplicationController
# DELETE /:room_uid # DELETE /:room_uid
def destroy def destroy
# Don't delete the users home room. begin
@room.destroy if @room.owned_by?(current_user) && @room != current_user.main_room # Don't delete the users home room.
raise I18n.t("room.delete.home_room") if @room == @room.owner.main_room
@room.destroy
rescue => e
flash[:alert] = I18n.t("room.delete.fail", error: e)
else
flash[:success] = I18n.t("room.delete.success")
end
redirect_to current_user.main_room # Redirect to home room if the redirect_back location is the deleted room
return redirect_to @current_user.main_room if request.referer == room_url(@room)
# Redirect to the location that the user deleted the room from
redirect_back fallback_location: current_user.main_room
end end
# POST /room/join # POST /room/join
@ -162,7 +180,6 @@ class RoomsController < ApplicationController
begin begin
options = params[:room].nil? ? params : params[:room] options = params[:room].nil? ? params : params[:room]
raise "Room name can't be blank" if options[:name].blank? raise "Room name can't be blank" if options[:name].blank?
raise "Unauthorized Request" if !@room.owned_by?(current_user) || @room == current_user.main_room
# Update the rooms values # Update the rooms values
room_settings_string = create_room_settings_string(options) room_settings_string = create_room_settings_string(options)
@ -179,7 +196,64 @@ class RoomsController < ApplicationController
flash[:alert] = I18n.t("room.update_settings_error") flash[:alert] = I18n.t("room.update_settings_error")
end end
redirect_to room_path redirect_back fallback_location: room_path(@room)
end
# POST /:room_uid/update_shared_access
def shared_access
begin
current_list = @room.shared_users.pluck(:id)
new_list = User.where(uid: params[:add]).pluck(:id)
# Get the list of users that used to be in the list but were removed
users_to_remove = current_list - new_list
# Get the list of users that are in the new list but not in the current list
users_to_add = new_list - current_list
# Remove users that are removed
SharedAccess.where(room_id: @room.id, user_id: users_to_remove).delete_all unless users_to_remove.empty?
# Add users that are added
users_to_add.each do |id|
SharedAccess.create(room_id: @room.id, user_id: id)
end
flash[:success] = I18n.t("room.shared_access_success")
rescue => e
logger.error "Support: Error in updating room shared access: #{e}"
flash[:alert] = I18n.t("room.shared_access_error")
end
redirect_back fallback_location: room_path
end
# POST /:room_uid/remove_shared_access
def remove_shared_access
begin
SharedAccess.find_by!(room_id: @room.id, user_id: params[:user_id]).destroy
flash[:success] = I18n.t("room.remove_shared_access_success")
rescue => e
logger.error "Support: Error in removing room shared access: #{e}"
flash[:alert] = I18n.t("room.remove_shared_access_error")
end
redirect_to current_user.main_room
end
# GET /:room_uid/shared_users
def shared_users
# Respond with JSON object of users that have access to the room
respond_to do |format|
format.json { render body: @room.shared_users.to_json }
end
end
# GET /:room_uid/room_settings
def room_settings
# Respond with JSON object of the room_settings
respond_to do |format|
format.json { render body: @room.room_settings.to_json }
end
end end
# GET /:room_uid/logout # GET /:room_uid/logout
@ -219,12 +293,24 @@ class RoomsController < ApplicationController
# Find the room from the uid. # Find the room from the uid.
def find_room def find_room
@room = Room.find_by!(uid: params[:room_uid]) @room = Room.includes(:owner).find_by!(uid: params[:room_uid])
end end
# Ensure the user is logged into the room they are accessing. # Ensure the user either owns the room or is an admin of the room owner or the room is shared with him
def verify_room_ownership def verify_room_ownership_or_admin_or_shared
return redirect_to root_path unless @room.owned_by?(current_user) return redirect_to root_path unless @room.owned_by?(current_user) ||
room_shared_with_user ||
current_user&.admin_of?(@room.owner)
end
# Ensure the user either owns the room or is an admin of the room owner
def verify_room_ownership_or_admin
return redirect_to root_path if !@room.owned_by?(current_user) && !current_user&.admin_of?(@room.owner)
end
# Ensure the user owns the room or is allowed to start it
def verify_room_ownership_or_shared
return redirect_to root_path unless @room.owned_by?(current_user) || room_shared_with_user
end end
def validate_accepted_terms def validate_accepted_terms
@ -236,10 +322,12 @@ class RoomsController < ApplicationController
end end
def verify_room_owner_verified def verify_room_owner_verified
unless @room.owner.activated? redirect_to root_path, alert: t("room.unavailable") unless @room.owner.activated?
flash[:alert] = t("room.unavailable") end
redirect_to root_path
end # Check to make sure the room owner is not pending or banned
def verify_room_owner_valid
redirect_to root_path, alert: t("room.owner_banned") if @room.owner.has_role?(:pending) || @room.owner.has_role?(:denied)
end end
def verify_user_not_admin def verify_user_not_admin
@ -250,6 +338,11 @@ class RoomsController < ApplicationController
@settings.get_value("Room Authentication") == "true" && current_user.nil? @settings.get_value("Room Authentication") == "true" && current_user.nil?
end end
# Checks if the room is shared with the user and room sharing is enabled
def room_shared_with_user
shared_access_allowed ? @room.shared_with?(current_user) : false
end
def room_limit_exceeded def room_limit_exceeded
limit = @settings.get_value("Room Limit").to_i limit = @settings.get_value("Room Limit").to_i

View File

@ -74,6 +74,10 @@ class SessionsController < ApplicationController
# Check user with that email exists # Check user with that email exists
return redirect_to(signin_path, alert: I18n.t("invalid_credentials")) unless user return redirect_to(signin_path, alert: I18n.t("invalid_credentials")) unless user
# Check if authenticators have switched
return switch_account_to_local(user) if !is_super_admin && auth_changed_to_local?(user)
# Check correct password was entered # Check correct password was entered
return redirect_to(signin_path, alert: I18n.t("invalid_credentials")) unless user.try(:authenticate, return redirect_to(signin_path, alert: I18n.t("invalid_credentials")) unless user.try(:authenticate,
session_params[:password]) session_params[:password])
@ -84,7 +88,10 @@ class SessionsController < ApplicationController
# Check that the user is a Greenlight account # Check that the user is a Greenlight account
return redirect_to(root_path, alert: I18n.t("invalid_login_method")) unless user.greenlight_account? return redirect_to(root_path, alert: I18n.t("invalid_login_method")) unless user.greenlight_account?
# Check that the user has verified their account # Check that the user has verified their account
return redirect_to(account_activation_path(email: user.email)) unless user.activated? unless user.activated?
user.create_activation_token
return redirect_to(account_activation_path(token: user.activation_token))
end
end end
login(user) login(user)
@ -199,6 +206,9 @@ class SessionsController < ApplicationController
# If using invitation registration method, make sure user is invited # If using invitation registration method, make sure user is invited
return redirect_to root_path, flash: { alert: I18n.t("registration.invite.no_invite") } unless passes_invite_reqs return redirect_to root_path, flash: { alert: I18n.t("registration.invite.no_invite") } unless passes_invite_reqs
# Switch the user to a social account if they exist under the same email with no social uid
switch_account_to_social if !@user_exists && auth_changed_to_social?(@auth['info']['email'])
user = User.from_omniauth(@auth) user = User.from_omniauth(@auth)
logger.info "Support: Auth user #{user.email} is attempting to login." logger.info "Support: Auth user #{user.email} is attempting to login."
@ -225,4 +235,28 @@ class SessionsController < ApplicationController
end end
end end
end end
# Send the user a password reset email to allow them to set their password
def switch_account_to_local(user)
logger.info "Switching social account to local account for #{user.uid}"
# Send the user a reset password email
user.create_reset_digest
send_password_reset_email(user)
# Overwrite the flash with a more descriptive message if successful
flash[:success] = I18n.t("reset_password.auth_change") if flash[:success].present?
redirect_to signin_path
end
# Set the user's social id to the new id being passed
def switch_account_to_social
user = User.find_by(email: @auth['info']['email'], provider: @user_domain, social_uid: nil)
logger.info "Switching account to social account for #{user.uid}"
# Set the user's social id to the one being returned from auth
user.update_attribute(:social_uid, @auth['uid'])
end
end end

View File

@ -26,7 +26,7 @@ class ThemesController < ApplicationController
lighten_color = @settings.get_value("Primary Color Lighten") || Rails.configuration.primary_color_lighten_default lighten_color = @settings.get_value("Primary Color Lighten") || Rails.configuration.primary_color_lighten_default
darken_color = @settings.get_value("Primary Color Darken") || Rails.configuration.primary_color_darken_default darken_color = @settings.get_value("Primary Color Darken") || Rails.configuration.primary_color_darken_default
file_name = Rails.root.join('app', 'assets', 'stylesheets', 'utilities', '_primary_themes.scss') file_name = Rails.root.join('lib', 'assets', '_primary_themes.scss')
@file_contents = File.read(file_name) @file_contents = File.read(file_name)
# Include the variables and covert scss file to css # Include the variables and covert scss file to css

View File

@ -58,6 +58,8 @@ class UsersController < ApplicationController
# Sign in automatically if email verification is disabled or if user is already verified. # Sign in automatically if email verification is disabled or if user is already verified.
login(@user) && return if !Rails.configuration.enable_email_verification || @user.email_verified login(@user) && return if !Rails.configuration.enable_email_verification || @user.email_verified
@user.create_activation_token
send_activation_email(@user) send_activation_email(@user)
redirect_to root_path redirect_to root_path
@ -80,7 +82,14 @@ class UsersController < ApplicationController
# PATCH /u/:user_uid/edit # PATCH /u/:user_uid/edit
def update def update
profile = params[:setting] == "password" ? change_password_path(@user) : edit_user_path(@user) profile = params[:setting] == "password" ? change_password_path(@user) : edit_user_path(@user)
redirect_path = current_user.admin_of?(@user) ? admins_path : profile if session[:prev_url].present?
path = session[:prev_url]
session.delete(:prev_url)
else
path = admins_path
end
redirect_path = current_user.admin_of?(@user) ? path : profile
if params[:setting] == "password" if params[:setting] == "password"
# Update the users password. # Update the users password.
@ -123,12 +132,13 @@ class UsersController < ApplicationController
# DELETE /u/:user_uid # DELETE /u/:user_uid
def destroy def destroy
# Include deleted users in the check # Include deleted users in the check
admin_path = request.referer.present? ? request.referer : admins_path
@user = User.include_deleted.find_by(uid: params[:user_uid]) @user = User.include_deleted.find_by(uid: params[:user_uid])
logger.info "Support: #{current_user.email} is deleting #{@user.email}." logger.info "Support: #{current_user.email} is deleting #{@user.email}."
self_delete = current_user == @user self_delete = current_user == @user
redirect_url = self_delete ? root_path : admins_path redirect_url = self_delete ? root_path : admin_path
begin begin
if current_user && (self_delete || current_user.admin_of?(@user)) if current_user && (self_delete || current_user.admin_of?(@user))
@ -183,7 +193,7 @@ class UsersController < ApplicationController
private private
def find_user def find_user
@user = User.where(uid: params[:user_uid]).includes(:roles).first @user = User.find_by(uid: params[:user_uid])
end end
# Verify that GreenLight is configured to allow user signup. # Verify that GreenLight is configured to allow user signup.

View File

@ -37,6 +37,14 @@ module AdminsHelper
end end
end end
def shared_access_string
if @settings.get_value("Shared Access") == "true"
I18n.t("administrator.site_settings.authentication.enabled")
else
I18n.t("administrator.site_settings.authentication.disabled")
end
end
def recording_default_visibility_string def recording_default_visibility_string
if @settings.get_value("Default Recording Visibility") == "public" if @settings.get_value("Default Recording Visibility") == "public"
I18n.t("recording.visibility.public") I18n.t("recording.visibility.public")
@ -56,6 +64,23 @@ module AdminsHelper
end end
end end
def log_level_string
case Rails.logger.level
when 0
t("administrator.site_settings.log_level.debug")
when 1
t("administrator.site_settings.log_level.info")
when 2
t("administrator.site_settings.log_level.warn")
when 3
t("administrator.site_settings.log_level.error")
when 4
t("administrator.site_settings.log_level.fatal")
when 5
t("administrator.site_settings.log_level.unknown")
end
end
def room_limit_number def room_limit_number
@settings.get_value("Room Limit").to_i @settings.get_value("Room Limit").to_i
end end
@ -63,4 +88,9 @@ module AdminsHelper
def edit_disabled def edit_disabled
@edit_disabled ||= @selected_role.priority <= current_user.highest_priority_role.priority @edit_disabled ||= @selected_role.priority <= current_user.highest_priority_role.priority
end end
# Get the room status to display in the Server Rooms table
def room_is_running(id)
@running_room_bbb_ids.include?(id)
end
end end

View File

@ -57,7 +57,6 @@ module ApplicationHelper
# Returns the page that the logo redirects to when clicked on # Returns the page that the logo redirects to when clicked on
def home_page 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_role? :super_admin
current_user.main_room current_user.main_room
end end

View File

@ -24,18 +24,13 @@ module RecordingsHelper
# Helper for converting BigBlueButton dates into a nice length string. # Helper for converting BigBlueButton dates into a nice length string.
def recording_length(playbacks) def recording_length(playbacks)
# Stats format currently doesn't support length. # Looping through playbacks array and returning first non-zero length value
valid_playbacks = playbacks.reject { |p| p[:type] == "statistics" } playbacks.each do |playback|
return "0 min" if valid_playbacks.empty? length = playback[:length]
return recording_length_string(length) unless length.zero?
len = valid_playbacks.first[:length]
if len > 60
"#{(len / 60).to_i} h #{len % 60} min"
elsif len.zero?
"< 1 min"
else
"#{len} min"
end end
# Return '< 1 min' if length values are zero
"< 1 min"
end end
# Prevents single images from erroring when not passed as an array. # Prevents single images from erroring when not passed as an array.
@ -51,4 +46,15 @@ module RecordingsHelper
def recording_thumbnails? def recording_thumbnails?
Rails.configuration.recording_thumbnails Rails.configuration.recording_thumbnails
end end
private
# Returns length of the recording as a string
def recording_length_string(len)
if len > 60
"#{(len / 60).to_i} h #{len % 60} min"
else
"#{len} min"
end
end
end end

View File

@ -27,7 +27,7 @@ class Ability
else else
highest_role = user.highest_priority_role highest_role = user.highest_priority_role
if highest_role.get_permission("can_edit_site_settings") if highest_role.get_permission("can_edit_site_settings")
can [:index, :site_settings, :server_recordings, :update_settings, :coloring, :registration_method], :admin can [:index, :site_settings, :update_settings, :coloring, :registration_method], :admin
end end
if highest_role.get_permission("can_edit_roles") if highest_role.get_permission("can_edit_roles")
@ -36,11 +36,13 @@ class Ability
if highest_role.get_permission("can_manage_users") if highest_role.get_permission("can_manage_users")
can [:index, :roles, :edit_user, :promote, :demote, :ban_user, :unban_user, can [:index, :roles, :edit_user, :promote, :demote, :ban_user, :unban_user,
:approve, :invite, :reset, :undelete], :admin :approve, :invite, :reset, :undelete, :merge_user], :admin
end end
can [:index, :server_recordings, :server_rooms], :admin if highest_role.get_permission("can_manage_rooms_recordings")
if !highest_role.get_permission("can_edit_site_settings") && !highest_role.get_permission("can_edit_roles") && if !highest_role.get_permission("can_edit_site_settings") && !highest_role.get_permission("can_edit_roles") &&
!highest_role.get_permission("can_manage_users") !highest_role.get_permission("can_manage_users") && !highest_role.get_permission("can_manage_rooms_recordings")
cannot :manage, AdminsController cannot :manage, AdminsController
end end
end end

View File

@ -48,6 +48,9 @@ module AuthValues
case auth['provider'] case auth['provider']
when :twitter when :twitter
auth['info']['image'].gsub("http", "https").gsub("_normal", "") auth['info']['image'].gsub("http", "https").gsub("_normal", "")
when :ldap
return auth['info']['image'] if auth['info']['image']&.starts_with?("http")
""
else else
auth['info']['image'] auth['info']['image']
end end

View File

@ -20,7 +20,7 @@ class Role < ApplicationRecord
has_and_belongs_to_many :users, join_table: :users_roles has_and_belongs_to_many :users, join_table: :users_roles
has_many :role_permissions has_many :role_permissions
default_scope { order(:priority) } default_scope { includes(:role_permissions).order(:priority) }
scope :by_priority, -> { order(:priority) } scope :by_priority, -> { order(:priority) }
scope :editable_roles, ->(provider) { where(provider: provider).where.not(name: %w[super_admin denied pending]) } scope :editable_roles, ->(provider) { where(provider: provider).where.not(name: %w[super_admin denied pending]) }
@ -35,14 +35,14 @@ class Role < ApplicationRecord
.update_all_role_permissions(can_create_rooms: true) .update_all_role_permissions(can_create_rooms: true)
Role.create(name: "admin", provider: provider, priority: 0, colour: "#f1c40f") Role.create(name: "admin", provider: provider, priority: 0, colour: "#f1c40f")
.update_all_role_permissions(can_create_rooms: true, send_promoted_email: true, .update_all_role_permissions(can_create_rooms: true, send_promoted_email: true,
send_demoted_email: true, can_edit_site_settings: true, send_demoted_email: true, can_edit_site_settings: true, can_manage_rooms_recordings: true,
can_edit_roles: true, can_manage_users: true) can_edit_roles: true, can_manage_users: true)
Role.create(name: "pending", provider: provider, priority: -1, colour: "#17a2b8").update_all_role_permissions Role.create(name: "pending", provider: provider, priority: -1, colour: "#17a2b8").update_all_role_permissions
Role.create(name: "denied", provider: provider, priority: -1, colour: "#343a40").update_all_role_permissions Role.create(name: "denied", provider: provider, priority: -2, colour: "#343a40").update_all_role_permissions
Role.create(name: "super_admin", provider: provider, priority: -2, colour: "#cd201f") Role.create(name: "super_admin", provider: provider, priority: -3, colour: "#cd201f")
.update_all_role_permissions(can_create_rooms: true, .update_all_role_permissions(can_create_rooms: true,
send_promoted_email: true, send_demoted_email: true, can_edit_site_settings: true, send_promoted_email: true, send_demoted_email: true, can_edit_site_settings: true,
can_edit_roles: true, can_manage_users: true) can_edit_roles: true, can_manage_users: true, can_manage_rooms_recordings: true)
end end
def self.create_new_role(role_name, provider) def self.create_new_role(role_name, provider)
@ -55,8 +55,8 @@ class Role < ApplicationRecord
role.priority = user_role.priority role.priority = user_role.priority
user_role.priority += 1 user_role.priority += 1
role.save!
user_role.save! user_role.save!
role.save!
role role
end end
@ -68,10 +68,15 @@ class Role < ApplicationRecord
update_permission("can_edit_site_settings", permissions[:can_edit_site_settings].to_s) update_permission("can_edit_site_settings", permissions[:can_edit_site_settings].to_s)
update_permission("can_edit_roles", permissions[:can_edit_roles].to_s) update_permission("can_edit_roles", permissions[:can_edit_roles].to_s)
update_permission("can_manage_users", permissions[:can_manage_users].to_s) update_permission("can_manage_users", permissions[:can_manage_users].to_s)
update_permission("can_manage_rooms_recordings", permissions[:can_manage_rooms_recordings].to_s)
update_permission("can_appear_in_share_list", permissions[:can_appear_in_share_list].to_s)
end end
# Updates the value of the permission and enables it # Updates the value of the permission and enables it
def update_permission(name, value) def update_permission(name, value)
# Dont update if it is not explicitly set to a value
return unless value.present?
permission = role_permissions.find_or_create_by!(name: name) permission = role_permissions.find_or_create_by!(name: name)
permission.update_attributes(value: value, enabled: true) permission.update_attributes(value: value, enabled: true)
@ -79,18 +84,36 @@ class Role < ApplicationRecord
# Returns the value if enabled or the default if not enabled # Returns the value if enabled or the default if not enabled
def get_permission(name, return_boolean = true) def get_permission(name, return_boolean = true)
permission = role_permissions.find_or_create_by!(name: name) value = nil
value = if permission[:enabled] role_permissions.each do |permission|
permission[:value] next if permission.name != name
else
"false" value = if permission.enabled
permission.value
else
default_value(name)
end
end end
# Create the role_permissions since it doesn't exist
role_permissions.create(name: name) if value.nil?
if return_boolean if return_boolean
value == "true" value == "true"
else else
value value
end end
end end
private
def default_value(name)
case name
when "can_appear_in_share_list"
Rails.configuration.shared_access_default.to_s
else
"false"
end
end
end end

View File

@ -26,11 +26,46 @@ class Room < ApplicationRecord
validates :name, presence: true validates :name, presence: true
belongs_to :owner, class_name: 'User', foreign_key: :user_id belongs_to :owner, class_name: 'User', foreign_key: :user_id
has_many :shared_access
def self.admins_search(string)
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"
"created_at::text"
else
"created_at"
end
search_query = "rooms.name LIKE :search OR rooms.uid LIKE :search OR users.email LIKE :search" \
" OR users.#{created_at_query} LIKE :search"
search_param = "%#{string}%"
where(search_query, search: search_param)
end
def self.admins_order(column, direction)
# Include the owner of the table
table = joins(:owner)
return table.order(Arel.sql("rooms.#{column} #{direction}")) if table.column_names.include?(column) || column == "users.name"
table
end
# Determines if a user owns a room. # Determines if a user owns a room.
def owned_by?(user) def owned_by?(user)
user_id == user&.id
end
def shared_users
User.where(id: shared_access.pluck(:user_id))
end
def shared_with?(user)
return false if user.nil? return false if user.nil?
user.rooms.include?(self) shared_users.include?(user)
end end
# Determines the invite path for the room. # Determines the invite path for the room.

View File

@ -28,22 +28,36 @@ class Setting < ApplicationRecord
# Returns the value if enabled or the default if not enabled # Returns the value if enabled or the default if not enabled
def get_value(name) def get_value(name)
feature = features.find_or_create_by!(name: name) # Return feature value if already exists
if feature[:enabled] features.each do |feature|
feature[:value] next if feature.name != name
else
case name return feature.value if feature.enabled
when "Branding Image" return default_value(name)
Rails.configuration.branding_image_default end
when "Primary Color"
Rails.configuration.primary_color_default # Create the feature since it doesn't exist
when "Registration Method" features.create(name: name)
Rails.configuration.registration_method_default default_value(name)
when "Room Authentication" end
false
when "Room Limit" private
Rails.configuration.number_of_rooms_default
end def default_value(name)
# return default value
case name
when "Branding Image"
Rails.configuration.branding_image_default
when "Primary Color"
Rails.configuration.primary_color_default
when "Registration Method"
Rails.configuration.registration_method_default
when "Room Authentication"
false
when "Room Limit"
Rails.configuration.number_of_rooms_default
when "Shared Access"
Rails.configuration.shared_access_default
end end
end end
end end

View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
class SharedAccess < ApplicationRecord
belongs_to :room
end

View File

@ -21,7 +21,7 @@ require 'bbb_api'
class User < ApplicationRecord class User < ApplicationRecord
include Deleteable include Deleteable
attr_accessor :reset_token attr_accessor :reset_token, :activation_token
after_create :setup_user after_create :setup_user
before_save { email.try(:downcase!) } before_save { email.try(:downcase!) }
@ -29,9 +29,10 @@ class User < ApplicationRecord
before_destroy :destroy_rooms before_destroy :destroy_rooms
has_many :rooms has_many :rooms
has_many :shared_access
belongs_to :main_room, class_name: 'Room', foreign_key: :room_id, required: false belongs_to :main_room, class_name: 'Room', foreign_key: :room_id, required: false
has_and_belongs_to_many :roles, -> { includes :role_permissions }, join_table: :users_roles has_and_belongs_to_many :roles, join_table: :users_roles
validates :name, length: { maximum: 256 }, presence: true validates :name, length: { maximum: 256 }, presence: true
validates :provider, presence: true validates :provider, presence: true
@ -102,6 +103,11 @@ class User < ApplicationRecord
order(Arel.sql("#{column} #{direction}")) order(Arel.sql("#{column} #{direction}"))
end end
# Returns a list of rooms ordered by last session
def ordered_rooms
[main_room] + rooms.where.not(id: main_room.id).order("last_session desc")
end
# Activates an account and initialize a users main room # Activates an account and initialize a users main room
def activate def activate
update_attributes(email_verified: true, activated_at: Time.zone.now) update_attributes(email_verified: true, activated_at: Time.zone.now)
@ -121,7 +127,7 @@ class User < ApplicationRecord
def authenticated?(attribute, token) def authenticated?(attribute, token)
digest = send("#{attribute}_digest") digest = send("#{attribute}_digest")
return false if digest.nil? return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token) digest == Digest::SHA256.base64digest(token)
end end
# Return true if password reset link expires # Return true if password reset link expires
@ -129,10 +135,9 @@ class User < ApplicationRecord
reset_sent_at < 2.hours.ago reset_sent_at < 2.hours.ago
end end
# Retrives a list of all a users rooms that are not the main room, sorted by last session date. # Retrieves a list of rooms that are shared with the user
def secondary_rooms def shared_rooms
room_list = rooms.where.not(uid: main_room.uid) Room.where(id: shared_access.pluck(:room_id))
room_list.where.not(last_session: nil).order("last_session desc") + room_list.where(last_session: nil)
end end
def name_chunk def name_chunk
@ -153,9 +158,9 @@ class User < ApplicationRecord
social_uid.nil? social_uid.nil?
end end
def activation_token def create_activation_token
# Create the token. self.activation_token = User.new_token
create_reset_activation_digest(User.new_token) update_attributes(activation_digest: User.digest(activation_token))
end end
def admin_of?(user) def admin_of?(user)
@ -172,8 +177,7 @@ class User < ApplicationRecord
end end
def self.digest(string) def self.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost Digest::SHA256.base64digest(string)
BCrypt::Password.create(string, cost: cost)
end end
# Returns a random token. # Returns a random token.
@ -183,7 +187,7 @@ class User < ApplicationRecord
# role functions # role functions
def highest_priority_role def highest_priority_role
roles.by_priority.first roles.min_by(&:priority)
end end
def add_role(role) def add_role(role)
@ -217,7 +221,11 @@ class User < ApplicationRecord
# rubocop:disable Naming/PredicateName # rubocop:disable Naming/PredicateName
def has_role?(role) def has_role?(role)
# rubocop:enable Naming/PredicateName # rubocop:enable Naming/PredicateName
roles.exists?(name: role) roles.each do |single_role|
return true if single_role.name.eql? role.to_s
end
false
end end
def self.with_role(role) def self.with_role(role)
@ -228,19 +236,24 @@ class User < ApplicationRecord
User.where.not(id: with_role(role).pluck(:id)) User.where.not(id: with_role(role).pluck(:id))
end end
def self.with_highest_priority_role(role)
User.all_users_highest_priority_role.where(roles: { name: role })
end
def self.all_users_with_roles def self.all_users_with_roles
User.joins("INNER JOIN users_roles ON users_roles.user_id = users.id INNER JOIN roles " \ User.joins("INNER JOIN users_roles ON users_roles.user_id = users.id INNER JOIN roles " \
"ON roles.id = users_roles.role_id INNER JOIN role_permissions ON roles.id = role_permissions.role_id").distinct "ON roles.id = users_roles.role_id INNER JOIN role_permissions ON roles.id = role_permissions.role_id").distinct
end end
private def self.all_users_highest_priority_role
User.joins("INNER JOIN (SELECT user_id, min(roles.priority) as role_priority FROM users_roles " \
def create_reset_activation_digest(token) "INNER JOIN roles ON users_roles.role_id = roles.id GROUP BY user_id) as a ON " \
# Create the digest and persist it. "a.user_id = users.id INNER JOIN roles ON roles.priority = a.role_priority " \
update_attribute(:activation_digest, User.digest(token)) " INNER JOIN role_permissions ON roles.id = role_permissions.role_id").distinct
token
end end
private
# Destory a users rooms when they are removed. # Destory a users rooms when they are removed.
def destroy_rooms def destroy_rooms
rooms.destroy_all rooms.destroy_all

View File

@ -22,7 +22,7 @@
<div class="card-body"> <div class="card-body">
<p><%= t("verify.not_verified") %></p> <p><%= t("verify.not_verified") %></p>
<div class="btn-list text-right pt-8"> <div class="btn-list text-right pt-8">
<%= button_to t("verify.resend"), resend_email_path, params: { email: params['email'], email_verified: false }, class: "btn btn-primary btn-space" %> <%= button_to t("verify.resend"), resend_email_path, params: { token: params['token'], email_verified: false }, class: "btn btn-primary btn-space", "data-disable": "" %>
</div> </div>
</div> </div>
</div> </div>

View File

@ -21,17 +21,22 @@
<span class="icon mr-3"><i class="fas fa-users"></i></span><%= t("administrator.users.title") %> <span class="icon mr-3"><i class="fas fa-users"></i></span><%= t("administrator.users.title") %>
<% end %> <% end %>
<% end %> <% end %>
<% if highest_role.get_permission("can_edit_site_settings") || highest_role.name == "super_admin" %> <% if highest_role.get_permission("can_manage_rooms_recordings") || highest_role.name == "super_admin" %>
<%= link_to admin_recordings_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "server_recordings"}" do %> <%= link_to admin_rooms_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "server_rooms"}" do %>
<span class="icon mr-4"><i class="fas fa-video"></i></i></span><%= t("administrator.recordings.title") %> <span class="icon mr-4"><i class="fas fa-binoculars"></i></span><%= t("administrator.rooms.title") %>
<% end %> <% 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></span><%= t("administrator.recordings.title") %>
<% end %>
<% end %>
<% if highest_role.get_permission("can_edit_site_settings") || highest_role.name == "super_admin" %>
<%= link_to admin_site_settings_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "site_settings"}" do %> <%= 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") %> <span class="icon mr-4"><i class="fas fa-cogs"></i></span><%= t("administrator.site_settings.title") %>
<% end %> <% end %>
<% end %> <% end %>
<% if highest_role.get_permission("can_edit_roles") || highest_role.name == "super_admin" %> <% if highest_role.get_permission("can_edit_roles") || highest_role.name == "super_admin" %>
<%= link_to admin_roles_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "roles"}" do %> <%= link_to admin_roles_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "roles"}" do %>
<span class="icon mr-4"><i class="fas fa-user-tag"></i></i></span><%= t("administrator.roles.title") %> <span class="icon mr-4"><i class="fas fa-user-tag"></i></span><%= t("administrator.roles.title") %>
<% end %> <% end %>
<% end %> <% end %>
</div> </div>

View File

@ -53,14 +53,14 @@
<%= f.check_box :can_create_rooms, checked: @selected_role.get_permission("can_create_rooms"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("can_create_rooms") %> <%= f.check_box :can_create_rooms, checked: @selected_role.get_permission("can_create_rooms"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("can_create_rooms") %>
<span class="custom-switch-indicator float-right"></span> <span class="custom-switch-indicator float-right"></span>
</label> </label>
<label class="custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block <%="form-disable" if !current_role.get_permission("send_promoted_email") %>"> <label class="custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block <%="form-disable" if !current_role.get_permission("can_manage_users") %>">
<span class="ml-0 custom-switch-description"><%= t("administrator.roles.promote_email")%></span> <span class="ml-0 custom-switch-description"><%= t("administrator.roles.manage_users")%></span>
<%= f.check_box :send_promoted_email, checked: @selected_role.get_permission("send_promoted_email"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("send_promoted_email") %> <%= f.check_box :can_manage_users, checked: @selected_role.get_permission("can_manage_users"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("can_manage_users") %>
<span class="custom-switch-indicator float-right"></span> <span class="custom-switch-indicator float-right"></span>
</label> </label>
<label class="custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block <%="form-disable" if !current_role.get_permission("send_demoted_email") %>"> <label class="custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block <%="form-disable" if !current_role.get_permission("can_manage_rooms_recordings") %>">
<span class="ml-0 custom-switch-description"><%= t("administrator.roles.demote_email")%></span> <span class="ml-0 custom-switch-description"><%= t("administrator.roles.manage_rooms_recordings")%></span>
<%= f.check_box :send_demoted_email, checked: @selected_role.get_permission("send_demoted_email"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("send_demoted_email") %> <%= f.check_box :can_manage_rooms_recordings, checked: @selected_role.get_permission("can_manage_rooms_recordings"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("can_manage_rooms_recordings") %>
<span class="custom-switch-indicator float-right"></span> <span class="custom-switch-indicator float-right"></span>
</label> </label>
<label class="custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block <%="form-disable" if !current_role.get_permission("can_edit_site_settings") %>"> <label class="custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block <%="form-disable" if !current_role.get_permission("can_edit_site_settings") %>">
@ -73,9 +73,19 @@
<%= f.check_box :can_edit_roles, checked: @selected_role.get_permission("can_edit_roles"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("can_edit_roles") %> <%= f.check_box :can_edit_roles, checked: @selected_role.get_permission("can_edit_roles"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("can_edit_roles") %>
<span class="custom-switch-indicator float-right"></span> <span class="custom-switch-indicator float-right"></span>
</label> </label>
<label class="custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block <%="form-disable" if !current_role.get_permission("can_manage_users") %>"> <label class="custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block <%="form-disable" if !current_role.get_permission("can_appear_in_share_list") %>">
<span class="ml-0 custom-switch-description"><%= t("administrator.roles.manage_users")%></span> <span class="ml-0 custom-switch-description"><%= t("administrator.roles.appear_in_share_list")%></span>
<%= f.check_box :can_manage_users, checked: @selected_role.get_permission("can_manage_users"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("can_manage_users") %> <%= f.check_box :can_appear_in_share_list, checked: @selected_role.get_permission("can_appear_in_share_list"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("can_appear_in_share_list") %>
<span class="custom-switch-indicator float-right"></span>
</label>
<label class="custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block <%="form-disable" if !current_role.get_permission("send_promoted_email") %>">
<span class="ml-0 custom-switch-description"><%= t("administrator.roles.promote_email")%></span>
<%= f.check_box :send_promoted_email, checked: @selected_role.get_permission("send_promoted_email"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("send_promoted_email") %>
<span class="custom-switch-indicator float-right"></span>
</label>
<label class="custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block <%="form-disable" if !current_role.get_permission("send_demoted_email") %>">
<span class="ml-0 custom-switch-description"><%= t("administrator.roles.demote_email")%></span>
<%= f.check_box :send_demoted_email, checked: @selected_role.get_permission("send_demoted_email"), class: "custom-switch-input", disabled: edit_disabled || !current_role.get_permission("send_demoted_email") %>
<span class="custom-switch-indicator float-right"></span> <span class="custom-switch-indicator float-right"></span>
</label> </label>

View File

@ -0,0 +1,65 @@
<%
# 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="rooms-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("administrator.users.table.name") %>
<% if @order_column == "name" && @order_direction == "desc" %>
<% elsif @order_column == "name" && @order_direction == "asc" %>
<% end %>
</th>
<th data-header="users.name" data-order="<%= @order_column == "users.name" ? @order_direction : "none" %>">
<%= t("room.owner") %>
<% if @order_column == "users.name" && @order_direction == "desc" %>
<% elsif @order_column == "users.name" && @order_direction == "asc" %>
<% end %>
</th>
<th data-header="uid" data-order="<%= @order_column == "uid" ? @order_direction : "none" %>">
<%= t("administrator.rooms.table.id") %>
<% if @order_column == "uid" && @order_direction == "desc" %>
<% elsif @order_column == "uid" && @order_direction == "asc" %>
<% end %>
</th>
<th>
<%= t("administrator.rooms.table.status") %>
</th>
<th class="text-center"><i class="icon-settings"></i></th>
</tr>
</thead>
<tbody id="rooms-table">
<% @rooms.each do |room| %>
<%= render "admins/components/server_room_row", room: room %>
<% end %>
</tbody>
</table>
<% if !@rooms.empty?%>
<div class="float-right mr-4 mt-4">
<%== pagy_bootstrap_nav(@pagy) %>
</div>
<% end %>
</div>
</div>
</div>

View File

@ -46,10 +46,10 @@
<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> <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 %> <% end %>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <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 %> <%= button_to update_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID], state: "public"), class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon fas fa-globe"></i> <%= t("recording.visibility.public") %> <i class="dropdown-icon fas fa-globe"></i> <%= t("recording.visibility.public") %>
<% end %> <% end %>
<%= button_to update_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID], state: "unlisted"), class: "dropdown-item" do %> <%= button_to update_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID], state: "unlisted"), class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon fas fa-link"></i> <%= t("recording.visibility.unlisted") %> <i class="dropdown-icon fas fa-link"></i> <%= t("recording.visibility.unlisted") %>
<% end %> <% end %>
</div> </div>
@ -73,7 +73,7 @@
<a class="dropdown-item email-link" data-pres-link="<%= recording_links %>"><i class="dropdown-icon far fa-envelope"></i> <%= t("recording.email") %></a> <a class="dropdown-item email-link" data-pres-link="<%= recording_links %>"><i class="dropdown-icon far fa-envelope"></i> <%= t("recording.email") %></a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<% end %> <% end %>
<%= button_to delete_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID]), method: :delete, class: "dropdown-item" do %> <%= button_to delete_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID]), method: :delete, class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon far fa-trash-alt"></i> <%= t("delete") %> <i class="dropdown-icon far fa-trash-alt"></i> <%= t("delete") %>
<% end %> <% end %>
</div> </div>

View File

@ -0,0 +1,72 @@
<%
# 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 id="room-block" data-path="<%= update_settings_path(room) %>" data-room-settings=<%= room.room_settings %> data-room-access-code="<%= room.access_code %>">
<td>
<div id="room-title" class="form-inline edit_hover_class">
<% if room.id == room.owner.room_id %>
<i class="fas fa-home pr-1"></i>
<% end %>
<text id="room-name-text">
<%= room.name %>
</text>
</div>
<div class="small text-muted">
<%= [t("administrator.users.table.created"), ": ", room.created_at].join %>
</div>
</td>
<td class="text-left">
<%= room.owner.name %>
</td>
<td class="text-left">
<%= room.uid %>
</td>
<td class="text-left">
<% running = room_is_running(room.bbb_id) %>
<% if running %>
<%= t("administrator.rooms.running") %>
<% else %>
<%= t("administrator.rooms.not_running") %>
<% 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">
<%= link_to room_path(room), class: "dropdown-item" do %>
<i class="dropdown-icon far fa-eye"></i> <%= t("administrator.rooms.view") %>
<% end %>
<%= button_to start_room_path(room), class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon fas fa-door-open"></i> <%= running ? t("room.join") : t("room.start") %>
<% end %>
<a href="" data-toggle="modal" data-target="#createRoomModal" class="update-room dropdown-item">
<i class="dropdown-icon fas fa-cog"></i> <%= t("room.settings") %>
</a>
<% if shared_access_allowed %>
<a href="" data-toggle="modal" data-target="#shareRoomModal" class="share-room dropdown-item" data-path="<%= room_shared_access_path(room) %>" data-users-path="<%= room_shared_users_path(room) %>">
<i class="dropdown-icon fas fa-users"></i> <%= t("room.share") %>
</a>
<% end %>
<% if room.id != room.owner.room_id %>
<a href="" data-toggle="modal" data-target="#deleteRoomModal" data-path="<%= room_path(room) %>" data-name="<%= room.name %>" class="delete-room dropdown-item">
<i class="dropdown-icon far fa-trash-alt"></i> <%= t("delete") %>
</a>
<% end %>
</div>
</div>
</td>
</tr>

View File

@ -64,13 +64,13 @@
<%= registration_method_string %> <%= registration_method_string %>
</button> </button>
<div class="dropdown-menu" aria-labelledby="registrationMethods"> <div class="dropdown-menu" aria-labelledby="registrationMethods">
<%= button_to admin_change_registration_path(value: "open"), class: "dropdown-item" do %> <%= button_to admin_change_registration_path(value: "open"), class: "dropdown-item", "data-disable": "" do %>
<%= t("administrator.site_settings.registration.methods.open") %> <%= t("administrator.site_settings.registration.methods.open") %>
<% end %> <% end %>
<%= button_to admin_change_registration_path(value: "invite"), class: "dropdown-item" do %> <%= button_to admin_change_registration_path(value: "invite"), class: "dropdown-item", "data-disable": "" do %>
<%= t("administrator.site_settings.registration.methods.invite") %> <%= t("administrator.site_settings.registration.methods.invite") %>
<% end %> <% end %>
<%= button_to admin_change_registration_path(value: "approval"), class: "dropdown-item" do %> <%= button_to admin_change_registration_path(value: "approval"), class: "dropdown-item", "data-disable": "" do %>
<%= t("administrator.site_settings.registration.methods.approval") %> <%= t("administrator.site_settings.registration.methods.approval") %>
<% end %> <% end %>
</div> </div>
@ -88,10 +88,10 @@
<%= room_authentication_string %> <%= room_authentication_string %>
</button> </button>
<div class="dropdown-menu" aria-labelledby="room-auth"> <div class="dropdown-menu" aria-labelledby="room-auth">
<%= button_to admin_update_settings_path(setting: "Room Authentication", value: "true"), class: "dropdown-item" do %> <%= button_to admin_update_settings_path(setting: "Room Authentication", value: "true"), class: "dropdown-item", "data-disable": "" do %>
<%= t("administrator.site_settings.authentication.enabled") %> <%= t("administrator.site_settings.authentication.enabled") %>
<% end %> <% end %>
<%= button_to admin_update_settings_path(setting: "Room Authentication", value: "false"), class: "dropdown-item" do %> <%= button_to admin_update_settings_path(setting: "Room Authentication", value: "false"), class: "dropdown-item", "data-disable": "" do %>
<%= t("administrator.site_settings.authentication.disabled") %> <%= t("administrator.site_settings.authentication.disabled") %>
<% end %> <% end %>
</div> </div>
@ -99,7 +99,28 @@
</div> </div>
</div> </div>
</div> </div>
<div class="mb-6 row"> <div class="mb-6 row">
<div class="col-12">
<div class="form-group">
<label class="form-label"><%= t("administrator.site_settings.shared_access.title") %></label>
<label class="form-label text-muted"><%= t("administrator.site_settings.shared_access.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">
<%= shared_access_string %>
</button>
<div class="dropdown-menu" aria-labelledby="room-auth">
<%= button_to admin_update_settings_path(setting: "Shared Access", value: "true"), class: "dropdown-item" do %>
<%= t("administrator.site_settings.authentication.enabled") %>
<% end %>
<%= button_to admin_update_settings_path(setting: "Shared Access", value: "false"), class: "dropdown-item" do %>
<%= t("administrator.site_settings.authentication.disabled") %>
<% end %>
</div>
</div>
</div>
</div>
</div>
<div class="mb-6 row">
<div class="col-12"> <div class="col-12">
<div class="form-group"> <div class="form-group">
<label class="form-label"><%= t("administrator.site_settings.recording_visibility.title") %></label> <label class="form-label"><%= t("administrator.site_settings.recording_visibility.title") %></label>
@ -109,10 +130,10 @@
<%= recording_default_visibility_string %> <%= recording_default_visibility_string %>
</button> </button>
<div class="dropdown-menu" aria-labelledby="room-auth"> <div class="dropdown-menu" aria-labelledby="room-auth">
<%= button_to admin_update_settings_path(setting: "Default Recording Visibility", value: "public"), class: "dropdown-item" do %> <%= button_to admin_update_settings_path(setting: "Default Recording Visibility", value: "public"), class: "dropdown-item", "data-disable": "" do %>
<%= t("recording.visibility.public") %> <%= t("recording.visibility.public") %>
<% end %> <% end %>
<%= button_to admin_update_settings_path(setting: "Default Recording Visibility", value: "private"), class: "dropdown-item" do %> <%= button_to admin_update_settings_path(setting: "Default Recording Visibility", value: "private"), class: "dropdown-item", "data-disable": "" do %>
<%= t("recording.visibility.unlisted") %> <%= t("recording.visibility.unlisted") %>
<% end %> <% end %>
</div> </div>
@ -128,25 +149,25 @@
<div class="row gutters-xs"> <div class="row gutters-xs">
<div class="col-auto"> <div class="col-auto">
<label class="colorinput"> <label class="colorinput">
<%= button_to admin_update_settings_path(setting: "Room Limit", value: 1), class: "colorinput-input" do %><% end %> <%= button_to admin_update_settings_path(setting: "Room Limit", value: 1), class: "colorinput-input", "data-disable": "" do %><% end %>
<span class="colorinput-color <%= room_limit_number == 1 ? "btn-primary" : "btn-outline-primary" %>">1</span> <span class="colorinput-color <%= room_limit_number == 1 ? "btn-primary" : "btn-outline-primary" %>">1</span>
</label> </label>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<label class="colorinput"> <label class="colorinput">
<%= button_to admin_update_settings_path(setting: "Room Limit", value: 5), class: "colorinput-input" do %><% end %> <%= button_to admin_update_settings_path(setting: "Room Limit", value: 5), class: "colorinput-input", "data-disable": "" do %><% end %>
<span class="colorinput-color <%= room_limit_number == 5 ? "btn-primary" : "btn-outline-primary" %>">5</span> <span class="colorinput-color <%= room_limit_number == 5 ? "btn-primary" : "btn-outline-primary" %>">5</span>
</label> </label>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<label class="colorinput"> <label class="colorinput">
<%= button_to admin_update_settings_path(setting: "Room Limit", value: 10), class: "colorinput-input" do %><% end %> <%= button_to admin_update_settings_path(setting: "Room Limit", value: 10), class: "colorinput-input", "data-disable": "" do %><% end %>
<span class="colorinput-color <%= room_limit_number == 10 ? "btn-primary" : "btn-outline-primary" %>">10</span> <span class="colorinput-color <%= room_limit_number == 10 ? "btn-primary" : "btn-outline-primary" %>">10</span>
</label> </label>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<label class="colorinput"> <label class="colorinput">
<%= button_to admin_update_settings_path(setting: "Room Limit", value: 15), class: "colorinput-input" do %><% end %> <%= button_to admin_update_settings_path(setting: "Room Limit", value: 15), class: "colorinput-input", "data-disable": "" do %><% end %>
<span class="colorinput-color <%= room_limit_number == 15 ? "btn-primary" : "btn-outline-primary" %>">15+</span> <span class="colorinput-color <%= room_limit_number == 15 ? "btn-primary" : "btn-outline-primary" %>">15+</span>
</label> </label>
</div> </div>
@ -155,12 +176,55 @@
</div> </div>
</div> </div>
<% if current_user.has_role? :super_admin%> <% if current_user.has_role? :super_admin%>
<hr>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="mb-6 form-group"> <div class="mb-6 form-group">
<label class="form-label"><%= t("administrator.site_settings.cache.title") %></label> <label class="form-label"><%= t("administrator.site_settings.cache.title") %></label>
<label class="form-label text-muted"><%= t("administrator.site_settings.cache.info") %></label> <label class="form-label text-muted"><%= t("administrator.site_settings.cache.info") %></label>
<%= button_to t("administrator.site_settings.cache.button"), admin_clear_cache_path, class: "btn btn-primary" %> <%= button_to t("administrator.site_settings.cache.button"), admin_clear_cache_path, class: "btn btn-primary", "data-disable": "" %>
</div>
</div>
</div>
<div class="mb-4 row">
<div class="col-12">
<div class="form-group">
<label class="form-label"><%= t("administrator.site_settings.clear_auth.title") %></label>
<label class="form-label text-muted"><%= t("administrator.site_settings.clear_auth.info") %></label>
<%= button_to t("administrator.site_settings.clear_auth.button"), admin_clear_auth_path, class: "btn btn-primary" %>
</div>
</div>
</div>
<div class="mb-4 row">
<div class="col-12">
<div class="form-group">
<label class="form-label"><%= t("administrator.site_settings.log_level.title") %></label>
<label class="form-label text-muted"><%= t("administrator.site_settings.log_level.information") %></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">
<%= log_level_string %>
</button>
<div class="dropdown-menu" aria-labelledby="room-auth">
<%= button_to admin_log_level_path(value: 0), class: "dropdown-item", "data-disable": "" do %>
<%= t("administrator.site_settings.log_level.debug") %>
<% end %>
<%= button_to admin_log_level_path(value: 1), class: "dropdown-item", "data-disable": "" do %>
<%= t("administrator.site_settings.log_level.info") %>
<% end %>
<%= button_to admin_log_level_path(value: 2), class: "dropdown-item", "data-disable": "" do %>
<%= t("administrator.site_settings.log_level.warn") %>
<% end %>
<%= button_to admin_log_level_path(value: 3), class: "dropdown-item", "data-disable": "" do %>
<%= t("administrator.site_settings.log_level.error") %>
<% end %>
<%= button_to admin_log_level_path(value: 4), class: "dropdown-item", "data-disable": "" do %>
<%= t("administrator.site_settings.log_level.fatal") %>
<% end %>
<%= button_to admin_log_level_path(value: 5), class: "dropdown-item", "data-disable": "" do %>
<%= t("administrator.site_settings.log_level.unknown") %>
<% end %>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -100,31 +100,34 @@
</a> </a>
<div class="dropdown-menu dropdown-menu"> <div class="dropdown-menu dropdown-menu">
<% if user.deleted? %> <% if user.deleted? %>
<%= button_to admin_undelete_path(user_uid: user.uid), class: "dropdown-item" do %> <%= button_to admin_undelete_path(user_uid: user.uid), class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon fas fa-recycle"></i> <%= t("administrator.users.settings.undelete") %> <i class="dropdown-icon fas fa-recycle"></i> <%= t("administrator.users.settings.undelete") %>
<% end %> <% end %>
<button class="delete-user dropdown-item" data-path="<%= delete_user_path(user_uid: user.uid, permanent: "true") %>" data-toggle="modal" data-target="#deleteAccountModal"> <button class="delete-user dropdown-item" data-path="<%= delete_user_path(user_uid: user.uid, permanent: "true") %>" data-toggle="modal" data-target="#deleteAccountModal">
<i class="dropdown-icon fas fa-skull-crossbones"></i> <%= t("administrator.users.settings.perm_delete") %> <i class="dropdown-icon fas fa-skull-crossbones"></i> <%= t("administrator.users.settings.perm_delete") %>
</button> </button>
<% elsif roles.include?("denied") %> <% elsif roles.include?("denied") %>
<%= button_to admin_unban_path(user_uid: user.uid), class: "dropdown-item" do %> <%= button_to admin_unban_path(user_uid: user.uid), class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon fas fa-lock-open"></i> <%= t("administrator.users.settings.unban") %> <i class="dropdown-icon fas fa-lock-open"></i> <%= t("administrator.users.settings.unban") %>
<% end %> <% end %>
<button class= "delete-user dropdown-item" data-path="<%= delete_user_path(user_uid: user.uid) %>" data-delete="temp-delete" data-toggle="modal" data-target="#deleteAccountModal"> <button class= "delete-user dropdown-item" data-path="<%= delete_user_path(user_uid: user.uid) %>" data-delete="temp-delete" data-toggle="modal" data-target="#deleteAccountModal">
<i class="dropdown-icon fas fa-user-minus"></i> <%= t("administrator.users.settings.delete") %> <i class="dropdown-icon fas fa-user-minus"></i> <%= t("administrator.users.settings.delete") %>
</button> </button>
<% elsif roles.include?("pending") %> <% elsif roles.include?("pending") %>
<%= button_to admin_approve_path(user_uid: user.uid), class: "dropdown-item" do %> <%= button_to admin_approve_path(user_uid: user.uid), class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon far fa-check-circle"></i> <%= t("administrator.users.settings.approve") %> <i class="dropdown-icon far fa-check-circle"></i> <%= t("administrator.users.settings.approve") %>
<% end %> <% end %>
<%= button_to admin_ban_path(user_uid: user.uid), class: "dropdown-item" do %> <%= button_to admin_ban_path(user_uid: user.uid), class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon far fa-times-circle"></i> <%= t("administrator.users.settings.decline") %> <i class="dropdown-icon far fa-times-circle"></i> <%= t("administrator.users.settings.decline") %>
<% end %> <% end %>
<% else %> <% else %>
<%= link_to admin_edit_user_path(user_uid: user.uid), class: "dropdown-item" do %> <%= link_to admin_edit_user_path(user_uid: user.uid), class: "dropdown-item" do %>
<i class="dropdown-icon fas fa-user-edit"></i> <%= t("administrator.users.settings.edit") %> <i class="dropdown-icon fas fa-user-edit"></i> <%= t("administrator.users.settings.edit") %>
<% end %> <% end %>
<%= button_to admin_ban_path(user_uid: user.uid), class: "dropdown-item" do %> <button class= "merge-user dropdown-item" data-path="<%= merge_user_path(user_uid: user.uid) %>" data-info="<%= user.slice(:name, :email, :uid).to_json %>" data-toggle="modal" data-target="#mergeUserModal">
<i class="dropdown-icon fas fa-user-friends"></i> <%= t("administrator.users.settings.merge") %>
</button>
<%= button_to admin_ban_path(user_uid: user.uid), class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon fas fa-lock"></i> <%= t("administrator.users.settings.ban") %> <i class="dropdown-icon fas fa-lock"></i> <%= t("administrator.users.settings.ban") %>
<% end %> <% end %>
<button class= "delete-user dropdown-item" data-path="<%= delete_user_path(user_uid: user.uid) %>" data-delete="temp-delete" data-toggle="modal" data-target="#deleteAccountModal"> <button class= "delete-user dropdown-item" data-path="<%= delete_user_path(user_uid: user.uid) %>" data-delete="temp-delete" data-toggle="modal" data-target="#deleteAccountModal">
@ -157,3 +160,4 @@
<%= render "shared/modals/invite_user_modal" %> <%= render "shared/modals/invite_user_modal" %>
<%= render "shared/modals/delete_account_modal", delete_location: relative_root %> <%= render "shared/modals/delete_account_modal", delete_location: relative_root %>
<%= render "shared/modals/merge_user_modal" %>

View File

@ -0,0 +1,33 @@
<%
# 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: "rooms", setting_title: t("administrator.rooms.title"), search: true %>
</div>
</div>
</div>
<%= render "shared/modals/delete_room_modal" %>
<%= render "shared/modals/create_room_modal" %>
<% if shared_access_allowed %>
<%= render "shared/modals/share_room_modal" %>
<% end %>

View File

@ -13,7 +13,7 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. # 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 %> data-room-access-code="<%= room.access_code %>" class="card"> <div id="room-block" data-path="<%= update_settings_path(room) %>" data-room-access-code="<%= room.access_code %>" class="card">
<div class="card-body p-1"> <div class="card-body p-1">
<table class="table table-hover table-vcenter text-wrap table-no-border"> <table class="table table-hover table-vcenter text-wrap table-no-border">
<tbody class="no-border-top"> <tbody class="no-border-top">
@ -28,11 +28,7 @@
</td> </td>
<td> <td>
<div id="room-name"> <div id="room-name">
<% if room == current_user.main_room %> <h4 id="room-name-text" contenteditable="false" class="m-0 force-text-normal" ><%= room.name %></h4>
<h4 contenteditable="false" class="m-0 force-text-normal" ><%= t("home_room") %></h4>
<% else %>
<h4 contenteditable="false" class="m-0 force-text-normal" ><%= room.name %></h4>
<% end %>
</div> </div>
<div id="room-name-editable" style="display: none"> <div id="room-name-editable" style="display: none">
<input id="room-name-editable-input" class="form-control input-sm w-100 h-4" value="<%= room.name %>"> <input id="room-name-editable-input" class="form-control input-sm w-100 h-4" value="<%= room.name %>">
@ -45,20 +41,27 @@
<% end %> <% end %>
</div> </div>
</td> </td>
<td class="text-right"> <td class="text-right">
<div class="item-action dropdown" data-display="static"> <div class="item-action dropdown" data-display="static">
<a href="javascript:void(0)" data-toggle="dropdown" data-display="static" class="icon <%= 'invisible' if room == current_user.main_room %>"> <a href="javascript:void(0)" data-toggle="dropdown" data-display="static" class="icon">
<i class="fas fa-ellipsis-v px-4"></i> <i class="fas fa-ellipsis-v px-4"></i>
</a> </a>
<div class="dropdown-menu dropdown-menu-right dropdown-menu-md-left"> <div class="dropdown-menu dropdown-menu-right dropdown-menu-md-left">
<a href="" data-toggle="modal" data-target="#createRoomModal" class="update-room dropdown-item"> <a href="" data-toggle="modal" data-target="#createRoomModal" class="update-room dropdown-item" data-settings-path="<%= room_settings_path(room) %>">
<i class="dropdown-icon fas fa-cog"></i> <%= t("room.settings") %> <i class="dropdown-icon fas fa-cog"></i> <%= t("room.settings") %>
</a> </a>
<a href="" data-toggle="modal" data-target="#deleteRoomModal" data-path="<%= room_path(room) %>" data-name="<%= room.name %>" class="delete-room dropdown-item"> <% if shared_access_allowed %>
<i class="dropdown-icon far fa-trash-alt"></i> <%= t("delete") %> <a href="" data-toggle="modal" data-target="#shareRoomModal" class="share-room dropdown-item" data-path="<%= room_shared_access_path(room) %>" data-users-path="<%= room_shared_users_path(room) %>">
</a> <i class="dropdown-icon fas fa-users"></i> <%= t("room.share") %>
</a>
<% end %>
<% unless room == current_user.main_room %>
<a href="" data-toggle="modal" data-target="#deleteRoomModal" data-path="<%= room_path(room) %>" data-name="<%= room.name %>" class="delete-room dropdown-item">
<i class="dropdown-icon far fa-trash-alt"></i> <%= t("delete") %>
</a>
<% end %>
</div> </div>
</div> </div>
</td> </td>
</tbody> </tbody>
</table> </table>

View File

@ -24,7 +24,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-8 col-sm-12 form-inline mb-5 align-top"> <div class="col-lg-6 col-md-6 col-sm-12 form-inline mb-5 align-top">
<% if @room.owner.image.blank? %> <% if @room.owner.image.blank? %>
<span class="avatar"><%= @room.owner.name.first %></span> <span class="avatar"><%= @room.owner.name.first %></span>
<% else %> <% else %>
@ -33,7 +33,7 @@
<h5 class="font-weight-normal ml-4 mt-3"><%= @room.owner.name %> (<%= t("room.owner") %>)</h5> <h5 class="font-weight-normal ml-4 mt-3"><%= @room.owner.name %> (<%= t("room.owner") %>)</h5>
</div> </div>
<div class="col-lg-6 col-md-4 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<%= yield %> <%= yield %>
</div> </div>
</div> </div>

View File

@ -0,0 +1,48 @@
<%
# 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 id="room-block" 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">
<td>
<span class="stamp stamp-md bg-primary">
<i class="fas fa-share-alt"></i>
</span>
</td>
<td>
<div id="room-name">
<h4 contenteditable="false" class="m-0 force-text-normal" ><%= room.name %></h4>
</div>
<div class="small text-muted text-break">
<%= t("room.shared_by", email: room.owner.name) %>
</div>
</td>
<td class="text-right">
<div class="item-action dropdown" data-display="static">
<a href="javascript:void(0)" data-toggle="dropdown" data-display="static" class="icon">
<i class="fas fa-ellipsis-v px-4"></i>
</a>
<div class="dropdown-menu dropdown-menu-right dropdown-menu-md-left">
<a href="" data-toggle="modal" data-target="#removeAccessModal" class="remove-share-room dropdown-item" data-path="<%= room_remove_shared_access_path(room) %>">
<i class="dropdown-icon far fa-trash-alt"></i> <%= t("remove") %>
</a>
</div>
</div>
</td>
</tbody>
</table>
</div>
</div>

View File

@ -31,7 +31,7 @@
<% end %> <% end %>
<% else %> <% else %>
<%= form_for room_path(@room), method: :post do |f| %> <%= form_for room_path(@room), method: :post do |f| %>
<div class="input-group join-input"> <div class="input-group">
<%= f.hidden_field(:search, :value => params[:search])%> <%= f.hidden_field(:search, :value => params[:search])%>
<%= f.hidden_field(:column, :value => params[:column])%> <%= f.hidden_field(:column, :value => params[:column])%>
<%= f.hidden_field(:direction, :value => params[:direction])%> <%= f.hidden_field(:direction, :value => params[:direction])%>
@ -43,7 +43,11 @@
readonly: !current_user.nil?, readonly: !current_user.nil?,
autofocus: true autofocus: true
%> %>
<%= f.submit (!@room_running && @anyone_can_start)? t("room.start") : t("room.join"), class: "btn btn-primary btn-sm col-sm-3 form-control join-form" %> <span class="input-group-append">
<button type="submit" class="btn btn-primary btn-sm px-7 form-control join-form">
<%= (!@room_running && @anyone_can_start) ? t("room.start") : t("room.join") %>
</button>
</span>
</div> </div>
<% end %> <% end %>
<% end %> <% end %>

View File

@ -25,7 +25,7 @@
<div class="col-lg-8 col-sm-12"> <div class="col-lg-8 col-sm-12">
<div id="room-title" class="display-3 form-inline <%= 'edit_hover_class' if current_user.main_room != @room %>" data-path="<%= update_settings_path(@room) %>"> <div id="room-title" class="display-3 form-inline <%= 'edit_hover_class' if current_user.main_room != @room %>" data-path="<%= update_settings_path(@room) %>">
<% if current_user.main_room == @room %> <% if current_user.main_room == @room %>
<h1 contenteditable=false id="user-text" class="display-3 text-left mb-3 font-weight-400"><%= t("home_room") %></h1> <h1 contenteditable=false id="user-text" class="display-3 text-left mb-3 font-weight-400"><%= @room.name %></h1>
<a class="disable-click"><i class="fas fa-home align-top home-indicator ml-2"></i></a> <a class="disable-click"><i class="fas fa-home align-top home-indicator ml-2"></i></a>
<% else %> <% else %>
<h1 contenteditable=false id="user-text" class="display-3 text-left mb-3 font-weight-400"><%= @room.name %></h1> <h1 contenteditable=false id="user-text" class="display-3 text-left mb-3 font-weight-400"><%= @room.name %></h1>
@ -47,7 +47,7 @@
<div class="col-lg-5 col-md-12"> <div class="col-lg-5 col-md-12">
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<a href="#" id="copy" class="btn btn-primary btn-block mt-2"> <a id="copy" class="btn btn-primary btn-block mt-2">
<i class="fas fa-copy"></i> <i class="fas fa-copy"></i>
<%= t("copy") %> <%= t("copy") %>
</a> </a>
@ -67,30 +67,34 @@
</div> </div>
<div class="offset-lg-1 col-lg-3 col-sm-12 force-bottom mt-5"> <div class="offset-lg-1 col-lg-3 col-sm-12 force-bottom mt-5">
<% if @room_running %> <% if @room_running %>
<%= button_to t("room.join"), room_path(@room), class: "btn btn-primary btn-block px-7 start-button float-right" %> <%= button_to t("room.join"), room_path(@room), class: "btn btn-primary btn-block px-7 start-button float-right", "data-disable": "" %>
<% else %> <% else %>
<% unless exceeds_limit %> <% unless exceeds_limit %>
<%= button_to t("room.start"), start_room_path(@room), class: "btn btn-primary btn-block px-7 start-button float-right" %> <%= button_to t("room.start"), start_room_path(@room), class: "btn btn-primary btn-block px-7 start-button float-right", "data-disable": "" %>
<% end %> <% end %>
<% end %> <% end %>
</div> </div>
</div> </div>
<div id="room_block_container" class="row pt-7 pb-5"> <div id="room_block_container" class="row pt-7 pb-5">
<% if current_user.rooms.length > 1 %> <% current_user.ordered_rooms.each do |room| %>
<div class="col-lg-4 col-md-6 col-sm-12"> <div class="col-lg-4 col-md-6 col-sm-12">
<%= link_to current_user.main_room do %> <%= link_to room do %>
<%= render "rooms/components/room_block", room: current_user.main_room %> <%= render "rooms/components/room_block", room: room %>
<% end %> <% end %>
</div> </div>
<% current_user.secondary_rooms.each do |room| %> <% end %>
<% if shared_access_allowed %>
<% current_user.shared_rooms.each do |room| %>
<div class="col-lg-4 col-md-6 col-sm-12"> <div class="col-lg-4 col-md-6 col-sm-12">
<%= link_to room do %> <%= link_to room do %>
<%= render "rooms/components/room_block", room: room %> <%= render "rooms/components/shared_room_block", room: room %>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
<% end %> <% end %>
<% unless room_limit_exceeded %> <% unless room_limit_exceeded %>
<%= render "rooms/components/create_room_block"%> <%= render "rooms/components/create_room_block"%>
<% end %> <% end %>
@ -98,8 +102,13 @@
</div> </div>
</div> </div>
<%= render "shared/sessions", recordings: @recordings, pagy: @pagy, only_public: false, user_recordings: false, title: t("room.recordings")%> <%= render "shared/sessions", recordings: @recordings, pagy: @pagy, only_public: false, shared_room: @shared_room, user_recordings: false, title: t("room.recordings")%>
<%= render "shared/modals/delete_room_modal" %> <%= render "shared/modals/delete_room_modal" %>
<%= render "shared/modals/create_room_modal" %> <%= render "shared/modals/create_room_modal" %>
<% if shared_access_allowed %>
<%= render "shared/modals/share_room_modal" %>
<%= render "shared/modals/remove_access_modal" %>
<% end %>

View File

@ -63,6 +63,10 @@
<%= link_to admins_path, class: "dropdown-item" do %> <%= link_to admins_path, class: "dropdown-item" do %>
<i class="dropdown-icon fas fa-user-tie mr-3"></i><%= t("header.dropdown.account_settings") %> <i class="dropdown-icon fas fa-user-tie mr-3"></i><%= t("header.dropdown.account_settings") %>
<% end %> <% end %>
<% elsif highest_role.get_permission("can_manage_rooms_recordings")%>
<%= link_to admin_rooms_path, class: "dropdown-item" do %>
<i class="dropdown-icon fas fa-user-tie mr-3"></i><%= t("header.dropdown.account_settings") %>
<% end %>
<% elsif highest_role.get_permission("can_edit_site_settings") %> <% elsif highest_role.get_permission("can_edit_site_settings") %>
<%= link_to admin_site_settings_path, class: "dropdown-item" do %> <%= link_to admin_site_settings_path, class: "dropdown-item" do %>
<i class="dropdown-icon fas fa-user-tie mr-3"></i><%= t("header.dropdown.account_settings") %> <i class="dropdown-icon fas fa-user-tie mr-3"></i><%= t("header.dropdown.account_settings") %>

View File

@ -94,7 +94,7 @@
<% failed_recordings = 0 %> <% failed_recordings = 0 %>
<% recordings.each do |recording| %> <% recordings.each do |recording| %>
<% begin %> <% begin %>
<% if only_public %> <% if only_public || (defined?(shared_room) && shared_room) %>
<%= render "shared/components/public_recording_row", recording: recording %> <%= render "shared/components/public_recording_row", recording: recording %>
<% else %> <% else %>
<%= render "shared/components/recording_row", recording: recording %> <%= render "shared/components/recording_row", recording: recording %>

View File

@ -53,10 +53,10 @@
<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> <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 %> <% end %>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <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 %> <%= button_to update_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID], state: "public"), class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon fas fa-globe"></i> <%= t("recording.visibility.public") %> <i class="dropdown-icon fas fa-globe"></i> <%= t("recording.visibility.public") %>
<% end %> <% end %>
<%= button_to update_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID], state: "unlisted"), class: "dropdown-item" do %> <%= button_to update_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID], state: "unlisted"), class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon fas fa-link"></i> <%= t("recording.visibility.unlisted") %> <i class="dropdown-icon fas fa-link"></i> <%= t("recording.visibility.unlisted") %>
<% end %> <% end %>
</div> </div>
@ -80,7 +80,7 @@
<a class="dropdown-item email-link" data-pres-link="<%= recording_links %>"><i class="dropdown-icon far fa-envelope"></i> <%= t("recording.email") %></a> <a class="dropdown-item email-link" data-pres-link="<%= recording_links %>"><i class="dropdown-icon far fa-envelope"></i> <%= t("recording.email") %></a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<% end %> <% end %>
<%= button_to delete_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID]), method: :delete, class: "dropdown-item" do %> <%= button_to delete_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID]), method: :delete, class: "dropdown-item", "data-disable": "" do %>
<i class="dropdown-icon far fa-trash-alt"></i> <%= t("delete") %> <i class="dropdown-icon far fa-trash-alt"></i> <%= t("delete") %>
<% end %> <% end %>
</div> </div>

View File

@ -26,7 +26,7 @@
<%= t("modal.delete_account.keep") %> <%= t("modal.delete_account.keep") %>
</button> </button>
<%= button_to delete_location, method: :delete, id: "delete-confirm", class: "btn btn-danger my-1 btn-del-room", disabled:"" do %> <%= button_to delete_location, method: :delete, id: "delete-confirm", class: "btn btn-danger my-1 btn-del-room", disabled:"", "data-disable": "" do %>
<%= t("modal.delete_account.delete") %> <%= t("modal.delete_account.delete") %>
<% end %> <% end %>

View File

@ -26,7 +26,7 @@
<%= t("modal.delete_room.keep") %> <%= t("modal.delete_room.keep") %>
</button> </button>
<%= button_to "/", method: :delete, id: "delete-confirm", class: "btn btn-danger my-1 btn-del-room" do %> <%= button_to "/", method: :delete, id: "delete-confirm", class: "btn btn-danger my-1 btn-del-room", "data-disable": "" do %>
<%= t("modal.delete_room.delete") %> <%= t("modal.delete_room.delete") %>
<% end %> <% end %>

View File

@ -0,0 +1,51 @@
<%
# 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="modal fade" id="mergeUserModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content text-center">
<div class="modal-body">
<div class="card-body p-6">
<div class="card-title">
<h3><%= t("modal.merge_user.title") %></h3>
</div>
<select class="selectpicker" title="<%= t("modal.share_access.select") %>" data-live-search="true" data-virtual-scroll="true" >
<% @user_list.each do |user| %>
<option value="<%= { uid: user.uid, email: user.email, name: user.name }.to_json %>" data-subtext="<%= user.email %>" ><%= user.name %></option>
<% end %>
</select>
<div class="mt-5 text-left row">
<div class="list-group-item col-6 text-center">
<label class="form-label text-primary"><%= t("modal.merge_user.from") %></label>
<div id="merge-from"></div>
</div>
<i id="merge-account-arrow" class="fas fa-2x fa-arrow-circle-right text-primary"></i>
<div class="list-group-item col-6 text-center">
<label class="form-label text-primary"><%= t("modal.merge_user.to") %></label>
<div id="merge-to"></div>
</div>
</div>
<div class="mt-6">
<button id="merge-save-access" class="btn btn-primary btn-block" onclick="mergeUsers()" ><%= t("modal.merge_user.save") %></button>
<button class="btn btn-secondary text-primary btn-block" onclick="$('#mergeUserModal').modal('hide')"><%= t("modal.merge_user.cancel") %></button>
</div>
</div>
<div class="card-footer">
<p><%= t("modal.merge_user.footer") %></p>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,39 @@
<%
# 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="modal fade" id="removeAccessModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content text-center">
<div class="modal-body">
<div class="card-body p-6">
<div class="card-title">
<h3><%= t("modal.remove_shared.title")%></h3>
</div>
<%= button_to "/", method: :delete, id: "remove-shared-confirm", class: "btn btn-danger my-1 btn-del-room" do %>
<%= hidden_field_tag :user_id, current_user.id %>
<%= t("modal.remove_shared.delete") %>
<% end %>
</div>
<div class="card-footer">
<p id="delete-footer">
<%= t("modal.remove_shared.warning").html_safe %>
</p>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,45 @@
<%
# 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="modal fade" id="shareRoomModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content text-center">
<div class="modal-body">
<div class="card-body p-6">
<div class="card-title">
<h3><%= t("modal.share_access.title") %></h3>
</div>
<select class="selectpicker" title="<%= t("modal.share_access.select") %>" data-live-search="true" data-virtual-scroll="true" >
<% @user_list.each do |user| %>
<option value="<%= user.uid %>" data-subtext="<%= user.uid %>" ><%= user.name %></option>
<% end %>
</select>
<div class="mt-5 text-left">
<label class="form-label"><%= t("modal.share_access.list") %></label>
<ul id="user-list" class="list-group">
</ul>
</div>
<div class="mt-6">
<button id="save-access" class="btn btn-primary btn-block" onclick="saveAccessChanges()" ><%= t("modal.share_access.save") %></button>
<button class="btn btn-secondary text-primary btn-block" onclick="$('#shareRoomModal').modal('hide')"><%= t("modal.share_access.cancel_changes") %></button>
</div>
</div>
<div class="card-footer">
<p><%= t("modal.share_access.footer") %></p>
</div>
</div>
</div>
</div>
</div>

View File

@ -25,7 +25,7 @@
</div> </div>
<% if Rails.configuration.terms && current_user && !current_user.accepted_terms %> <% if Rails.configuration.terms && current_user && !current_user.accepted_terms %>
<div class="btn-list text-right pt-8"> <div class="btn-list text-right pt-8">
<%= button_to t("terms.accept_existing"), terms_path, params: { accept: true }, class: "btn btn-primary btn-space" %> <%= button_to t("terms.accept_existing"), terms_path, params: { accept: true }, class: "btn btn-primary btn-space", "data-disable": "" %>
</div> </div>
<% end %> <% end %>
</form> </form>

View File

@ -153,13 +153,10 @@ module Greenlight
# Default limit on number of rooms users can create # Default limit on number of rooms users can create
config.number_of_rooms_default = 15 config.number_of_rooms_default = 15
# Allow users to share rooms by default
config.shared_access_default = "true"
# Default admin password # Default admin password
config.admin_password_default = ENV['ADMIN_PASSWORD'] || 'administrator' config.admin_password_default = ENV['ADMIN_PASSWORD'] || 'administrator'
config.action_cable.log_tags = [
->(request) { request.session['user_id'] || "no-account" },
:action_cable,
->(request) { request.uuid }
]
end end
end end

View File

@ -110,7 +110,9 @@ Rails.application.configure do
# Use Lograge for logging # Use Lograge for logging
config.lograge.enabled = true config.lograge.enabled = true
config.lograge.ignore_actions = ["HealthCheckController#all", "ThemesController#index"] config.lograge.ignore_actions = ["HealthCheckController#all", "ThemesController#index",
"ApplicationCable::Connection#connect", "WaitingChannel#subscribe",
"ApplicationCable::Connection#disconnect", "WaitingChannel#unsubscribe"]
config.lograge.custom_options = lambda do |event| config.lograge.custom_options = lambda do |event|
# capture some specific timing values you are interested in # capture some specific timing values you are interested in

View File

@ -13,4 +13,8 @@ Rails.application.config.assets.version = '1.0'
# Precompile additional assets. # Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets # application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added. # folder are already added.
Rails.application.config.assets.precompile += %w(pickr.min.js monolith.min.scss) Rails.application.config.assets.precompile += %w(_primary_theme.scss
pickr.min.js
monolith.min.scss
bootstrap-select.min.js
bootstrap-select.min.css)

View File

@ -46,12 +46,25 @@ en:
info: Clears the stored provider cache which forces a new request for the updated info info: Clears the stored provider cache which forces a new request for the updated info
title: Clear Provider Cache title: Clear Provider Cache
button: Clear Cache button: Clear Cache
clear_auth:
info: Clears the current authenticator for users allowing them to sign back in using a different authentication method
title: Clear Current Authenticator
button: Clear Auth
color: color:
info: Changing the Regular Color will change both Lighten and Darken. Lighten and Darken can then be changed individually info: Changing the Regular Color will change both Lighten and Darken. Lighten and Darken can then be changed individually
title: Primary Color title: Primary Color
regular: Regular regular: Regular
lighten: Lighten lighten: Lighten
darken: Darken darken: Darken
log_level:
title: Log Level
information: Change the Log Level for the entire deployment
debug: Debug
info: Info
warn: Warn
error: Error
fatal: Fatal
unknown: Unknown
recording_visibility: recording_visibility:
info: Set the default recording visbility for new recordings info: Set the default recording visbility for new recordings
title: Recording Default Visibility title: Recording Default Visibility
@ -66,6 +79,9 @@ en:
rooms: rooms:
info: Limits the number of rooms that a user can have (including Home Room). This setting does not apply to administrators. info: Limits the number of rooms that a user can have (including Home Room). This setting does not apply to administrators.
title: Number of Rooms per User title: Number of Rooms per User
shared_access:
info: Setting to disabled will remove the button from the Room options dropdown, preventing users from sharing rooms
title: Allow Users to Share Rooms
subtitle: Customize Greenlight subtitle: Customize Greenlight
title: Site Settings title: Site Settings
flash: flash:
@ -77,6 +93,8 @@ en:
demoted: User has been successfully demoted demoted: User has been successfully demoted
invite: Invite successfully sent to %{email} invite: Invite successfully sent to %{email}
invite_email_verification: Emails must be enabled in order to use this method. Please contact your system administrator. invite_email_verification: Emails must be enabled in order to use this method. Please contact your system administrator.
merge_fail: There was an issue merging the user accounts. Please check the users selected and try again
merge_success: User accounts merged successfully
perm_deleted: User has been permanently deleted perm_deleted: User has been permanently deleted
promoted: User has been successfully promoted promoted: User has been successfully promoted
registration_method_updated: Registration method successfully updated registration_method_updated: Registration method successfully updated
@ -88,11 +106,13 @@ en:
title: Server Recordings title: Server Recordings
no_recordings: This server has no recordings. no_recordings: This server has no recordings.
roles: roles:
appear_in_share_list: Include users with this role in the dropdown for sharing rooms
can_create_rooms: Can create rooms can_create_rooms: Can create rooms
delete: Delete the role delete: Delete the role
invalid_create: There was a problem creating a new role. Please check the role values and try again invalid_create: There was a problem creating a new role. Please check the role values and try again
invalid_order: There was a problem updating the priority of the role. Please check the values and try again invalid_order: There was a problem updating the priority of the role. Please check the values and try again
invalid_update: There was a problem updating the permissions of the role. Please check the values and try again invalid_update: There was a problem updating the permissions of the role. Please check the values and try again
manage_rooms_recordings: Allow users with this role to manage server rooms and recordings
name: Role Name name: Role Name
new_role: Create a new role new_role: Create a new role
role_has_users: This role is assigned to %{user_count} accounts. Please remove all accounts from this role before deleting it. role_has_users: This role is assigned to %{user_count} accounts. Please remove all accounts from this role before deleting it.
@ -106,6 +126,14 @@ en:
colour: colour:
title: Role Colour title: Role Colour
info: Set the colour that will be associated with the role info: Set the colour that will be associated with the role
rooms:
title: Server Rooms
table:
id: ID
not_running: Not Running
running: Running
status: Status
view: View
title: Organization Settings title: Organization Settings
users: users:
invite: Invite User invite: Invite User
@ -228,6 +256,8 @@ en:
body: 'To view the recording, follow the link below:' body: 'To view the recording, follow the link below:'
autogenerated: 'This e-mail is auto-generated by BigBlueButton.' autogenerated: 'This e-mail is auto-generated by BigBlueButton.'
footer: 'BigBlueButton is an open source web conferencing system. For more information on BigBlueButton, see https://bigbluebutton.org/.' footer: 'BigBlueButton is an open source web conferencing system. For more information on BigBlueButton, see https://bigbluebutton.org/.'
search:
start: Start searching...
landing: landing:
about: "%{href} is a simple front-end for your BigBlueButton open-source web conferencing server. You can create your own rooms to host sessions, or join others using a short and convenient link." about: "%{href} is a simple front-end for your BigBlueButton open-source web conferencing server. You can create your own rooms to host sessions, or join others using a short and convenient link."
welcome: Welcome to BigBlueButton. welcome: Welcome to BigBlueButton.
@ -291,6 +321,7 @@ en:
maintenance: maintenance:
window_alert: Maintenance window scheduled for %{date} window_alert: Maintenance window scheduled for %{date}
max_concurrent: The maximum number of concurrent sessions allowed has been reached! max_concurrent: The maximum number of concurrent sessions allowed has been reached!
merged: Merged
modal: modal:
create_role: create_role:
create: Create a new Role create: Create a new Role
@ -329,6 +360,10 @@ en:
with: Sign in with %{provider} with: Sign in with %{provider}
forgot_password: Forgot Password? forgot_password: Forgot Password?
rename_recording: rename_recording:
remove_shared:
title: Are you sure you want to remove this room from your room list?
delete: I'm sure, remove this room.
warning: You will <b>not</b> be able to access this room anymore.
room_settings: room_settings:
title: Room Settings title: Room Settings
update: Update Room update: Update Room
@ -340,6 +375,20 @@ en:
footer_text: Adjustment to your room can be done at anytime. footer_text: Adjustment to your room can be done at anytime.
rename_room: rename_room:
name_placeholder: Enter a new room name... name_placeholder: Enter a new room name...
share_access:
footer: Sharing a room with a user allows them to start the room and view the room's recordings
list: Shared With
title: Share Room Access
save: Save Changes
cancel_changes: Cancel Changes
select: Select User
merge_user:
cancel: Cancel
from: Account to be Merged
title: Merge User Accounts
to: Primary Account
save: Merge
footer: The rooms of the account to be merged will be transfered over to the Primary Account's room list and then the account will be deleted.
name_update_success: Room name successfully changed! name_update_success: Room name successfully changed!
no_user_email_exists: There is no existing user with the email specified. Please make sure you typed it correctly. no_user_email_exists: There is no existing user with the email specified. Please make sure you typed it correctly.
omniauth_error: An error occured while authenticating with omniauth. Please try again or contact an administrator! omniauth_error: An error occured while authenticating with omniauth. Please try again or contact an administrator!
@ -400,12 +449,14 @@ en:
invite: invite:
fail: Your token is either invalid or has expired. If you believe this is a mistake, please contact your administrator. fail: Your token is either invalid or has expired. If you believe this is a mistake, please contact your administrator.
no_invite: You do not have an invitation to join. Please contact your administrator to receive one. no_invite: You do not have an invitation to join. Please contact your administrator to receive one.
remove: Remove
rename: Rename rename: Rename
reset_password: reset_password:
subtitle: Reset Password subtitle: Reset Password
password: New Password password: New Password
confirm: New Password Confirmation confirm: New Password Confirmation
update: Update Password update: Update Password
auth_change: The authentication method has changed. Please check your email to set your password.
roles: roles:
active: Active active: Active
admin: Admin admin: Admin
@ -418,6 +469,10 @@ en:
create_room: Create a Room create_room: Create a Room
create_room_error: There was an error creating the room create_room_error: There was an error creating the room
create_room_success: Room created successfully create_room_success: Room created successfully
delete:
home_room: Can't delete user's Home Room
success: Room deleted successfully
fail: Failed to delete room (%{error})
enter_the_access_code: Enter the room's access code enter_the_access_code: Enter the room's access code
invalid_provider: You have entered an invalid url. Please check the url and try again. invalid_provider: You have entered an invalid url. Please check the url and try again.
invited: You have been invited to join invited: You have been invited to join
@ -426,6 +481,7 @@ en:
last_session: Last session on %{session} last_session: Last session on %{session}
login: Enter login: Enter
owner: Owner owner: Owner
owner_banned: This room is currently unavailable
no_room: no_room:
description: Enter the room url or the room id for the room you want to join. description: Enter the room url or the room id for the room you want to join.
edit_profile: Edit User Profile edit_profile: Edit User Profile
@ -441,6 +497,12 @@ en:
room_limit_exceeded: You have exceeded the number of rooms allowed. Please delete %{difference} room(s) to access this room. room_limit_exceeded: You have exceeded the number of rooms allowed. Please delete %{difference} room(s) to access this room.
sessions: Sessions sessions: Sessions
settings: Room Settings settings: Room Settings
share: Manage Access
shared_by: Shared by %{email}
remove_shared_access_success: Successfully removed shared room from your room list
remove_shared_access_error: There was an error removing the shared room from your list
shared_access_success: Room shared successfully
shared_access_error: There was an error sharing the room
start: Start start: Start
unavailable: This room is currently unavailable due to the owner's email not being verified. unavailable: This room is currently unavailable due to the owner's email not being verified.
update_settings_error: There was an error updating the room settings update_settings_error: There was an error updating the room settings

View File

@ -38,6 +38,7 @@ Rails.application.routes.draw do
scope '/admins' do scope '/admins' do
# Panel Tabs # Panel Tabs
get '/rooms', to: 'admins#server_rooms', as: :admin_rooms
get '/recordings', to: 'admins#server_recordings', as: :admin_recordings get '/recordings', to: 'admins#server_recordings', as: :admin_recordings
get '/site_settings', to: 'admins#site_settings', as: :admin_site_settings get '/site_settings', to: 'admins#site_settings', as: :admin_site_settings
get '/roles', to: 'admins#roles', as: :admin_roles get '/roles', to: 'admins#roles', as: :admin_roles
@ -49,11 +50,14 @@ Rails.application.routes.draw do
post '/approve/:user_uid', to: 'admins#approve', as: :admin_approve post '/approve/:user_uid', to: 'admins#approve', as: :admin_approve
get '/reset', to: 'admins#reset', as: :admin_reset get '/reset', to: 'admins#reset', as: :admin_reset
post '/undelete', to: 'admins#undelete', as: :admin_undelete post '/undelete', to: 'admins#undelete', as: :admin_undelete
post '/merge/:user_uid', to: 'admins#merge_user', as: :merge_user
# Site Settings # Site Settings
post '/update_settings', to: 'admins#update_settings', as: :admin_update_settings post '/update_settings', to: 'admins#update_settings', as: :admin_update_settings
post '/registration_method', to: 'admins#registration_method', as: :admin_change_registration post '/registration_method', to: 'admins#registration_method', as: :admin_change_registration
post '/coloring', to: 'admins#coloring', as: :admin_coloring post '/coloring', to: 'admins#coloring', as: :admin_coloring
post '/clear_cache', to: 'admins#clear_cache', as: :admin_clear_cache post '/clear_cache', to: 'admins#clear_cache', as: :admin_clear_cache
post '/clear_auth', to: 'admins#clear_auth', as: :admin_clear_auth
post '/log_level', to: 'admins#log_level', as: :admin_log_level
# Roles # Roles
post '/role', to: 'admins#new_role', as: :admin_new_role post '/role', to: 'admins#new_role', as: :admin_new_role
patch 'roles/order', to: 'admins#change_role_order', as: :admin_roles_order patch 'roles/order', to: 'admins#change_role_order', as: :admin_roles_order
@ -109,7 +113,11 @@ Rails.application.routes.draw do
scope '/:room_uid' do scope '/:room_uid' do
post '/', to: 'rooms#join' post '/', to: 'rooms#join'
patch '/', to: 'rooms#update', as: :update_room patch '/', to: 'rooms#update', as: :update_room
get '/room_settings', to: 'rooms#room_settings'
post '/update_settings', to: 'rooms#update_settings' post '/update_settings', to: 'rooms#update_settings'
post '/update_shared_access', to: 'rooms#shared_access', as: :room_shared_access
delete '/remove_shared_access', to: 'rooms#remove_shared_access', as: :room_remove_shared_access
get '/shared_users', to: 'rooms#shared_users', as: :room_shared_users
post '/start', to: 'rooms#start', as: :start_room post '/start', to: 'rooms#start', as: :start_room
get '/logout', to: 'rooms#logout', as: :logout_room get '/logout', to: 'rooms#logout', as: :logout_room
post '/login', to: 'rooms#login', as: :login_room post '/login', to: 'rooms#login', as: :login_room

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
class MigrationProduct < ActiveRecord::Base
self.table_name = :roles
end
class SubMigrationProduct < ActiveRecord::Base
self.table_name = :role_permissions
end
class AddManageRoomRecordingsToPermissions < ActiveRecord::Migration[5.2]
def change
reversible do |dir|
dir.up do
MigrationProduct.all.each do |role|
SubMigrationProduct.create(role_id: role.id, name: "can_manage_rooms_recordings",
value: SubMigrationProduct.find_by(role_id: role.id, name: "can_manage_users").value, enabled: true)
end
end
dir.down do
MigrationProduct.all.each do |role|
SubMigrationProduct.find_by(role_id: role.id, name: "can_manage_rooms_recordings").destroy
end
end
end
end
end

View File

@ -0,0 +1,12 @@
# frozen_string_literal: true
class CreateSharedAccesses < ActiveRecord::Migration[5.2]
def change
create_table :shared_accesses do |t|
t.belongs_to :room, foreign_key: true
t.references :user, foreign_key: true
t.timestamps
end
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
class MigrationProduct < ActiveRecord::Base
self.table_name = :roles
end
class ChangeRolePriorityToUnique < ActiveRecord::Migration[5.2]
def change
reversible do |dir|
dir.up do
MigrationProduct.where("priority < 0").where.not(name: "pending").each do |role|
role.decrement!(:priority)
end
add_index MigrationProduct, [:priority, :provider], unique: true
end
dir.down do
remove_index MigrationProduct, [:priority, :provider]
MigrationProduct.where("priority < 0").where.not(name: "pending").each do |role|
role.increment!(:priority)
end
end
end
end
end

View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_08_28_153347) do ActiveRecord::Schema.define(version: 2020_01_30_144841) do
create_table "features", force: :cascade do |t| create_table "features", force: :cascade do |t|
t.integer "setting_id" t.integer "setting_id"
@ -58,6 +58,7 @@ ActiveRecord::Schema.define(version: 2019_08_28_153347) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.index ["name", "provider"], name: "index_roles_on_name_and_provider", unique: true t.index ["name", "provider"], name: "index_roles_on_name_and_provider", unique: true
t.index ["name"], name: "index_roles_on_name" t.index ["name"], name: "index_roles_on_name"
t.index ["priority", "provider"], name: "index_roles_on_priority_and_provider", unique: true
end end
create_table "rooms", force: :cascade do |t| create_table "rooms", force: :cascade do |t|
@ -90,6 +91,15 @@ ActiveRecord::Schema.define(version: 2019_08_28_153347) do
t.index ["provider"], name: "index_settings_on_provider" t.index ["provider"], name: "index_settings_on_provider"
end end
create_table "shared_accesses", force: :cascade do |t|
t.integer "room_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["room_id"], name: "index_shared_accesses_on_room_id"
t.index ["user_id"], name: "index_shared_accesses_on_user_id"
end
create_table "users", force: :cascade do |t| create_table "users", force: :cascade do |t|
t.integer "room_id" t.integer "room_id"
t.string "provider" t.string "provider"

View File

@ -18,19 +18,18 @@ services:
volumes: volumes:
- ./log:/usr/src/app/log - ./log:/usr/src/app/log
# When using sqlite3 as the database # When using sqlite3 as the database
- ./db/production:/usr/src/app/db/production # - ./db/production:/usr/src/app/db/production
# When using postgresql as the database # When using postgresql as the database
# links: links:
# - db - db
# db: db:
# image: postgres:9.5 image: postgres:9.5
# restart: on-failure restart: on-failure
# ports: ports:
# - 127.0.0.1:5432:5432 - 127.0.0.1:5432:5432
# volumes: volumes:
# - ./db/production:/var/lib/postgresql/data - ./db/production:/var/lib/postgresql/data
# environment: environment:
# - PGHOST=postgres - POSTGRES_DB=postgres
# - PGDATABASE=postgres - POSTGRES_USER=postgres
# - PGUSER=postgres - POSTGRES_PASSWORD=password
# - PGPASSWORD=password

View File

@ -91,7 +91,8 @@ a {
opacity: 0.9; opacity: 0.9;
} }
} }
&:active { &:active, &.active {
color: $primary-color !important;
background-color: $primary-color-lighten !important; background-color: $primary-color-lighten !important;
} }
} }

View File

@ -215,11 +215,11 @@ REPORT_ISSUE_URL=https://github.com/bigbluebutton/greenlight/issues/new
# #
# For deployments based on the docker-compose script also included, the HOST should be set with the Docker container id. # For deployments based on the docker-compose script also included, the HOST should be set with the Docker container id.
# #
# DB_ADAPTER=postgresql DB_ADAPTER=postgresql
# DB_HOST=db DB_HOST=db
# DB_NAME=greenlight_production DB_NAME=greenlight_production
# DB_USERNAME=postgres DB_USERNAME=postgres
# DB_PASSWORD=password DB_PASSWORD=password
# Specify the default registration to be used by Greenlight until an administrator sets the # Specify the default registration to be used by Greenlight until an administrator sets the
# registration method # registration method

View File

@ -27,7 +27,7 @@ describe AccountActivationsController, type: :controller do
user = create(:user, provider: "greenlight") user = create(:user, provider: "greenlight")
@request.session[:user_id] = user.id @request.session[:user_id] = user.id
get :show, params: { email: user.email } get :show, params: { uid: user.uid }
expect(response).to redirect_to(user.main_room) expect(response).to redirect_to(user.main_room)
end end
@ -35,7 +35,8 @@ describe AccountActivationsController, type: :controller do
it "renders the verify view if the user is not signed in and is not verified" do it "renders the verify view if the user is not signed in and is not verified" do
user = create(:user, email_verified: false, provider: "greenlight") user = create(:user, email_verified: false, provider: "greenlight")
get :show, params: { email: user.email } user.create_activation_token
get :show, params: { token: user.activation_token }
expect(response).to render_template(:show) expect(response).to render_template(:show)
end end
@ -45,7 +46,8 @@ describe AccountActivationsController, type: :controller do
it "activates a user if they have the correct activation token" do it "activates a user if they have the correct activation token" do
@user = create(:user, email_verified: false, provider: "greenlight") @user = create(:user, email_verified: false, provider: "greenlight")
get :edit, params: { email: @user.email, token: @user.activation_token } @user.create_activation_token
get :edit, params: { token: @user.activation_token }
@user.reload @user.reload
expect(@user.email_verified).to eq(true) expect(@user.email_verified).to eq(true)
@ -53,22 +55,17 @@ describe AccountActivationsController, type: :controller do
expect(response).to redirect_to(signin_path) expect(response).to redirect_to(signin_path)
end end
it "does not activate a user if they have the correct activation token" do it "should not find user when given fake activation token" do
@user = create(:user, email_verified: false, provider: "greenlight") @user = create(:user, email_verified: false, provider: "greenlight")
get :edit, params: { email: @user.email, token: "fake_token" } expect { get :edit, params: { token: "fake_token" } }.to raise_error(ActiveRecord::RecordNotFound)
@user.reload
expect(@user.email_verified).to eq(false)
expect(flash[:alert]).to be_present
expect(response).to redirect_to(root_path)
end end
it "does not allow the user to click the verify link again" do it "does not allow the user to click the verify link again" do
@user = create(:user, provider: "greenlight") @user = create(:user, provider: "greenlight")
get :edit, params: { email: @user.email, token: @user.activation_token } @user.create_activation_token
get :edit, params: { token: @user.activation_token }
expect(flash[:alert]).to be_present expect(flash[:alert]).to be_present
expect(response).to redirect_to(root_path) expect(response).to redirect_to(root_path)
end end
@ -78,7 +75,8 @@ describe AccountActivationsController, type: :controller do
@user.add_role :pending @user.add_role :pending
get :edit, params: { email: @user.email, token: @user.activation_token } @user.create_activation_token
get :edit, params: { token: @user.activation_token }
expect(flash[:success]).to be_present expect(flash[:success]).to be_present
expect(response).to redirect_to(root_path) expect(response).to redirect_to(root_path)
@ -89,7 +87,8 @@ describe AccountActivationsController, type: :controller do
it "resends the email to the current user if the resend button is clicked" do it "resends the email to the current user if the resend button is clicked" do
user = create(:user, email_verified: false, provider: "greenlight") user = create(:user, email_verified: false, provider: "greenlight")
expect { get :resend, params: { email: user.email } }.to change { ActionMailer::Base.deliveries.count }.by(1) user.create_activation_token
expect { get :resend, params: { token: user.activation_token } }.to change { ActionMailer::Base.deliveries.count }.by(1)
expect(flash[:success]).to be_present expect(flash[:success]).to be_present
expect(response).to redirect_to(root_path) expect(response).to redirect_to(root_path)
end end
@ -97,7 +96,8 @@ describe AccountActivationsController, type: :controller do
it "redirects a verified user to the root path" do it "redirects a verified user to the root path" do
user = create(:user, provider: "greenlight") user = create(:user, provider: "greenlight")
get :resend, params: { email: user.email } user.create_activation_token
get :resend, params: { token: user.activation_token }
expect(flash[:alert]).to be_present expect(flash[:alert]).to be_present
expect(response).to redirect_to(root_path) expect(response).to redirect_to(root_path)

View File

@ -67,6 +67,8 @@ describe AdminsController, type: :controller do
post :ban_user, params: { user_uid: @user.uid } post :ban_user, params: { user_uid: @user.uid }
@user.reload
expect(@user.has_role?(:denied)).to eq(true) expect(@user.has_role?(:denied)).to eq(true)
expect(flash[:success]).to be_present expect(flash[:success]).to be_present
expect(response).to redirect_to(admins_path) expect(response).to redirect_to(admins_path)
@ -82,6 +84,8 @@ describe AdminsController, type: :controller do
post :unban_user, params: { user_uid: @user.uid } post :unban_user, params: { user_uid: @user.uid }
@user.reload
expect(@user.has_role?(:denied)).to eq(false) expect(@user.has_role?(:denied)).to eq(false)
expect(flash[:success]).to be_present expect(flash[:success]).to be_present
expect(response).to redirect_to(admins_path) expect(response).to redirect_to(admins_path)
@ -153,6 +157,8 @@ describe AdminsController, type: :controller do
post :approve, params: { user_uid: @user.uid } post :approve, params: { user_uid: @user.uid }
@user.reload
expect(@user.has_role?(:pending)).to eq(false) expect(@user.has_role?(:pending)).to eq(false)
expect(flash[:success]).to be_present expect(flash[:success]).to be_present
expect(response).to redirect_to(admins_path) expect(response).to redirect_to(admins_path)
@ -197,6 +203,42 @@ describe AdminsController, type: :controller do
expect(response).to redirect_to(admins_path) expect(response).to redirect_to(admins_path)
end end
end end
context "POST #merge_user" do
it "merges the users room to the primary account and deletes the old user" do
@request.session[:user_id] = @admin.id
@user2 = create(:user)
room1 = create(:room, owner: @user2)
room2 = create(:room, owner: @user2)
room3 = @user2.main_room
post :merge_user, params: { user_uid: @user.uid, merge: @user2.uid }
room1.reload
room2.reload
room3.reload
expect(User.exists?(uid: @user2.uid)).to be false
expect(room1.name).to start_with("(Merged)")
expect(room2.name).to start_with("(Merged)")
expect(room3.name).to start_with("(Merged)")
expect(room1.owner).to eq(@user)
expect(room2.owner).to eq(@user)
expect(room3.owner).to eq(@user)
expect(flash[:success]).to be_present
expect(response).to redirect_to(admins_path)
end
it "does not merge if trying to merge the same user into themself" do
@request.session[:user_id] = @admin.id
post :merge_user, params: { user_uid: @user.uid, merge: @user.uid }
expect(flash[:alert]).to be_present
expect(response).to redirect_to(admins_path)
end
end
end end
describe "User Design" do describe "User Design" do
@ -344,6 +386,66 @@ describe AdminsController, type: :controller do
expect(response).to redirect_to(admin_site_settings_path) expect(response).to redirect_to(admin_site_settings_path)
end end
end end
context "POST #shared_access" do
it "changes the shared access 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 :update_settings, params: { setting: "Shared Access", value: "false" }
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Shared Access")
expect(feature[:value]).to eq("false")
expect(response).to redirect_to(admin_site_settings_path)
end
end
context "POST #clear_auth" do
it "clears all users social uids if clear auth button is clicked" do
allow_any_instance_of(ApplicationController).to receive(:set_user_domain).and_return("provider1")
controller.instance_variable_set(:@user_domain, "provider1")
@request.session[:user_id] = @admin.id
@admin.add_role :super_admin
@admin.update_attribute(:provider, "greenlight")
@user2 = create(:user, provider: "provider1")
@user3 = create(:user, provider: "provider1")
@user.update_attribute(:social_uid, Faker::Internet.password)
@user2.update_attribute(:social_uid, Faker::Internet.password)
@user3.update_attribute(:social_uid, Faker::Internet.password)
expect(@user.social_uid).not_to be(nil)
expect(@user2.social_uid).not_to be(nil)
expect(@user3.social_uid).not_to be(nil)
post :clear_auth
@user.reload
@user2.reload
@user3.reload
expect(@user.social_uid).to be(nil)
expect(@user2.social_uid).to be(nil)
expect(@user3.social_uid).to be(nil)
end
end
context "POST #log_level" do
it "changes the log level" do
@request.session[:user_id] = @admin.id
@admin.add_role :super_admin
expect(Rails.logger.level).to eq(0)
post :log_level, params: { value: 2 }
expect(Rails.logger.level).to eq(2)
end
end
end end
describe "Roles" do describe "Roles" do
@ -413,6 +515,7 @@ describe AdminsController, type: :controller do
context "PATCH #change_role_order" do context "PATCH #change_role_order" do
before do before do
Role.create_default_roles("provider1") Role.create_default_roles("provider1")
@user.roles.delete(Role.find_by(name: "user", provider: "greenlight"))
end end
it "should fail if user attempts to change the order of the admin or user roles" do it "should fail if user attempts to change the order of the admin or user roles" do
@ -428,35 +531,9 @@ describe AdminsController, type: :controller do
end end
it "should fail if a user attempts to edit a role with a higher priority than their own" do it "should fail if a user attempts to edit a role with a higher priority than their own" do
Role.create(name: "test1", priority: 1, provider: "greenlight") new_role3 = Role.create_new_role("test3", "provider1")
new_role2 = Role.create(name: "test2", priority: 2, provider: "greenlight") new_role2 = Role.create_new_role("test2", "provider1")
new_role2.update_permission("can_edit_roles", "true") new_role2.update_permission("can_edit_roles", "true")
new_role3 = Role.create(name: "test3", priority: 3, provider: "greenlight")
user_role = Role.find_by(name: "user", provider: "greenlight")
user_role.priority = 4
user_role.save!
@user.roles << new_role2
@user.save!
@request.session[:user_id] = @user.id
patch :change_role_order, params: { role: [new_role3.id, new_role2.id] }
expect(flash[:alert]).to eq(I18n.t("administrator.roles.invalid_order"))
expect(response).to redirect_to admin_roles_path
end
it "should fail if a user attempts to edit a role with a higher priority than their own" do
Role.create(name: "test1", priority: 1, provider: "greenlight")
new_role2 = Role.create(name: "test2", priority: 2, provider: "greenlight")
new_role2.update_permission("can_edit_roles", "true")
new_role3 = Role.create(name: "test3", priority: 3, provider: "greenlight")
user_role = Role.find_by(name: "user", provider: "greenlight")
user_role.priority = 4
user_role.save!
@user.roles << new_role2 @user.roles << new_role2
@user.save! @user.save!
@ -470,10 +547,11 @@ describe AdminsController, type: :controller do
end end
it "should update the role order" do it "should update the role order" do
user_role = Role.find_by(name: "user", provider: "provider1")
user_role.update_attribute(:priority, 4)
new_role1 = Role.create(name: "test1", priority: 1, provider: "provider1") new_role1 = Role.create(name: "test1", priority: 1, provider: "provider1")
new_role2 = Role.create(name: "test2", priority: 2, provider: "provider1") new_role2 = Role.create(name: "test2", priority: 2, provider: "provider1")
new_role3 = Role.create(name: "test3", priority: 3, provider: "provider1") new_role3 = Role.create(name: "test3", priority: 3, provider: "provider1")
user_role = Role.find_by(name: "user", provider: "provider1")
@request.session[:user_id] = @admin.id @request.session[:user_id] = @admin.id
@ -494,16 +572,15 @@ describe AdminsController, type: :controller do
context 'POST #update_role' do context 'POST #update_role' do
before do before do
Role.create_default_roles("provider1") Role.create_default_roles("provider1")
@user.roles.delete(Role.find_by(name: "user", provider: "greenlight"))
end end
it "should fail to update a role with a lower priority than the user" do it "should fail to update a role with a lower priority than the user" do
user_role = Role.find_by(name: "user", provider: "provider1")
user_role.update_attribute(:priority, 3)
new_role1 = Role.create(name: "test1", priority: 1, provider: "provider1") new_role1 = Role.create(name: "test1", priority: 1, provider: "provider1")
new_role2 = Role.create(name: "test2", priority: 2, provider: "provider1") new_role2 = Role.create(name: "test2", priority: 2, provider: "provider1")
new_role2.update_permission("can_edit_roles", "true") new_role2.update_permission("can_edit_roles", "true")
user_role = Role.find_by(name: "user", provider: "greenlight")
user_role.priority = 3
user_role.save!
@user.roles << new_role2 @user.roles << new_role2
@user.save! @user.save!
@ -517,7 +594,7 @@ describe AdminsController, type: :controller do
end end
it "should fail to update if there is a duplicate name" do it "should fail to update if there is a duplicate name" do
new_role = Role.create(name: "test2", priority: 1, provider: "provider1") new_role = Role.create(name: "test2", priority: 2, provider: "provider1")
new_role.update_permission("can_edit_roles", "true") new_role.update_permission("can_edit_roles", "true")
@request.session[:user_id] = @admin.id @request.session[:user_id] = @admin.id
@ -529,7 +606,7 @@ describe AdminsController, type: :controller do
end end
it "should update role permisions" do it "should update role permisions" do
new_role = Role.create(name: "test2", priority: 1, provider: "provider1") new_role = Role.create(name: "test2", priority: 2, provider: "provider1")
new_role.update_permission("can_edit_roles", "true") new_role.update_permission("can_edit_roles", "true")
@request.session[:user_id] = @admin.id @request.session[:user_id] = @admin.id
@ -574,7 +651,7 @@ describe AdminsController, type: :controller do
end end
it "should successfully delete the role" do it "should successfully delete the role" do
new_role = Role.create(name: "test2", priority: 1, provider: "provider1") new_role = Role.create(name: "test2", priority: 2, provider: "provider1")
new_role.update_permission("can_edit_roles", "true") new_role.update_permission("can_edit_roles", "true")
@request.session[:user_id] = @admin.id @request.session[:user_id] = @admin.id

View File

@ -115,19 +115,15 @@ describe PasswordResetsController, type: :controller do
end end
it "updates attributes if the password update is a success" do it "updates attributes if the password update is a success" do
user = create(:user) user = create(:user, provider: "greenlight")
token = "reset_token" user.create_reset_digest
old_digest = user.password_digest
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
user.reset_digest = BCrypt::Password.create(token, cost: cost)
allow(controller).to receive(:valid_user).and_return(nil) allow(controller).to receive(:valid_user).and_return(nil)
allow(controller).to receive(:check_expiration).and_return(nil) allow(controller).to receive(:check_expiration).and_return(nil)
controller.instance_variable_set(:@user, user)
params = { params = {
id: token, id: user.reset_token,
email: user.email,
user: { user: {
password: :password, password: :password,
password_confirmation: :password, password_confirmation: :password,
@ -135,6 +131,10 @@ describe PasswordResetsController, type: :controller do
} }
patch :update, params: params patch :update, params: params
user.reload
expect(old_digest.eql?(user.password_digest)).to be false
expect(response).to redirect_to(root_path) expect(response).to redirect_to(root_path)
end end
end end

View File

@ -137,6 +137,24 @@ describe RoomsController, type: :controller do
expect(flash[:alert]).to be_present expect(flash[:alert]).to be_present
expect(response).to redirect_to(root_path) expect(response).to redirect_to(root_path)
end end
it "redirects to root if owner is pending" do
@request.session[:user_id] = @owner.id
@owner.add_role :pending
get :show, params: { room_uid: @owner.main_room, search: :none }
expect(response).to redirect_to(root_path)
end
it "redirects to root if owner is banned" do
@request.session[:user_id] = @owner.id
@owner.add_role :denied
get :show, params: { room_uid: @owner.main_room, search: :none }
expect(response).to redirect_to(root_path)
end
end end
describe "POST #create" do describe "POST #create" do
@ -155,13 +173,27 @@ describe RoomsController, type: :controller do
post :create, params: { room: room_params } post :create, params: { room: room_params }
r = @owner.secondary_rooms.last r = @owner.rooms.last
expect(r.name).to eql(name) expect(r.name).to eql(name)
expect(r.owner).to eql(@owner) expect(r.owner).to eql(@owner)
expect(r.room_settings).to eql(json_room_settings) expect(r.room_settings).to eql(json_room_settings)
expect(response).to redirect_to(r) expect(response).to redirect_to(r)
end end
it "should respond with JSON object of the room_settings" do
@request.session[:user_id] = @owner.id
@owner.main_room.update_attribute(:room_settings, { "muteOnStart": true, "requireModeratorApproval": true,
"anyoneCanStart": true, "joinModerator": true }.to_json)
json_room_settings = "{\"muteOnStart\":true,\"requireModeratorApproval\":true," \
"\"anyoneCanStart\":true,\"joinModerator\":true}"
get :room_settings, params: { room_uid: @owner.main_room }, format: :json
expect(JSON.parse(response.body)).to eql(json_room_settings)
end
it "should redirect to root if not logged in" do it "should redirect to root if not logged in" do
expect do expect do
name = Faker::Games::Pokemon.name name = Faker::Games::Pokemon.name
@ -214,7 +246,6 @@ describe RoomsController, type: :controller do
it "should use join name if user is not logged in and meeting running" do it "should use join name if user is not logged in and meeting running" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(true) allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(true)
post :join, params: { room_uid: @room, join_name: "Join Name" } post :join, params: { room_uid: @room, join_name: "Join Name" }
expect(response).to redirect_to(join_path(@owner.main_room, "Join Name", {})) expect(response).to redirect_to(join_path(@owner.main_room, "Join Name", {}))
@ -310,6 +341,24 @@ describe RoomsController, type: :controller do
expect(flash[:alert]).to be_present expect(flash[:alert]).to be_present
expect(response).to redirect_to(root_path) expect(response).to redirect_to(root_path)
end end
it "redirects to root if owner is pending" do
@request.session[:user_id] = @owner.id
@owner.add_role :pending
post :join, params: { room_uid: @room }
expect(response).to redirect_to(root_path)
end
it "redirects to root if owner is banned" do
@request.session[:user_id] = @owner.id
@owner.add_role :denied
post :join, params: { room_uid: @room }
expect(response).to redirect_to(root_path)
end
end end
describe "DELETE #destroy" do describe "DELETE #destroy" do
@ -342,6 +391,45 @@ describe RoomsController, type: :controller do
delete :destroy, params: { room_uid: @user.main_room } delete :destroy, params: { room_uid: @user.main_room }
end.to change { Room.count }.by(0) end.to change { Room.count }.by(0)
end end
it "allows admin to delete room" do
@admin = create(:user)
@admin.add_role :admin
@request.session[:user_id] = @admin.id
expect do
delete :destroy, params: { room_uid: @secondary_room }
end.to change { Room.count }.by(-1)
expect(response).to redirect_to(@admin.main_room)
end
it "does not allow admin to delete a users home room" do
@admin = create(:user)
@admin.add_role :admin
@request.session[:user_id] = @admin.id
expect do
delete :destroy, params: { room_uid: @user.main_room }
end.to change { Room.count }.by(0)
expect(flash[:alert]).to be_present
expect(response).to redirect_to(@admin.main_room)
end
it "does not allow an admin from a different context to delete room" do
allow_any_instance_of(User).to receive(:admin_of?).and_return(false)
@admin = create(:user)
@admin.add_role :admin
@request.session[:user_id] = @admin.id
expect do
delete :destroy, params: { room_uid: @secondary_room }
end.to change { Room.count }.by(0)
expect(response).to redirect_to(root_path)
end
end end
describe "POST #start" do describe "POST #start" do
@ -374,6 +462,27 @@ describe RoomsController, type: :controller do
expect(response).to redirect_to(root_path) expect(response).to redirect_to(root_path)
end end
it "redirects to join path if admin" do
@admin = create(:user)
@admin.add_role :admin
@request.session[:user_id] = @admin.id
post :start, params: { room_uid: @user.main_room }
expect(response).to redirect_to(join_path(@user.main_room, @admin.name, { user_is_moderator: true }, @admin.uid))
end
it "redirects to root path if not admin of current user" do
allow_any_instance_of(User).to receive(:admin_of?).and_return(false)
@admin = create(:user)
@admin.add_role :admin
@request.session[:user_id] = @admin.id
post :start, params: { room_uid: @user.main_room }
expect(response).to redirect_to(root_path)
end
end end
describe "POST #update_settings" do describe "POST #update_settings" do
@ -413,6 +522,35 @@ describe RoomsController, type: :controller do
expect(response).to redirect_to(@secondary_room) expect(response).to redirect_to(@secondary_room)
end end
it "allows admin to update room settings" do
@admin = create(:user)
@admin.add_role :admin
@request.session[:user_id] = @admin.id
room_params = { "mute_on_join": "1", "name": @secondary_room.name }
formatted_room_params = "{\"muteOnStart\":true,\"requireModeratorApproval\":false," \
"\"anyoneCanStart\":false,\"joinModerator\":false}" # JSON string format
expect { post :update_settings, params: { room_uid: @secondary_room.uid, room: room_params } }
.to change { @secondary_room.reload.room_settings }
.from(@secondary_room.room_settings).to(formatted_room_params)
expect(response).to redirect_to(@secondary_room)
end
it "does not allow admins from a different context to update room settings" do
allow_any_instance_of(User).to receive(:admin_of?).and_return(false)
@admin = create(:user)
@admin.add_role :admin
@request.session[:user_id] = @admin.id
room_params = { "mute_on_join": "1", "name": @secondary_room.name }
expect { post :update_settings, params: { room_uid: @secondary_room.uid, room: room_params } }
.not_to change { @secondary_room.reload.room_settings }
expect(response).to redirect_to(root_path)
end
end end
describe "GET #logout" do describe "GET #logout" do
@ -480,4 +618,122 @@ describe RoomsController, type: :controller do
expect(response).to redirect_to room_path(@user1.main_room) expect(response).to redirect_to room_path(@user1.main_room)
end end
end end
describe "POST #shared_access" do
before do
@user = create(:user)
@room = create(:room, owner: @user)
@user1 = create(:user)
allow(Rails.configuration).to receive(:shared_access_default).and_return("true")
end
it "shares a room with another user" do
@request.session[:user_id] = @user.id
post :shared_access, params: { room_uid: @room.uid, add: [@user1.uid] }
expect(SharedAccess.exists?(room_id: @room.id, user_id: @user1.id)).to be true
expect(flash[:success]).to be_present
expect(response).to redirect_to room_path(@room)
end
it "allows a user to view a shared room and start it" do
@request.session[:user_id] = @user.id
post :shared_access, params: { room_uid: @room.uid, add: [@user1.uid] }
allow(controller).to receive(:current_user).and_return(@user1)
get :show, params: { room_uid: @room.uid }
expect(response).to render_template(:show)
end
it "unshares a room from the user if they are removed from the list" do
SharedAccess.create(room_id: @room.id, user_id: @user1.id)
expect(SharedAccess.exists?(room_id: @room.id, user_id: @user1.id)).to be true
@request.session[:user_id] = @user.id
post :shared_access, params: { room_uid: @room.uid, add: [] }
expect(SharedAccess.exists?(room_id: @room.id, user_id: @user1.id)).to be false
expect(flash[:success]).to be_present
expect(response).to redirect_to room_path(@room)
end
it "doesn't allow a user to share a room they don't own" do
@request.session[:user_id] = @user1.id
post :shared_access, params: { room_uid: @room.uid, add: [@user1.uid] }
expect(SharedAccess.exists?(room_id: @room.id, user_id: @user1.id)).to be false
expect(response).to redirect_to root_path
end
it "disables shared room functionality if the site setting is disabled" do
allow_any_instance_of(Setting).to receive(:get_value).and_return("false")
@request.session[:user_id] = @user.id
post :shared_access, params: { room_uid: @room.uid, add: [@user1.uid] }
expect(SharedAccess.exists?(room_id: @room.id, user_id: @user1.id)).to be true
allow(controller).to receive(:current_user).and_return(@user1)
get :show, params: { room_uid: @room.uid }
expect(response).to render_template(:join)
end
it "allows admins to update room access" do
@admin = create(:user)
@admin.add_role :admin
@request.session[:user_id] = @admin.id
post :shared_access, params: { room_uid: @room.uid, add: [@user1.uid] }
expect(SharedAccess.exists?(room_id: @room.id, user_id: @user1.id)).to be true
expect(flash[:success]).to be_present
expect(response).to redirect_to room_path(@room)
end
it "redirects to root path if not admin of current user" do
allow_any_instance_of(User).to receive(:admin_of?).and_return(false)
@admin = create(:user)
@admin.add_role :admin
@request.session[:user_id] = @admin.id
post :shared_access, params: { room_uid: @room.uid, add: [] }
expect(response).to redirect_to(root_path)
end
end
describe "POST #remove_shared_access" do
before do
@user = create(:user)
@room = create(:room, owner: @user)
@user1 = create(:user)
allow(Rails.configuration).to receive(:shared_access_default).and_return("true")
end
it "unshares a room from the user if they click the remove button" do
SharedAccess.create(room_id: @room.id, user_id: @user1.id)
expect(SharedAccess.exists?(room_id: @room.id, user_id: @user1.id)).to be true
@request.session[:user_id] = @user1.id
post :remove_shared_access, params: { room_uid: @room.uid, user_id: @user1.id }
expect(SharedAccess.exists?(room_id: @room.id, user_id: @user1.id)).to be false
expect(flash[:success]).to be_present
expect(response).to redirect_to @user1.main_room
end
it "doesn't allow some random user to change share access" do
@user2 = create(:user)
SharedAccess.create(room_id: @room.id, user_id: @user1.id)
expect(SharedAccess.exists?(room_id: @room.id, user_id: @user1.id)).to be true
@request.session[:user_id] = @user2.id
post :remove_shared_access, params: { room_uid: @room.uid, user_id: @user1.id }
expect(SharedAccess.exists?(room_id: @room.id, user_id: @user1.id)).to be true
expect(response).to redirect_to root_path
end
end
end end

View File

@ -88,7 +88,11 @@ describe SessionsController, type: :controller do
end end
describe "POST #create" do describe "POST #create" do
before { allow(Rails.configuration).to receive(:enable_email_verification).and_return(true) } before do
allow(Rails.configuration).to receive(:enable_email_verification).and_return(true)
allow_any_instance_of(SessionsController).to receive(:auth_changed_to_local?).and_return(false)
end
before(:each) do before(:each) do
@user1 = create(:user, provider: 'greenlight', password: 'example', password_confirmation: 'example') @user1 = create(:user, provider: 'greenlight', password: 'example', password_confirmation: 'example')
@user2 = create(:user, password: 'example', password_confirmation: "example") @user2 = create(:user, password: 'example', password_confirmation: "example")
@ -139,7 +143,8 @@ describe SessionsController, type: :controller do
} }
expect(@request.session[:user_id]).to be_nil expect(@request.session[:user_id]).to be_nil
expect(response).to redirect_to(account_activation_path(email: @user3.email)) # Expect to redirect to activation path since token is not known here
expect(response.location.start_with?(account_activation_url(token: ""))).to be true
end end
it "should not login user if account is deleted" do it "should not login user if account is deleted" do
@ -251,6 +256,22 @@ describe SessionsController, type: :controller do
expect(@user1.rooms.find { |r| r.name == "Old Home Room" }).to_not be_nil expect(@user1.rooms.find { |r| r.name == "Old Home Room" }).to_not be_nil
expect(@user1.rooms.find { |r| r.name == "Test" }).to_not be_nil expect(@user1.rooms.find { |r| r.name == "Test" }).to_not be_nil
end end
it "sends the user a reset password email if the authentication method is changing to local" do
allow_any_instance_of(SessionsController).to receive(:auth_changed_to_local?).and_return(true)
email = Faker::Internet.email
create(:user, email: email, provider: "greenlight", social_uid: "google-user")
expect {
post :create, params: {
session: {
email: email,
password: 'example',
},
}
}.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end end
describe "GET/POST #omniauth" do describe "GET/POST #omniauth" do
@ -428,6 +449,66 @@ describe SessionsController, type: :controller do
expect(response).to redirect_to(root_path) expect(response).to redirect_to(root_path)
end end
it "switches a social account to a different social account if the authentication method changed" do
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:bn_launcher]
get :omniauth, params: { provider: 'bn_launcher' }
u = User.find_by(social_uid: "bn-launcher-user")
u.social_uid = nil
users_old_uid = u.uid
u.save!
new_user = OmniAuth::AuthHash.new(
provider: "bn_launcher",
uid: "bn-launcher-user-new",
info: {
email: "user@google.com",
name: "Office User",
nickname: "googleuser",
image: "touch.png",
customer: 'customer1',
}
)
allow_any_instance_of(SessionsController).to receive(:auth_changed_to_social?).and_return(true)
allow_any_instance_of(ApplicationController).to receive(:set_user_domain).and_return("customer1")
controller.instance_variable_set(:@user_domain, "customer1")
request.env["omniauth.auth"] = new_user
get :omniauth, params: { provider: 'bn_launcher' }
new_u = User.find_by(social_uid: "bn-launcher-user-new")
expect(users_old_uid).to eq(new_u.uid)
end
it "switches a local account to a different social account if the authentication method changed" do
email = Faker::Internet.email
user = create(:user, email: email, provider: "customer1")
users_old_uid = user.uid
new_user = OmniAuth::AuthHash.new(
provider: "bn_launcher",
uid: "bn-launcher-user-new",
info: {
email: email,
name: "Office User",
nickname: "googleuser",
image: "touch.png",
customer: 'customer1',
}
)
allow_any_instance_of(SessionsController).to receive(:auth_changed_to_social?).and_return(true)
allow_any_instance_of(ApplicationController).to receive(:set_user_domain).and_return("customer1")
controller.instance_variable_set(:@user_domain, "customer1")
request.env["omniauth.auth"] = new_user
get :omniauth, params: { provider: 'bn_launcher' }
new_u = User.find_by(social_uid: "bn-launcher-user-new")
expect(users_old_uid).to eq(new_u.uid)
end
end end
describe "POST #ldap" do describe "POST #ldap" do
@ -452,6 +533,51 @@ describe SessionsController, type: :controller do
expect(@request.session[:user_id]).to eql(u.id) expect(@request.session[:user_id]).to eql(u.id)
end end
it "should defaults the users image to blank if actual image is provided" do
entry = Net::LDAP::Entry.new("cn=Test User,ou=people,dc=planetexpress,dc=com")
entry[:cn] = "Test User"
entry[:givenName] = "Test"
entry[:sn] = "User"
entry[:mail] = "test@example.com"
entry[:jpegPhoto] = "\FF\F8" # Pretend image
allow_any_instance_of(Net::LDAP).to receive(:bind_as).and_return([entry])
post :ldap, params: {
session: {
user: "test",
password: 'password',
},
}
u = User.last
expect(u.provider).to eql("ldap")
expect(u.image).to eql("")
expect(@request.session[:user_id]).to eql(u.id)
end
it "uses the users image if a url is provided" do
image = Faker::Internet.url
entry = Net::LDAP::Entry.new("cn=Test User,ou=people,dc=planetexpress,dc=com")
entry[:cn] = "Test User"
entry[:givenName] = "Test"
entry[:sn] = "User"
entry[:mail] = "test@example.com"
entry[:jpegPhoto] = image
allow_any_instance_of(Net::LDAP).to receive(:bind_as).and_return([entry])
post :ldap, params: {
session: {
user: "test",
password: 'password',
},
}
u = User.last
expect(u.provider).to eql("ldap")
expect(u.image).to eql(image)
expect(@request.session[:user_id]).to eql(u.id)
end
it "should redirect to signin on invalid credentials" do it "should redirect to signin on invalid credentials" do
allow_any_instance_of(Net::LDAP).to receive(:bind_as).and_return(false) allow_any_instance_of(Net::LDAP).to receive(:bind_as).and_return(false)

View File

@ -312,7 +312,7 @@ describe UsersController, type: :controller do
user_role.save! user_role.save!
tmp_role = Role.create(name: "test", priority: -2, provider: "greenlight") tmp_role = Role.create(name: "test", priority: -4, provider: "greenlight")
params = random_valid_user_params params = random_valid_user_params
patch :update, params: params.merge!(user_uid: user, user: { role_ids: tmp_role.id.to_s }) patch :update, params: params.merge!(user_uid: user, user: { role_ids: tmp_role.id.to_s })
@ -354,14 +354,16 @@ describe UsersController, type: :controller do
@request.session[:user_id] = admin.id @request.session[:user_id] = admin.id
tmp_role1 = Role.create(name: "test1", priority: 1, provider: "greenlight") tmp_role1 = Role.create(name: "test1", priority: 2, provider: "greenlight")
tmp_role1.update_permission("send_promoted_email", "true") tmp_role1.update_permission("send_promoted_email", "true")
tmp_role2 = Role.create(name: "test2", priority: 2, provider: "greenlight") tmp_role2 = Role.create(name: "test2", priority: 3, provider: "greenlight")
params = random_valid_user_params params = random_valid_user_params
params = params.merge!(user_uid: user, user: { role_ids: "#{tmp_role1.id} #{tmp_role2.id}" }) params = params.merge!(user_uid: user, user: { role_ids: "#{tmp_role1.id} #{tmp_role2.id}" })
expect { patch :update, params: params }.to change { ActionMailer::Base.deliveries.count }.by(1) expect { patch :update, params: params }.to change { ActionMailer::Base.deliveries.count }.by(1)
user.reload
expect(user.roles.count).to eq(2) expect(user.roles.count).to eq(2)
expect(user.highest_priority_role.name).to eq("test1") expect(user.highest_priority_role.name).to eq("test1")
expect(response).to redirect_to(admins_path) expect(response).to redirect_to(admins_path)
@ -375,7 +377,7 @@ describe UsersController, type: :controller do
admin.add_role :admin admin.add_role :admin
tmp_role1 = Role.create(name: "test1", priority: 1, provider: "greenlight") tmp_role1 = Role.create(name: "test1", priority: 2, provider: "greenlight")
tmp_role1.update_permission("send_demoted_email", "true") tmp_role1.update_permission("send_demoted_email", "true")
user.roles << tmp_role1 user.roles << tmp_role1
user.save! user.save!

View File

@ -112,6 +112,28 @@ describe User, type: :model do
end end
end end
context '#ordered_rooms' do
it 'correctly orders the users rooms' do
user = create(:user)
room1 = create(:room, owner: user)
room2 = create(:room, owner: user)
room3 = create(:room, owner: user)
room4 = create(:room, owner: user)
room4.update_attributes(sessions: 1, last_session: "2020-02-24 19:52:57")
room3.update_attributes(sessions: 1, last_session: "2020-01-25 19:52:57")
room2.update_attributes(sessions: 1, last_session: "2019-09-05 19:52:57")
room1.update_attributes(sessions: 1, last_session: "2015-02-24 19:52:57")
rooms = user.ordered_rooms
expect(rooms[0]).to eq(user.main_room)
expect(rooms[1]).to eq(room4)
expect(rooms[2]).to eq(room3)
expect(rooms[3]).to eq(room2)
expect(rooms[4]).to eq(room1)
end
end
context 'password reset' do context 'password reset' do
it 'creates token and respective reset digest' do it 'creates token and respective reset digest' do
user = create(:user) user = create(:user)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long