Skip to content

Commit

Permalink
implementing CSV User import, rubocop -A changes, updating node packa…
Browse files Browse the repository at this point in the history
…ges, updating gems
  • Loading branch information
AlexanderUngefug committed Jun 5, 2024
1 parent 2907612 commit 08e2c4b
Show file tree
Hide file tree
Showing 27 changed files with 1,186 additions and 505 deletions.
555 changes: 555 additions & 0 deletions Gemfile.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Procfile.dev
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
web: bin/rails server -p $PORT
web: ruby bin/rails server -p $PORT
js: yarn build:development
css: yarn build:development:css --watch
css: yarn build:development:css --watch
15 changes: 12 additions & 3 deletions app/controllers/api/v1/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ def create
# Users created by a user will have the creator language by default with a fallback to the server configured default_locale.
create_user_params[:language] = current_user&.language || I18n.default_locale if create_user_params[:language].blank?

user = UserCreator.new(user_params: create_user_params.except(:invite_token), provider: current_provider, role: default_role).call
role_name = create_user_params[:role] || default_role.name
user_role = Role.find_by(name: role_name, provider: current_provider)

user = UserCreator.new(user_params: create_user_params.except(:invite_token, :role), provider: current_provider, role: user_role).call

smtp_enabled = ENV['SMTP_SERVER'].present?

Expand All @@ -71,7 +74,7 @@ def create
user.pending! if !admin_create && registration_method == SiteSetting::REGISTRATION_METHODS[:approval]

if user.save
if smtp_enabled
if smtp_enabled && !user.verified?
token = user.generate_activation_token!
UserMailer.with(user:,
activation_url: activate_account_url(token), base_url: request.base_url,
Expand Down Expand Up @@ -156,7 +159,7 @@ def change_password
private

def create_user_params
@create_user_params ||= params.require(:user).permit(:name, :email, :password, :avatar, :language, :role_id, :invite_token)
@create_user_params ||= params.require(:user).permit(:name, :email, :password, :avatar, :language, :role_id, :invite_token, :role, :verified)
end

def update_user_params
Expand All @@ -174,6 +177,12 @@ def valid_invite_token
Invitation.destroy_by(email: create_user_params[:email].downcase, provider: current_provider,
token: create_user_params[:invite_token]).present?
end

def get_role_by_name(role_name)
role = Role.find_by(name: role_name, provider: current_provider)
Rails.logger.info "Role found: #{role.inspect} (Class: #{role.class})" # Log the role object and its class
role
end
end
end
end
2 changes: 1 addition & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def current_user

# Returns whether hcaptcha is enabled by checking if ENV variables are set
def hcaptcha_enabled?
(ENV['HCAPTCHA_SITE_KEY'].present? && ENV['HCAPTCHA_SECRET_KEY'].present?)
ENV['HCAPTCHA_SITE_KEY'].present? && ENV['HCAPTCHA_SECRET_KEY'].present?
end

# Returns the current provider value
Expand Down
35 changes: 22 additions & 13 deletions app/javascript/components/admin/manage_users/ManageUsers.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import PendingUsers from './PendingUsers';
import BannedUsers from './BannedUsers';
import { useAuth } from '../../../contexts/auth/AuthProvider';
import useEnv from '../../../hooks/queries/env/useEnv';
import ImportFromCSV from './forms/ImportFromCSV';


export default function ManageUsers() {
const { t } = useTranslation();
Expand Down Expand Up @@ -67,19 +69,16 @@ export default function ManageUsers() {
<Stack direction="horizontal" className="mb-4">
<SearchBar searchInput={searchInput} setSearchInput={setSearchInput} />
<div className="ms-auto">
{registrationMethod === 'invite'
&& (
<Modal
modalButton={(
<Button variant="brand-outline" className="me-3">
<EnvelopeIcon className="hi-s me-1" />{t('admin.manage_users.invite_user')}
</Button>
)}
title={t('admin.manage_users.invite_user')}
body={<InviteUserForm />}
size="md"
/>
<Modal
modalButton={(
<Button variant="brand-outline" className="me-3">
<EnvelopeIcon className="hi-s me-1" />{t('admin.manage_users.invite_user')}
</Button>
)}
title={t('admin.manage_users.invite_user')}
body={<InviteUserForm />}
size="md"
/>
{
(!envAPI.isLoading && !envAPI.data?.EXTERNAL_AUTH)
&& (
Expand All @@ -92,7 +91,17 @@ export default function ManageUsers() {
/>
)
}

{(!envAPI.isLoading && !envAPI.data?.EXTERNAL_AUTH) && (
<Modal
modalButton={(
<Button variant="brand" className="mx-2">
<UserPlusIcon className="hi-s me-1" /> CSV Import
</Button>
)}
title={t('admin.manage_users.create_new_user')}
body={<ImportFromCSV />}
/>
)}
</div>
</Stack>
<Tabs defaultActiveKey="active" unmountOnExit>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2022 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.
//
// Greenlight 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 Greenlight; if not, see <http://www.gnu.org/licenses/>.

import React from 'react';
import { Button, Stack } from 'react-bootstrap';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import Spinner from '../../../shared_components/utilities/Spinner';
import useAdminCreateUser from '../../../../hooks/mutations/admin/manage_users/useAdminCreateUser';


export default function UserSignupForm({ handleClose, users }) {
const { t } = useTranslation();
const createUserAPI = useAdminCreateUser({ onSettled: handleClose });

async function handleSubmit() {
for (const user of users) {
try {
await createUserAPI.mutateAsync(user);
console.log('User created successfully:', user.email);
} catch (error) {
console.error('Error creating user:', user.email, error);
}
}
}

return (

<Stack className="mt-1" direction="horizontal" gap={1}>
<Button variant="neutral" className="ms-auto" onClick={handleClose}>
{t('close')}
</Button>
<Button variant="brand" onClick={() => handleSubmit()} disabled={createUserAPI.isLoading}>
{createUserAPI.isLoading && <Spinner className="me-2" />}
{t('admin.manage_users.create_account')}
</Button>
</Stack>

);
}

UserSignupForm.propTypes = {
handleClose: PropTypes.func,
};

UserSignupForm.defaultProps = {
handleClose: () => { },
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { useState } from 'react';
import { Button, Table, Form } from 'react-bootstrap';
import Papa from 'papaparse';
import CsvSignUpForm from './CsvSignUpForm';

const CsvUploadAndDisplay = () => {
const [csvData, setCsvData] = useState([]);
const [error, setError] = useState(null);
const handleFileUpload = (event) => {
const file = event.target.files[0];
if (file) {
Papa.parse(file, {
header: true,
complete: (results) => {
if (results.errors.length) {
setError(results.errors[0].message);
} else {
setCsvData(results.data);
setError(null);
}
},
error: (error) => {
setError(error.message);
},
});
}
};

return (
<div className='d-flex flex-column gap-4'>
<Form.Group controlId="formFile" className="mb-3">
<Form.Label>Upload CSV File</Form.Label>
<Form.Control type="file" accept=".csv" onChange={handleFileUpload} />
</Form.Group>
{error && <div className="alert alert-danger">{error}</div>}
{csvData.length > 0 && (
<CsvSignUpForm
users={csvData.map((data) => ({
name: data.name,
email: data.email,
role: data.role,
password: "Fe99b949!" + generateRandomFiveDigitNumber(),
verified: data.verified.toLowerCase() === 'true',
}))}
/>
)}
{csvData.length > 0 && (
<div className='w-full h-96 overflow-scroll'>
<Table striped bordered hover >
<thead>
<tr>
{Object.keys(csvData[0]).map((key) => (
<th key={key}>{key}</th>
))}
</tr>
</thead>
<tbody>
{csvData.map((row, index) => (
<tr key={index}>
{Object.values(row).map((value, idx) => (
<td key={idx}>{value}</td>
))}
</tr>
))}
</tbody>
</Table>
</div>
)}

</div>
);
};

export default CsvUploadAndDisplay;

function generateRandomFiveDigitNumber() {
const min = 10000;
const max = 99999;
return Math.floor(Math.random() * (max - min + 1)) + min;
}

Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ export default function UserSignupForm({ handleClose }) {
<FormControl field={fields.password} type="password" />
<FormControl field={fields.password_confirmation} type="password" />


<Stack className="mt-1" direction="horizontal" gap={1}>
<Button variant="neutral" className="ms-auto" onClick={handleClose}>
{t('close')}
</Button>
<Button variant="brand" type="submit" disabled={createUserAPI.isLoading}>
{ createUserAPI.isLoading && <Spinner className="me-2" /> }
{ t('admin.manage_users.create_account') }
{createUserAPI.isLoading && <Spinner className="me-2" />}
{t('admin.manage_users.create_account')}
</Button>
</Stack>
</Form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ export default function useUserSignupForm({ defaultValues: _defaultValues, ..._c
},
},
},
role: {
label: "Role",
placeHolder: "Role",
controlId: 'createUserFormRole',
hookForm: {
id: 'role',
},
},
}), [i18n.resolvedLanguage]);

const validationSchema = useSignUpFormValidation();
Expand Down
6 changes: 3 additions & 3 deletions app/models/recording.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def self.search(input)
input: "%#{input}%").includes(:formats)
end

all.includes(:formats)
includes(:formats)
end

def self.public_search(input)
Expand All @@ -55,7 +55,7 @@ def self.public_search(input)
input: "%#{input}%").includes(:formats)
end

all.includes(:formats)
includes(:formats)
end

def self.server_search(input)
Expand All @@ -68,7 +68,7 @@ def self.server_search(input)
.includes(:formats)
end

all.includes(:formats)
includes(:formats)
end

private
Expand Down
2 changes: 1 addition & 1 deletion app/models/role.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def self.search(input)
def create_role_permissions
return if %w[Administrator User Guest SuperAdmin].include? name # skip creation for default roles

Permission.all.find_each do |permission|
Permission.find_each do |permission|
value = case permission.name
when 'CreateRoom', 'SharedList', 'CanRecord'
'true'
Expand Down
10 changes: 5 additions & 5 deletions app/models/room.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def get_setting(name:)
def create_meeting_options
configs = RoomsConfiguration.joins(:meeting_option).where(provider: user.provider).pluck(:name, :value).to_h

MeetingOption.all.find_each do |option|
MeetingOption.find_each do |option|
value = if %w[true default_enabled].include? configs[option.name]
option.true_value
else
Expand Down Expand Up @@ -103,10 +103,10 @@ def set_meeting_id

# Create unique pin for voice brige max 10^5 - 10000 unique ids
def set_voice_brige
if Rails.application.config.voice_bridge_phone_number != nil
id = SecureRandom.random_number((10.pow(5)) - 1)
raise if Room.exists?(voice_bridge: id) || id < 10000
unless Rails.application.config.voice_bridge_phone_number.nil?
id = SecureRandom.random_number(10.pow(5) - 1)
raise if Room.exists?(voice_bridge: id) || id < 10_000

self.voice_bridge = id
end
rescue StandardError
Expand Down
3 changes: 2 additions & 1 deletion app/services/meeting_starter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ def initialize(room:, base_url:, current_user:, provider:)

def call
# TODO: amir - Check the legitimately of the action.
options = RoomSettingsGetter.new(room_id: @room.id, provider: @room.user.provider, current_user: @current_user, only_bbb_options: true, voice_bridge: @room.voice_bridge).call
options = RoomSettingsGetter.new(room_id: @room.id, provider: @room.user.provider, current_user: @current_user, only_bbb_options: true,
voice_bridge: @room.voice_bridge).call
viewer_code = RoomSettingsGetter.new(
room_id: @room.id,
provider: @room.user.provider,
Expand Down
6 changes: 3 additions & 3 deletions app/services/room_settings_getter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ def infer_can_record(room_settings:)
end

def set_voice_brige(room_settings:)
if @voice_bridge != nil
room_settings['voiceBridge'] = "#{@voice_bridge}"
end
return if @voice_bridge.nil?

room_settings['voiceBridge'] = @voice_bridge.to_s
end
end
1 change: 1 addition & 0 deletions config/initializers/content_security_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# with Greenlight; if not, see <http://www.gnu.org/licenses/>.

# frozen_string_literal: true

# Be sure to restart your server when you modify this file.

# Define an application-wide content security policy
Expand Down
1 change: 1 addition & 0 deletions config/initializers/inflections.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# with Greenlight; if not, see <http://www.gnu.org/licenses/>.

# frozen_string_literal: true

# Be sure to restart your server when you modify this file.

# Add new inflection rules using the following format. Inflections
Expand Down
1 change: 1 addition & 0 deletions config/initializers/permissions_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# with Greenlight; if not, see <http://www.gnu.org/licenses/>.

# frozen_string_literal: true

# Define an application-wide HTTP permissions policy. For further
# information see https://developers.google.com/web/updates/2018/06/feature-policy
#
Expand Down
Loading

0 comments on commit 08e2c4b

Please sign in to comment.