forked from External/greenlight
GRN2-260: Added the ability to merge user accounts (#938)
* Added the ability to merge user accounts * Styling fixes
This commit is contained in:
parent
31258272c2
commit
005c738e4d
|
@ -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("<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"){
|
||||
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')
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -88,4 +88,12 @@
|
|||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
#merge-account-arrow {
|
||||
position: absolute;
|
||||
top: 47%;
|
||||
right: 47%;
|
||||
z-index: 999;
|
||||
background: white;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -124,6 +124,9 @@
|
|||
<%= 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") %>
|
||||
<% end %>
|
||||
<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") %>
|
||||
<% 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" %>
|
|
@ -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>
|
|
@ -21,7 +21,7 @@
|
|||
<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" >
|
||||
<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 %>
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue