Admin panel (#496)

* Added the administrator role and functionality that comes with it  (#403)

* GRN-20: Added roles to the user model

* GRN-75: Added a view for admins to see their users

* GRN-77: Added Edit/Delete/Promote ability for admins

* GRN-71: Added admin account by default

* Changed the way locales are shown

* Updated the rest of the locales

* Changed the way available_locales are defined

* Updated locales in Russian

* Updated locaales for German

* Update user.rb

* Update admins.js

* GRN-15: Added the ability to change color and image from admin interface (#425)

* GRN-20: Added roles to the user model

* GRN-75: Added a view for admins to see their users

* GRN-77: Added Edit/Delete/Promote ability for admins

* GRN-71: Added admin account by default

* Changed the way locales are shown

* Updated the rest of the locales

* Changed the way available_locales are defined

* Updated locales in Russian

* Updated locaales for German

* GRN-15: Added the ability for admins to customize color and image

* Update user.rb

* Update user.rb

* Update routes.rb

* Update admins_controller.rb

* GRN-87:Added a super admin role and made changes to how to the design works (#430)

* GRN-20: Added roles to the user model

* GRN-75: Added a view for admins to see their users

* GRN-77: Added Edit/Delete/Promote ability for admins

* GRN-71: Added admin account by default

* Changed the way locales are shown

* Updated the rest of the locales

* Changed the way available_locales are defined

* Updated locales in Russian

* Updated locaales for German

* GRN-15: Added the ability for admins to customize color and image

* Added the super admin and completed the design tab

* Update user.rb

* Update themes_controller_spec.rb

* Update routes.rb

* Update admins_controller.rb

* Removed duplicated code that broke the build after last merge

* GRN-78: Restructured some of the views to make the UI more consistent and responsive (#435)

* GRN-20: Added roles to the user model

* GRN-75: Added a view for admins to see their users

* GRN-77: Added Edit/Delete/Promote ability for admins

* GRN-71: Added admin account by default

* GRN-15: Added the ability for admins to customize color and image

* Added the super admin and completed the design tab

* GRN-78: Cleaned up buttons and moved signin to its own page

* GRN-78: Moved the Rooms and Recordings link to nav bar

* Merge fix

* Views restructure fix (#458)

* Added cache to gitlab-ci.yml

* Restructured seed

* GRN2-99 -> GRN2-106: UI cleanup and refactoring (#478)

* GRN2-98: Change Fullname to Full name

* GRN2-105: Changed View Users to Manage Users

* GRN2-101/103: Updated email to match branding

* GRN2-100: Updated Email Sent flash to be more descriptive

* GRN2-104: Redirect user to sign in page w/ flash after clicking activation link

* GRN2-102: Changed the wording in the verification email

* GRN2-99: Added email form validation

* GRN2-106: Cleaned up Users list front end

* Fixes to rake and admin password validator for passing rubocop

* GRN2-113: Fixed issues with admin panel (#479)

* GRN2-116: Code clean up after restructure of views (#482)

* Removed unused references

* Rubocop

* Added pagination to admin view (#483)

* GRN2-114: Added the ability for admins to ban/unban users (#487)

* Added the ability for admins to ban and unban users

* Update sessions_helper.rb

*  Merge branch 'master' into admin-panel  (#492)

* Updated rubocop gem

* Updated rubocop and fixed issues (#490)

* Rubocop fixes

* GRN2-122: Updated sign in flow for admins and switch design tab to site settings (#489)

* Switched design tab to site settings

* Update _header with spaces instead of tabs

* Added more test cases to increase coverage (#494)
This commit is contained in:
Jesus Federico 2019-05-03 13:05:12 -04:00 committed by GitHub
parent b9efff586a
commit 9f74b0e2c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 2286 additions and 203 deletions

View File

@ -57,6 +57,7 @@ gem 'bigbluebutton-api-ruby'
# Front-end.
gem 'bootstrap', '~> 4.3.1'
gem 'tabler-rubygem'
gem 'pagy'
# For detecting the users preferred language.
gem 'http_accept_language'
@ -70,6 +71,11 @@ gem 'redcarpet'
# For health check endpoint
gem "health_check"
# For providing user roles
gem "rolify"
# For limiting access based on user roles
gem 'cancancan', '~> 2.0'
group :production do
# Use a postgres database in production.
gem 'pg', '~> 0.18'

View File

@ -56,6 +56,7 @@ GEM
sassc-rails (>= 2.0.0)
builder (3.2.3)
byebug (10.0.2)
cancancan (2.3.0)
coffee-rails (4.2.2)
coffee-script (>= 2.2.0)
railties (>= 4.0.0)
@ -174,6 +175,7 @@ GEM
omniauth-twitter (1.4.0)
omniauth-oauth (~> 1.1)
rack
pagy (2.1.5)
parallel (1.17.0)
parser (2.6.3.0)
ast (~> 2.4.0)
@ -226,6 +228,7 @@ GEM
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
rolify (5.2.0)
rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
@ -336,6 +339,7 @@ DEPENDENCIES
bigbluebutton-api-ruby
bootstrap (~> 4.3.1)
byebug
cancancan (~> 2.0)
coffee-rails (~> 4.2)
coveralls
dotenv-rails
@ -353,6 +357,7 @@ DEPENDENCIES
omniauth-ldap
omniauth-microsoft-office365 (~> 0.0.7)
omniauth-twitter
pagy
pg (~> 0.18)
puma (~> 3.0)
rails (~> 5.0.7)
@ -361,6 +366,7 @@ DEPENDENCIES
redcarpet
redis (~> 3.0)
remote_syslog_logger
rolify
rspec-rails (~> 3.7)
rubocop
sass-rails (~> 5.0)

View File

@ -0,0 +1,97 @@
// 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/>.
$(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
// Only run on the admins page.
if (controller == "admins" && action == "index") {
// show the modal with the correct form action url
$(".delete-user").click(function(data){
var uid = $(data.target).closest("tr").data("user-uid")
$("#delete-confirm").parent().attr("action", "/u/" + uid)
})
// Change the color of the color inputs when the color is changed
$(".colorinput-input").change(function(data) {
// Get the color from the input
var color = $(data.target).val()
// Update the color in the database and reload the page
$.post($("#coloring-path").val(), {color: color}).done(function(data) {
location.reload()
});
});
// Submit search if the user hits enter
$("#search-input").keypress(function(key) {
var keyPressed = key.which
if (keyPressed == 13) {
searchPage()
}
})
// Add listeners for sort
$("th[data-order]").click(function(data){
var header_elem = $(data.target)
if(header_elem.data('order') === 'asc'){ // asc
header_elem.data('order', 'desc');
}
else if(header_elem.data('order') === 'desc'){ // desc
header_elem.data('order', 'none');
}
else{ // none
header_elem.data('order', 'asc');
}
var search = $("#search-input").val()
window.location.replace(window.location.pathname + "?page=1&search=" + search + "&column=" + header_elem.data("header") + "&direction="+ header_elem.data('order'))
})
}
// Only run on the admins edit user page.
if (controller == "admins" && action == "edit_user") {
$("#users").click(function(data){
var url = $("body").data("relative-root")
if (!url.endsWith("/")) {
url += "/"
}
url += "admins"
window.location.href = url
})
}
});
// Change the branding image to the image provided
function changeBrandingImage(path) {
var url = $("#branding-url").val()
$.post(path, {url: url})
}
// Searches the user table for the given string
function searchPage() {
var search = $("#search-input").val()
window.location.replace(window.location.pathname + "?page=1&search=" + search)
}
// Clears the search bar
function clearSearch() {
window.location.replace(window.location.pathname + "?page=1")
}

View File

@ -57,7 +57,7 @@ $(document).on('turbolinks:load', function(){
}
// Display and update all fields related to creating a room in the createRoomModal
$("#create-room").click(function(){
$("#create-room-block").click(function(){
$("#create-room-name").val("")
$("#createRoomModal form").attr("action", $("body").data('relative-root'))
updateDropdown($(".dropdown-item[value='default']"))

View File

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

View File

@ -15,8 +15,10 @@
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$(document).on('turbolinks:load', function(){
// Check if there is a table on this page
if ($("table").length) {
var controller = $("body").data('controller');
var action = $("body").data('action');
if(controller == "rooms" && action == "show" || controller == "rooms" && action == "update" || controller == "users" && action == "recordings"){
// Choose active header
// (Name, Length or Users)

View File

@ -31,7 +31,7 @@
@import "tabler/nav";
@import "tabler/button";
//@import "tabler/alert";
@import "tabler/alert";
//@import "tabler/close";
//@import "tabler/badge";
@import "tabler/tables";
@ -68,7 +68,7 @@
//@import "tabler/forms/custom-selectgroup";
@import "tabler/forms/custom-switch";
//@import "tabler/forms/custom-imagecheck";
//@import "tabler/forms/custom-colorinput";
@import "tabler/forms/custom-colorinput";
//@import "tabler/timeline";

View File

@ -0,0 +1,32 @@
// 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/>.
#users-table {
.user-role:hover {
cursor: default;
}
}
#clear-search {
z-index: 9;
position: absolute;
right: 55px;
top: 8px;
&:hover {
cursor: pointer;
}
}

View File

@ -35,12 +35,17 @@
@import "tabler-custom";
@import "utilities/variables";
@import "admins";
@import "main";
@import "rooms";
@import "sessions";
@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,300i,400,400i,500,500i,600,600i,700,700i&amp;subset=latin-ext);
* {
outline: none !important;
}
html, body {
position: relative;
width: 100%;
@ -77,10 +82,6 @@ a {
width: 100%;
}
.table-responsive {
overflow: visible;
}
.background {
background-color: $background-color;
}
@ -135,3 +136,34 @@ a {
background-color: green !important;
}
}
input:focus {
border-color: $primary !important;
}
.list-group-item-action.active {
color: $primary;
}
.header .header-nav {
color: $text-muted !important;
&:hover {
padding-bottom: 21px;
border-bottom: 1px solid $primary;
}
&.active {
color: $primary !important;
padding-bottom: 21px;
border-bottom: 1px solid $primary;
}
}
table {
thead {
th[data-order]:hover {
cursor: pointer;
}
}
}

View File

@ -144,3 +144,7 @@
padding:10px 10px 10px 10px;
}
}
.signin-button {
font-size: 16px;
}

View File

@ -56,3 +56,12 @@
vertical-align: middle;
padding-top: 12px;
}
#create-room-block {
border: 1px dashed lightgray;
&:hover {
cursor: pointer;
background-color: rgba(0, 0, 0, 0.04);
}
}

View File

@ -0,0 +1,125 @@
.btn-primary,
.btn-primary:visited,
.btn-primary i {
background-color: $primary-color !important;
border-color: $primary-color !important;
color: white !important;
}
.btn-primary:active,
.btn-primary:active:focus,
.btn-primary:active:hover,
.btn-primary:focus,
.btn-primary:hover,
.btn-primary:hover i {
background-color: $primary-color-darken !important;
border-color: $primary-color-darken !important;
color: white !important;
}
a {
color: $primary-color !important;
}
.oauth-signin {
color: white !important;
&:hover * {
color: white !important;
}
}
.btn-outline-primary {
border-color: $primary-color !important;
color: $primary-color !important;
&:hover {
background: $primary-color !important;
color: white !important;
}
&:focus {
box-shadow: 0 0 0 2px $primary-color-lighten;
}
}
.header {
& .avatar {
background-color: $primary-color !important;
color: white !important;
}
& a:hover:not(.btn) {
color: $primary-color !important;
}
& .header-nav {
border-color: $primary-color !important;
}
& .header-nav.active {
color: $primary-color !important;
}
}
.dropdown-item {
color: #6e7687 !important;
&:hover {
color: $primary-color !important;
}
&:active {
background-color: $primary-color-lighten !important;
}
}
input:focus, select:focus {
box-shadow: 0 0 5px $primary-color !important;
border-color: $primary-color !important;
}
.list-group-item.active {
background-color: $primary-color-lighten !important;
&, .list-group-item.active * {
color: $primary-color !important;
}
}
.text-primary {
color: $primary-color !important;
}
.bg-primary {
background-color: $primary-color !important;
}
.btn-danger {
color: white !important;
}
#clear-search {
&:hover {
color: $primary-color-darken !important;
}
}
.pagination {
.page-item {
&.active a {
color:white !important;
background-color: $primary-color !important;
}
& a {
border-color: $primary-color !important;
}
& a:hover {
background-color: $primary-color-lighten !important;
}
& a:focus {
box-shadow: 0 0 3px $primary-color !important;
}
}
}

View File

@ -33,11 +33,11 @@ class AccountActivationsController < ApplicationController
@user.activate
flash[:success] = I18n.t("verify.activated") + " " + I18n.t("verify.signin")
redirect_to signin_path
else
flash[:alert] = I18n.t("verify.invalid")
redirect_to root_path
end
redirect_to root_url
end
# GET /account_activations/resend
@ -51,7 +51,7 @@ class AccountActivationsController < ApplicationController
logger.error "Error in email delivery: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
else
flash[:success] = I18n.t("email_sent")
flash[:success] = I18n.t("email_sent", email_type: t("verify.verification"))
end
end

View File

@ -0,0 +1,101 @@
# 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/>.
class AdminsController < ApplicationController
include Pagy::Backend
authorize_resource class: false
before_action :find_user, only: [:edit_user, :promote, :demote, :ban_user, :unban_user]
before_action :verify_admin_of_user, only: [:edit_user, :promote, :demote, :ban_user, :unban_user]
before_action :find_setting, only: [:branding, :coloring]
# GET /admins
def index
@search = params[:search] || ""
@order_column = params[:column] && params[:direction] != "none" ? params[:column] : "created_at"
@order_direction = params[:direction] && params[:direction] != "none" ? params[:direction] : "DESC"
puts @order_direction.to_s
if Rails.configuration.loadbalanced_configuration
@pagy, @users = pagy(User.without_role(:super_admin)
.where(provider: user_settings_provider)
.where.not(id: current_user.id)
.admins_search(@search)
.admins_order(@order_column, @order_direction))
else
@pagy, @users = pagy(User.where.not(id: current_user.id)
.admins_search(@search)
.admins_order(@order_column, @order_direction))
end
end
# GET /admins/edit/:user_uid
def edit_user
render "admins/index", locals: { setting_id: "account" }
end
# POST /admins/promote/:user_uid
def promote
@user.add_role :admin
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.promoted") }
end
# POST /admins/demote/:user_uid
def demote
@user.remove_role :admin
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.demoted") }
end
# POST /admins/branding
def branding
@settings.update_value("Branding Image", params[:url])
redirect_to admins_path
end
# POST /admins/color
def coloring
@settings.update_value("Primary Color", params[:color])
redirect_to admins_path(setting: "site_settings")
end
# POST /admins/ban/:user_uid
def ban_user
@user.add_role :denied
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.banned") }
end
# POST /admins/unban/:user_uid
def unban_user
@user.remove_role :denied
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.unbanned") }
end
private
def find_user
@user = User.find_by!(uid: params[:user_uid])
end
def find_setting
@settings = Setting.find_or_create_by!(provider: user_settings_provider)
end
def verify_admin_of_user
redirect_to admins_path,
flash: { alert: I18n.t("administrator.flash.unauthorized") } unless current_user.admin_of?(@user)
end
end

View File

@ -20,10 +20,13 @@ require 'bigbluebutton_api'
class ApplicationController < ActionController::Base
include SessionsHelper
include ThemingHelper
before_action :migration_error?
before_action :set_locale
before_action :check_admin_password
before_action :set_user_domain
before_action :check_if_unbanned
# Force SSL for loadbalancer configurations.
before_action :redirect_to_https
@ -102,6 +105,21 @@ class ApplicationController < ActionController::Base
}
end
# Manually deal with 401 errors
rescue_from CanCan::AccessDenied do |_exception|
render "errors/not_found"
end
# Checks to make sure that the admin has changed his password from the default
def check_admin_password
if current_user&.has_role?(:admin) && current_user&.greenlight_account? &&
current_user&.authenticate(Rails.configuration.admin_password_default)
flash.now[:alert] = I18n.t("default_admin",
edit_link: edit_user_path(user_uid: current_user.uid) + "?setting=password").html_safe
end
end
def redirect_to_https
if Rails.configuration.loadbalanced_configuration && request.headers["X-Forwarded-Proto"] == "http"
redirect_to protocol: "https://"
@ -116,4 +134,13 @@ class ApplicationController < ActionController::Base
end
end
helper_method :set_user_domain
# Checks if the user is banned and logs him out if he is
def check_if_unbanned
if current_user&.has_role?(:denied)
session.delete(:user_id)
redirect_to unauthorized_path
end
end
helper_method :check_if_unbanned
end

View File

@ -28,4 +28,8 @@ class ErrorsController < ApplicationController
def internal_error
render status: 500, formats: :html
end
def unauthorized
render status: 401, formats: :html
end
end

View File

@ -17,16 +17,7 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
class MainController < ApplicationController
# before_action :redirect_to_room
# GET /
def index
end
private
def redirect_to_room
# If the user is logged in already, move them along to their room.
redirect_to room_path(current_user.room) if current_user
end
end

View File

@ -30,7 +30,7 @@ class PasswordResetsController < ApplicationController
if @user
@user.create_reset_digest
@user.send_password_reset_email(reset_link)
flash[:success] = I18n.t("email_sent")
flash[:success] = I18n.t("email_sent", email_type: t("reset_password.subtitle"))
redirect_to root_path
else
flash[:alert] = I18n.t("no_user_email_exists")

View File

@ -27,12 +27,18 @@ class SessionsController < ApplicationController
# POST /users/login
def create
user = User.find_by(email: session_params[:email], provider: @user_domain)
redirect_to(root_path, alert: I18n.t("invalid_user")) && return unless user
redirect_to(root_path, alert: I18n.t("invalid_login_method")) && return unless user.greenlight_account?
admin = User.find_by(email: session_params[:email])
if admin&.has_role? :super_admin
user = admin
else
user = User.find_by(email: session_params[:email], provider: @user_domain)
redirect_to(root_path, alert: I18n.t("invalid_user")) && return unless user
redirect_to(root_path, alert: I18n.t("invalid_login_method")) && return unless user.greenlight_account?
redirect_to(account_activation_path(email: user.email)) && return unless user.activated?
end
redirect_to(root_path, alert: I18n.t("invalid_credentials")) && return unless user.try(:authenticate,
session_params[:password])
redirect_to(account_activation_path(email: user.email)) && return unless user.activated?
login(user)
end

View File

@ -0,0 +1,44 @@
# 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/>.
class ThemesController < ApplicationController
before_action :provider_settings
# GET /primary
def index
color = @settings.get_value("Primary Color") || Rails.configuration.primary_color_default
file_name = Rails.root.join('app', 'assets', 'stylesheets', 'utilities', '_primary_themes.scss')
@file_contents = File.read(file_name)
# Include the variables and covert scss file to css
@compiled = Sass::Engine.new("$primary-color:#{color};" \
"$primary-color-lighten:lighten(#{color}, 40%);" \
"$primary-color-darken:darken(#{color}, 10%);" +
@file_contents, syntax: :scss).render
respond_to do |format|
format.css { render body: @compiled }
end
end
private
def provider_settings
@settings = Setting.find_or_create_by(provider: user_settings_provider)
end
end

View File

@ -44,11 +44,15 @@ class UsersController < ApplicationController
logger.error "Error in email delivery: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
else
flash[:success] = I18n.t("email_sent")
flash[:success] = I18n.t("email_sent", email_type: t("verify.verification"))
end
redirect_to(root_path)
end
# GET /signin
def signin
end
# GET /signup
def new
if Rails.configuration.allow_user_signup
@ -61,7 +65,7 @@ class UsersController < ApplicationController
# GET /u/:user_uid/edit
def edit
if current_user
redirect_to current_user.room unless @user == current_user
redirect_to current_user.main_room if @user != current_user && !current_user.admin_of?(@user)
else
redirect_to root_path
end
@ -113,6 +117,16 @@ class UsersController < ApplicationController
if current_user && current_user == @user
@user.destroy
session.delete(:user_id)
elsif current_user.admin_of?(@user)
begin
@user.destroy
rescue => e
logger.error "Error in user deletion: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("administrator.flash.delete_fail"))
else
flash[:success] = I18n.t("administrator.flash.delete")
end
redirect_to(admins_path) && return
end
redirect_to root_path
end

View File

@ -0,0 +1,21 @@
# 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 AdminsHelper
include Pagy::Frontend
end

View File

@ -95,4 +95,11 @@ module ApplicationHelper
@translations ||= I18n.backend.send(:translations)
@translations[I18n.locale].with_indifferent_access[:javascript] || {}
end
# Returns the page that the logo redirects to when clicked on
def home_page
return root_path unless current_user
return admins_path if current_user.has_role? :super_admin
current_user.main_room
end
end

View File

@ -31,9 +31,13 @@ module SessionsHelper
# If email verification is disabled, or the user has verified, go to their room
def check_email_verified(user)
if user.activated?
# Get the url to redirect the user to
url = if cookies[:return_to] && ![root_url, signup_url].include?(cookies[:return_to])
# Admin users should be redirected to the admin page
if user.has_role? :super_admin
redirect_to admins_path
elsif user.activated?
# Dont redirect to any of these urls
dont_redirect_to = [root_url, signup_url, unauthorized_url, internal_error_url, not_found_url]
url = if cookies[:return_to] && !dont_redirect_to.include?(cookies[:return_to])
cookies[:return_to]
else
user.main_room

View File

@ -0,0 +1,42 @@
# 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 ThemingHelper
# Returns the logo based on user's provider
def logo_image
Setting.find_or_create_by(provider: user_settings_provider)
.get_value("Branding Image") || Rails.configuration.branding_image_default
end
# Returns the primary color based on user's provider
def user_color
Setting.find_or_create_by(provider: user_settings_provider)
.get_value("Primary Color") || Rails.configuration.primary_color_default
end
# Returns the user's provider in the settings context
def user_settings_provider
if Rails.configuration.loadbalanced_configuration && !current_user&.has_role?(:super_admin)
current_user.provider
elsif Rails.configuration.loadbalanced_configuration
@user_domain
else
"greenlight"
end
end
end

View File

@ -17,6 +17,7 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
class ApplicationMailer < ActionMailer::Base
add_template_helper(ThemingHelper)
default from: 'from@example.com'
layout 'mailer'
end

33
app/models/ability.rb Normal file
View File

@ -0,0 +1,33 @@
# 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/>.
class Ability
include CanCan::Ability
def initialize(user)
if !user
cannot :manage, AdminsController
elsif user.has_role? :super_admin
can :manage, :all
elsif user.has_role? :admin
can :manage, :all
elsif user.has_role? :user
cannot :manage, AdminsController
end
end
end

21
app/models/feature.rb Normal file
View File

@ -0,0 +1,21 @@
# 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/>.
class Feature < ApplicationRecord
belongs_to :setting
end

31
app/models/role.rb Normal file
View File

@ -0,0 +1,31 @@
# 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/>.
class Role < ApplicationRecord
has_and_belongs_to_many :users, join_table: :users_roles
belongs_to :resource,
polymorphic: true,
optional: true
validates :resource_type,
inclusion: { in: Rolify.resource_types },
allow_nil: true
scopify
end

43
app/models/setting.rb Normal file
View File

@ -0,0 +1,43 @@
# 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/>.
class Setting < ApplicationRecord
has_many :features
# Updates the value of the feature and enables it
def update_value(name, value)
feature = features.find_or_create_by!(name: name)
feature.update_attributes(value: value, enabled: true)
end
# Returns the value if enabled or the default if not enabled
def get_value(name)
feature = features.find_or_create_by!(name: name)
if feature[:enabled]
feature[:value]
else
case name
when "Branding Image"
Rails.configuration.branding_image_default
when "Primary Color"
Rails.configuration.primary_color_default
end
end
end
end

View File

@ -19,11 +19,14 @@
require 'bbb_api'
class User < ApplicationRecord
rolify
include ::APIConcern
include ::BbbApi
attr_accessor :reset_token
after_create :assign_default_role
after_create :initialize_main_room
before_save { email.try(:downcase!) }
before_destroy :destroy_rooms
@ -33,6 +36,7 @@ class User < ApplicationRecord
validates :name, length: { maximum: 256 }, presence: true
validates :provider, presence: true
validate :check_if_email_can_be_blank
validates :email, length: { maximum: 256 }, allow_blank: true,
uniqueness: { case_sensitive: false, scope: :provider },
format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i }
@ -98,6 +102,17 @@ class User < ApplicationRecord
end
end
def self.admins_search(string)
search_query = "name LIKE :search OR email LIKE :search OR username LIKE :search" \
" OR created_at LIKE :search OR provider LIKE :search"
search_param = "%#{string}%"
where(search_query, search: search_param)
end
def self.admins_order(column, direction)
order("#{column} #{direction}")
end
def all_recordings
pag_num = Rails.configuration.pagination_number
@ -199,6 +214,18 @@ class User < ApplicationRecord
create_reset_activation_digest(User.new_token)
end
def admin_of?(user)
if Rails.configuration.loadbalanced_configuration
if has_role? :super_admin
id != user.id
else
(has_role? :admin) && (id != user.id) && (provider == user.provider) && (!user.has_role? :super_admin)
end
else
((has_role? :admin) || (has_role? :super_admin)) && (id != user.id)
end
end
def self.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
@ -229,4 +256,19 @@ class User < ApplicationRecord
self.main_room = Room.create!(owner: self, name: I18n.t("home_room"))
save
end
# Initialize the user to use the default user role
def assign_default_role
add_role(:user) if roles.blank?
end
def check_if_email_can_be_blank
if email.blank?
if Rails.configuration.loadbalanced_configuration && greenlight_account?
errors.add(:email, I18n.t("errors.messages.blank"))
elsif provider == "greenlight"
errors.add(:email, I18n.t("errors.messages.blank"))
end
end
end
end

View File

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

View File

@ -0,0 +1,20 @@
<%
# 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 text-center pt-9">
<div class="display-1 text-muted mb-5">401</div>
<h1 class="h2 mb-3"><%= t("errors.unauthorized.message") %></h1>
<p class="h4 text-muted font-weight-normal mb-7"><%= t("errors.unauthorized.help") %></p>
</div>

View File

@ -32,7 +32,10 @@
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<!-- Primary color styling -->
<%= stylesheet_link_tag themes_primary_path %>
<script type="text/javascript">
// Rail-ish function to translate a string, according to the Rails locales
window.t = function t(key) {
@ -42,7 +45,7 @@
var keys = key.split('.');
var value = <%= current_translations.to_json.html_safe %>; // Add I18n variables
keys.forEach(key => {
if (value) {
value = value[key];
@ -77,11 +80,9 @@
</div>
<% end %>
<%= yield %>
<%= render 'shared/flash_messages' unless flash.empty? %>
<% if current_user %>
<%= render "shared/modals/create_room_modal" %>
<% end %>
<%= yield %>
</div>
<%= render "shared/footer" %>

View File

@ -13,8 +13,6 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<%= render 'shared/flash_messages' unless flash.empty? %>
<div class="background">
<div class="container pt-9 pb-8">
<div class="row">

View File

@ -13,8 +13,6 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<%= render 'shared/flash_messages' unless flash.empty? %>
<div class="container">
<div class="row pt-7">
<div class="col col-4 offset-4">

View File

@ -13,8 +13,6 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<%= render 'shared/flash_messages' unless flash.empty? %>
<div class="container">
<div class="row pt-7">
<div class="col col-4 offset-4">

View File

@ -13,11 +13,9 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<%= render 'shared/flash_messages' unless flash.empty? %>
<div class="background pb-1">
<div class="container">
<div class="row pt-9">
<div class="row pt-7">
<div class="col-lg-9 col-sm-12">
<div id="room-title" class="display-3 form-inline <%= 'edit_hover_class' if current_user.main_room != @room %>">
<h1 contenteditable=false id="user-text" class="display-3 text-left mb-3 font-weight-400"><%= @room.name %></h1>
@ -51,7 +49,7 @@
</div>
</div>
<div id="room_block_container" class="row pt-7 pb-2">
<div id="room_block_container" class="row pt-7 pb-5">
<% if current_user.rooms.length > 1 %>
<div class="col-lg-4 col-md-6 col-sm-12">
<%= link_to current_user.main_room do %>
@ -67,8 +65,11 @@
<%= render "shared/modals/delete_room_modal", room: room %>
<% end %>
<% end %>
<%= render "shared/components/create_room_block"%>
</div>
</div>
</div>
<%= render "shared/sessions", recordings: @recordings, only_public: false, user_recordings: false %>
<%= render "shared/sessions", recordings: @recordings, only_public: false, user_recordings: false, title: t("room.recordings")%>
<%= render "shared/modals/create_room_modal" %>

View File

@ -15,16 +15,19 @@
<% flash.each do |key,value| %>
<% if key.eql? "success" %>
<div class="alert alert-icon alert-success text-center mb-0">
<%= content_tag :div, value, class: "flash #{key} d-inline" %>
<div class="alert alert-success alert-dismissible text-center mb-0">
<button type="button" class="close" data-dismiss="alert">&times</button>
<%= value %>
</div>
<% elsif key.eql? "alert" %>
<div class="alert alert-icon alert-danger text-center mb-0">
<%= content_tag :div, value, class: "flash #{key} d-inline" %>
<div class="alert alert-danger alert-dismissible text-center mb-0">
<button type="button" class="close" data-dismiss="alert">&times</button>
<%= value %>
</div>
<% elsif key.eql? "info" %>
<div class="alert alert-icon alert-info text-center mb-0">
<%= content_tag :div, value, class: "flash #{key} d-inline" %>
<div class="alert alert-info alert-dismissible text-center mb-0">
<button type="button" class="close" data-dismiss="alert">&times</button>
<%= value %>
</div>