diff --git a/app/assets/javascripts/admins.js b/app/assets/javascripts/admins.js index 13a64baf..53fcfb57 100644 --- a/app/assets/javascripts/admins.js +++ b/app/assets/javascripts/admins.js @@ -41,6 +41,49 @@ $(document).on('turbolinks:load', function(){ 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("" + userInfo.name + "" + "" + userInfo.email + "" + "" + userInfo.uid + "") + + }) + + $("#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("" + userInfo.name + "" + "" + userInfo.email + "" + "" + userInfo.uid + "") + } + }) } else if(action == "site_settings"){ loadColourSelectors() @@ -79,6 +122,11 @@ function changeBrandingImage(path) { $.post(path, {value: url}) } +function mergeUsers() { + let userToMerge = $("#from-uid").text() + $.post($("#merge-save-access").data("path"), {merge: userToMerge}) +} + // Filters by role function filterRole(role) { var search = new URL(location.href).searchParams.get('search') diff --git a/app/assets/javascripts/room.js b/app/assets/javascripts/room.js index 4b762e04..79e8652a 100644 --- a/app/assets/javascripts/room.js +++ b/app/assets/javascripts/room.js @@ -69,7 +69,7 @@ $(document).on('turbolinks:load', function(){ }) $('.selectpicker').selectpicker({ - liveSearchPlaceholder: "Start searching..." + liveSearchPlaceholder: getLocalizedString('javascript.search.start') }); // Fixes turbolinks issue with bootstrap select $(window).trigger('load.bs.select.data-api'); diff --git a/app/assets/stylesheets/admins.scss b/app/assets/stylesheets/admins.scss index 2265448d..3e872d74 100644 --- a/app/assets/stylesheets/admins.scss +++ b/app/assets/stylesheets/admins.scss @@ -88,4 +88,12 @@ &:hover { cursor: pointer; } +} + +#merge-account-arrow { + position: absolute; + top: 47%; + right: 47%; + z-index: 999; + background: white; } \ No newline at end of file diff --git a/app/controllers/admins_controller.rb b/app/controllers/admins_controller.rb index 3dc7ba32..37de5067 100644 --- a/app/controllers/admins_controller.rb +++ b/app/controllers/admins_controller.rb @@ -24,7 +24,7 @@ class AdminsController < ApplicationController 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] authorize_resource class: false before_action :find_user, only: manage_users @@ -41,6 +41,8 @@ class AdminsController < ApplicationController @role = params[:role] ? Role.find_by(name: params[:role], provider: @user_domain) : nil @tab = params[:tab] || "active" + @user_list = merge_user_list + @pagy, @users = pagy(manage_users_list) end @@ -140,6 +142,45 @@ class AdminsController < ApplicationController redirect_to redirect_path, flash: { success: I18n.t("administrator.flash.reset_password") } 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_to admins_path + end + # SITE SETTINGS # POST /admins/update_settings diff --git a/app/controllers/concerns/populator.rb b/app/controllers/concerns/populator.rb index 20974a5a..970fe0ee 100644 --- a/app/controllers/concerns/populator.rb +++ b/app/controllers/concerns/populator.rb @@ -77,7 +77,18 @@ module Populator roles_can_appear << role.name if role.get_permission("can_appear_in_share_list") && role.name != "super_admin" end - initial_list = User.where.not(uid: current_user.uid).with_highest_priority_role(roles_can_appear) + 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) diff --git a/app/models/ability.rb b/app/models/ability.rb index 714d5a45..541d6940 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -36,7 +36,7 @@ class Ability if highest_role.get_permission("can_manage_users") can [:index, :roles, :edit_user, :promote, :demote, :ban_user, :unban_user, - :approve, :invite, :reset, :undelete], :admin + :approve, :invite, :reset, :undelete, :merge_user], :admin end can [:index, :server_recordings, :server_rooms], :admin if highest_role.get_permission("can_manage_rooms_recordings") diff --git a/app/models/room.rb b/app/models/room.rb index a5bc17a3..b7354694 100644 --- a/app/models/room.rb +++ b/app/models/room.rb @@ -60,6 +60,11 @@ class Room < ApplicationRecord user.rooms.include?(self) end + # Determines whether room is a home room + def home_room? + owner.main_room == self + end + def shared_users User.where(id: shared_access.pluck(:user_id)) end diff --git a/app/views/admins/components/_users.html.erb b/app/views/admins/components/_users.html.erb index 74383a07..8d46812e 100644 --- a/app/views/admins/components/_users.html.erb +++ b/app/views/admins/components/_users.html.erb @@ -124,6 +124,9 @@ <%= link_to admin_edit_user_path(user_uid: user.uid), class: "dropdown-item" do %> <%= t("administrator.users.settings.edit") %> <% end %> + <%= button_to admin_ban_path(user_uid: user.uid), class: "dropdown-item", "data-disable": "" do %> <%= t("administrator.users.settings.ban") %> <% end %> @@ -157,3 +160,4 @@ <%= render "shared/modals/invite_user_modal" %> <%= render "shared/modals/delete_account_modal", delete_location: relative_root %> +<%= render "shared/modals/merge_user_modal" %> \ No newline at end of file diff --git a/app/views/shared/modals/_merge_user_modal.html.erb b/app/views/shared/modals/_merge_user_modal.html.erb new file mode 100644 index 00000000..e5c74bbc --- /dev/null +++ b/app/views/shared/modals/_merge_user_modal.html.erb @@ -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 . +%> + + diff --git a/app/views/shared/modals/_share_room_modal.html.erb b/app/views/shared/modals/_share_room_modal.html.erb index 87800dfa..5a06d9e4 100644 --- a/app/views/shared/modals/_share_room_modal.html.erb +++ b/app/views/shared/modals/_share_room_modal.html.erb @@ -21,7 +21,7 @@

<%= t("modal.share_access.title") %>

- " data-live-search="true" data-virtual-scroll="true" > <% @user_list.each do |user| %> <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 570c84fa..098e6d35 100755 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -84,6 +84,8 @@ en: demoted: User has been successfully demoted invite: Invite successfully sent to %{email} 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 promoted: User has been successfully promoted registration_method_updated: Registration method successfully updated @@ -245,6 +247,8 @@ en: body: 'To view the recording, follow the link below:' 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/.' + search: + start: Start searching... 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." welcome: Welcome to BigBlueButton. @@ -308,6 +312,7 @@ en: maintenance: window_alert: Maintenance window scheduled for %{date} max_concurrent: The maximum number of concurrent sessions allowed has been reached! + merged: Merged modal: create_role: create: Create a new Role @@ -368,6 +373,13 @@ en: 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! 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! diff --git a/config/routes.rb b/config/routes.rb index 9fb059d3..b9656163 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -50,6 +50,7 @@ Rails.application.routes.draw do post '/approve/:user_uid', to: 'admins#approve', as: :admin_approve get '/reset', to: 'admins#reset', as: :admin_reset post '/undelete', to: 'admins#undelete', as: :admin_undelete + post '/merge/:user_uid', to: 'admins#merge_user', as: :merge_user # Site Settings post '/update_settings', to: 'admins#update_settings', as: :admin_update_settings post '/registration_method', to: 'admins#registration_method', as: :admin_change_registration diff --git a/spec/controllers/admins_controller_spec.rb b/spec/controllers/admins_controller_spec.rb index 2b16da90..0801bca0 100644 --- a/spec/controllers/admins_controller_spec.rb +++ b/spec/controllers/admins_controller_spec.rb @@ -197,6 +197,42 @@ describe AdminsController, type: :controller do expect(response).to redirect_to(admins_path) 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 describe "User Design" do