diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index a1fe0bd0..c6c989bc 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -314,8 +314,8 @@ def get_from_room_session(room, key) end def remove_from_room_session(room, key) - if session.dig(COOKIE_ROOMS_SCOPE, room.handler, key) - session[COOKIE_ROOMS_SCOPE][room.handler].delete(key) + if session.dig(COOKIE_ROOMS_SCOPE, key) + session[COOKIE_ROOMS_SCOPE].delete(key) end end diff --git a/app/controllers/rooms_controller.rb b/app/controllers/rooms_controller.rb index 80dee877..63e0a804 100644 --- a/app/controllers/rooms_controller.rb +++ b/app/controllers/rooms_controller.rb @@ -8,6 +8,7 @@ class RoomsController < ApplicationController include ApplicationHelper + include MeetingsHelper include BbbApi include BbbAppRooms @@ -19,7 +20,9 @@ class RoomsController < ApplicationController before_action :validate_room, except: %i[launch close] before_action :find_user before_action :find_app_launch, only: %i[launch] + before_action :set_user_groups_on_session, only: %i[launch] before_action :set_room_title, only: :show + before_action :set_group_variables, only: %i[show meetings] before_action only: %i[show launch close] do authorize_user!(:show, @room) @@ -56,6 +59,12 @@ def meetings_pagination offset: offset, includeRecordings: true } + # with groups configured, non-moderators only see meetings that belong to the current + # selected group + if @room.moodle_group_select_enabled? && !@user.moderator?(Abilities.moderator_roles) + options['meta_bbb-moodle-group-id'] = get_from_room_session(@room, 'current_group_id') + end + meetings_and_recordings, all_meetings_loaded = get_all_meetings(@room, options) args = { meetings_and_recordings: meetings_and_recordings, @@ -211,6 +220,20 @@ def filesender_auth redirect_to(filesender_path(@room, record_id: params['record_id'])) end + # POST /rooms/1/set_current_group_on_session + # expected params: [:group_id, :redir_url] + def set_current_group_on_session + if @room.moodle_group_select_enabled? + if params[:group_id].present? + add_to_room_session(@room, 'current_group_id', params[:group_id]) + else + remove_from_room_session(@room, 'current_group_id') + end + end + + redirect_to params[:redir_url] + end + helper_method :meetings, :recording_date, :recording_length private @@ -285,6 +308,84 @@ def set_launch_room ) end + # Adds the user first group ID to the session if the grouping + # feature is enabled. + # Adds the formatted user groups to the session + # Example: + # current_group_id: 1 + # user_groups: {'1': 'Grupo A', '2': 'Grupo B'} + def set_user_groups_on_session + if @room.moodle_group_select_enabled? + moodle_token = @room.moodle_token + Rails.logger.info "Moodle token #{moodle_token.token} found, group select is enabled" + # testing if the token is configured with the necessary functions + wsfunctions = [ + 'core_group_get_activity_groupmode', + 'core_group_get_course_user_groups', + 'core_course_get_course_module_by_instance', + 'core_group_get_course_groups' + ] + + unless Moodle::API.check_token_functions(moodle_token, wsfunctions) + Rails.logger.error 'A function required for the groups feature is not configured in Moodle' + set_error('room', 'moodle_token_misconfigured', :forbidden) + respond_with_error(@error) + return + end + + # the `resource_link_id` provided by Moodle is the `instance_id` of the activity. + # We use it to fetch the activity data, from where we get its `cmid` (course module id) + # to fetch the effective groupmode configured on the activity + activity_data = Moodle::API.get_activity_data(moodle_token, @app_launch.params['resource_link_id']) + if activity_data.nil? + Rails.logger.error "Could not find the necessary data for this activity (instance_id: #{@app_launch.params['resource_link_id']})" + set_error('room', 'moodle_token_misconfigured', :forbidden) + respond_with_error(@error) + return + end + + groupmode = Moodle::API.get_groupmode(moodle_token, activity_data['id']) + # testing if the activity has its groupmode configured for separate groups (1) + # or visible groups (2) + if groupmode == 0 || groupmode.nil? + Rails.logger.error 'The Moodle activity has an invalid groupmode configured' + set_error('room', 'moodle_token_misconfigured', :forbidden) + respond_with_error(@error) + return + end + + Rails.logger.info "Moodle groups are configured for this session (#{@app_launch.nonce})" + + if @user.moderator?(Abilities.moderator_roles) + # Gets all course groups except the default 'All Participants' group (id 0) + groups = Moodle::API.get_course_groups(moodle_token, @app_launch.context_id) + .delete_if{ |element| element['id'] == "0" } + else + groups = Moodle::API.get_user_groups(moodle_token, @user.uid, @app_launch.context_id) + end + + if groups.any? + groups_hash = groups.collect{ |g| g.slice('id', 'name').values }.to_h + current_group_id = groups.first['id'] + else + groups_hash = {'no_groups': 'Você não pertence a nenhum grupo'} + current_group_id = 'no_groups' + end + + add_to_room_session(@room, 'current_group_id', current_group_id) + add_to_room_session(@room, 'user_groups', groups_hash) + end + end + + # Set the variables expected by the `group_select` partial + def set_group_variables + if @room.moodle_group_select_enabled? + @groups_hash = get_from_room_session(@room, 'user_groups') + @group_select = @groups_hash.invert + @current_group_id = get_from_room_session(@room, 'current_group_id') + end + end + def set_room_title if @app_launch&.coc_launch? @title = @room.name diff --git a/app/controllers/scheduled_meetings_controller.rb b/app/controllers/scheduled_meetings_controller.rb index 4f39014e..d76dd678 100644 --- a/app/controllers/scheduled_meetings_controller.rb +++ b/app/controllers/scheduled_meetings_controller.rb @@ -111,8 +111,21 @@ def join # only way for a meeting to be created is through here if @user.present? + opts = {} + if @room.moodle_group_select_enabled? + # coming from an external link + if params[:moodle_group_id].present? + opts = { moodle_group: { id: params[:moodle_group_id] } } + # coming from a 'play' button + else + groups = get_from_room_session(@room, 'user_groups') + group_id = get_from_room_session(@room, 'current_group_id').to_s + opts = { moodle_group: { name: groups[group_id], id: group_id } } unless group_id == 'no_groups' + end + end + # make user wait until moderator is in room - if wait_for_mod?(@scheduled_meeting, @user) && (!mod_in_room?(@scheduled_meeting) || + if wait_for_mod?(@scheduled_meeting, @user) && (!mod_in_room?(@scheduled_meeting, opts) || (params[:no_auto_join] == 'true' && device_type? != 'desktop')) redirect_to wait_room_scheduled_meeting_path(@room, @scheduled_meeting) else @@ -122,7 +135,7 @@ def join end # join as moderator (creates the meeting if not created yet) - res = join_api_url(@scheduled_meeting, @user) + res = join_api_url(@scheduled_meeting, @user, opts) if res[:can_join?] if params[:join_in_app] == 'true' direct_join_url = 'br.rnp.conferenciawebmobile://direct-join/' + res[:join_api_url].gsub(/^https?:\/\//, '') + "&meetingName=#{@scheduled_meeting.name}" @@ -145,7 +158,12 @@ def join return end - if !mod_in_room?(@scheduled_meeting) + opts = {} + if @room.moodle_group_select_enabled? && params[:moodle_group_id].present? + opts = { moodle_group: { id: params[:moodle_group_id] } } + end + + if !mod_in_room?(@scheduled_meeting, opts) redirect_to wait_room_scheduled_meeting_path( @room, @scheduled_meeting, first_name: params[:first_name], last_name: params[:last_name] @@ -153,7 +171,7 @@ def join else # join as guest name = "#{params[:first_name]} #{params[:last_name]}" - res = external_join_api_url(@scheduled_meeting, name) + res = external_join_api_url(@scheduled_meeting, name, opts) if res[:can_join?] if params[:join_in_app] == 'true' direct_join_url = 'br.rnp.conferenciawebmobile://direct-join/' + res[:join_api_url].gsub(/^https?:\/\//, '') + "&meetingName=#{@scheduled_meeting.name}" @@ -196,7 +214,18 @@ def wait first_name: params[:first_name], last_name: params[:last_name] ) end - @is_running = mod_in_room?(@scheduled_meeting) + opts = {} + if @room.moodle_group_select_enabled? + # coming from an external link + if params[:moodle_group_id].present? + opts = { moodle_group: { id: params[:moodle_group_id] } } + # coming from a 'play' button + else + group_id = get_from_room_session(@room, 'current_group_id').to_s + opts = { moodle_group: { id: group_id } } unless group_id == 'no_groups' + end + end + @is_running = mod_in_room?(@scheduled_meeting, opts) @can_join_or_create = join_or_create? end @@ -217,19 +246,57 @@ def external @scheduled_meeting.update_to_next_recurring_date - @is_running = mod_in_room?(@scheduled_meeting) - @participants_count = get_participants_count(@scheduled_meeting) - @ended = !@scheduled_meeting.active? && !mod_in_room?(@scheduled_meeting) - @started_ago = get_current_duration(@scheduled_meeting) + opts = {} + if params[:moodle_group_id].present? && @room.moodle_group_select_enabled? + opts = { moodle_group: { id: params[:moodle_group_id] } } + # ??? testar se grupo existe aqui, pra valer tanto pra user logado quanto nao logado? + # fazer chamada pra api do moodle ou pegar de alguma coisa no db, tipo a @room ou @scheduled? + + # if @user.present? + # Situações pra um user logado: + # grupo existe, faz parte + # se é moderador, pode abrir, senão fica no wait + # grupo existe, n faz parte + # fica no wait + # grupo n existe + # mostra erro 404 + + # testar se ele faz parte daquele grupo? + # ou deixa o join cuidar disso? + if @user.nil? + # meeting exists? + @is_running = mod_in_room?(@scheduled_meeting, opts) + unless @is_running + set_error('scheduled_meeting', 'meeting_not_found', :not_found) + respond_with_error(@error) and return + end + end + end + + @is_running ||= mod_in_room?(@scheduled_meeting, opts) + @ended = !@scheduled_meeting.active? && !mod_in_room?(@scheduled_meeting, opts) + @participants_count = get_participants_count(@scheduled_meeting, opts) + @started_ago = get_current_duration(@scheduled_meeting, opts) @disclaimer = config&.external_disclaimer end def running + opts = {} + if @room.moodle_group_select_enabled? + # coming from an external link + if params[:moodle_group_id].present? + opts = { moodle_group: { id: params[:moodle_group_id] } } + # coming from a 'play' button + else + group_id = get_from_room_session(@room, 'current_group_id').to_s + opts = { moodle_group: { id: group_id } } unless group_id == 'no_groups' + end + end respond_to do |format| format.json { render json: { status: :ok, - running: mod_in_room?(@scheduled_meeting), + running: mod_in_room?(@scheduled_meeting, opts), interval: Rails.configuration.cable_polling_secs.to_i, can_join_or_create: join_or_create? } @@ -280,8 +347,19 @@ def set_blank_repeat_as_nil end def join_or_create? - can_join = (@user.present? && !(wait_for_mod?(@scheduled_meeting, @user) && !mod_in_room?(@scheduled_meeting))) || - (!@user.present? && mod_in_room?(@scheduled_meeting)) + opts = {} + if @room.moodle_group_select_enabled? + # coming from an external link + if params[:moodle_group_id].present? + opts = { moodle_group: { id: params[:moodle_group_id] } } + # coming from a 'play' button + else + group_id = get_from_room_session(@room, 'current_group_id').to_s + opts = { moodle_group: { id: group_id } } unless group_id == 'no_groups' + end + end + can_join = (@user.present? && !(wait_for_mod?(@scheduled_meeting, @user) && !mod_in_room?(@scheduled_meeting, opts))) || + (!@user.present? && mod_in_room?(@scheduled_meeting, opts)) can_join end diff --git a/app/models/room.rb b/app/models/room.rb index 6654caf4..aad552d8 100644 --- a/app/models/room.rb +++ b/app/models/room.rb @@ -21,7 +21,7 @@ def self.from_param(param) def can_create_moodle_calendar_event moodle_token = self.consumer_config&.moodle_token if moodle_token - Moodle::API.check_token_functions(moodle_token, 'core_calendar_create_calendar_events') + Moodle::API.check_token_functions(moodle_token, ['core_calendar_create_calendar_events']) else false end @@ -31,6 +31,14 @@ def consumer_config ConsumerConfig.find_by(key: self.consumer_key) end + def moodle_token + consumer_config&.moodle_token + end + + def moodle_group_select_enabled? + moodle_token&.group_select_enabled? + end + def default_values self.handler ||= Digest::SHA1.hexdigest(SecureRandom.uuid) self.moderator = random_password(8) if moderator.blank? diff --git a/config/locales/en.yml b/config/locales/en.yml index 86f2d0ea..4f67dbd2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -214,6 +214,12 @@ en: suggestion: "The token could not be validated" explanation: "This problem can be caused by using an invalid key when validating the request through the LTI Provider." status_code: "403" + moodle_token_misconfigured: + code: "AccessForbidden" + message: "Access denied" + suggestion: "The Moodle integration is misconfigured" + explanation: "The issue may be caused by incorrect configuration in the LTI integration with your Moodle environment." + status_code: "403" external_context_parse_error: code: "ParseError" message: "Internal error fetching data" @@ -239,6 +245,12 @@ en: suggestion: "The scheduled meeting was not found" explanation: "It might have been removed by the session creator." status_code: "404" + meeting_not_found: + code: "NotFound" + message: "Meeting not found" + suggestion: "The meeting was not found" + explanation: "The group identifier is invalid." + status_code: "404" meeting: learning_dashboard_url_missing: "Error: Learning dashboard URL not configured" bucket_api: diff --git a/config/locales/es.yml b/config/locales/es.yml index 9835fc9a..d0d1a316 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -182,6 +182,12 @@ es: suggestion: "No se puede verificar el token" explanation: "El problema puede deberse al uso de una clave no válida para validar la solicitud realizada por el proveedor de LTI." status_code: "403" + moodle_token_misconfigured: + code: "AccessForbidden" + message: "Acceso denegado" + suggestion: "La integración con Moodle está mal configurada" + explanation: "El problema puede ser causado por una configuración incorrecta en la integración de LTI con su entorno Moodle." + status_code: "403" external_context_parse_error: code: "ParseError" message: "Error interno al buscar datos" @@ -207,6 +213,12 @@ es: suggestion: "No se encontró la programación" explanation: "Es posible que el creador de la sesión lo haya eliminado." status_code: "404" + meeting_not_found: + code: "NotFound" + message: "Reunión no encontrada" + suggestion: "No se encontró la reunión" + explanation: "El identificador de grupo no es válido." + status_code: "404" meeting: learning_dashboard_url_missing: "Error: URL del panel de aprendizaje no está configurada" bucket_api: diff --git a/config/locales/pt.yml b/config/locales/pt.yml index cfd79f02..1af4080b 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -182,6 +182,12 @@ pt: suggestion: "O token não pode ser validado" explanation: "O problema pode ser causado pela utilização de uma chave inválida na validação da requisição feita pelo provedor LTI." status_code: "403" + moodle_token_misconfigured: + code: "AccessForbidden" + message: "Acesso negado" + suggestion: "A integração com o Moodle está mal configurada" + explanation: "O problema pode ser causado pela configuração incorreta na integração do LTI com o seu ambiente Moodle." + status_code: "403" external_context_parse_error: code: “ParseError” message: "Erro interno ao buscar dados" @@ -207,6 +213,12 @@ pt: suggestion: "O agendamento não foi encontrado." explanation: "Ele pode ter sido removido pelo criador da sessão." status_code: "404" + meeting_not_found: + code: "NotFound" + message: "Reunião não encontrada" + suggestion: "A reunião não foi encontrada." + explanation: "O identificador de grupo é inválido." + status_code: "404" meeting: learning_dashboard_url_missing: "Erro: URL do painel de aprendizagem não configurada" bucket_api: diff --git a/config/routes.rb b/config/routes.rb index 0d562432..e44f53e5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -81,6 +81,7 @@ member do get :meetings get :meetings_pagination + post :set_current_group_on_session get '/error/:code', to: 'rooms#error' end diff --git a/db/migrate/20240326200202_add_group_select_enabled_to_moodle_tokens.rb b/db/migrate/20240326200202_add_group_select_enabled_to_moodle_tokens.rb new file mode 100644 index 00000000..3be11b86 --- /dev/null +++ b/db/migrate/20240326200202_add_group_select_enabled_to_moodle_tokens.rb @@ -0,0 +1,5 @@ +class AddGroupSelectEnabledToMoodleTokens < ActiveRecord::Migration[6.1] + def change + add_column(:moodle_tokens, :group_select_enabled, :boolean, default: false) + end +end diff --git a/db/schema.rb b/db/schema.rb index 72752bf5..0481eacc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2024_03_14_172824) do +ActiveRecord::Schema.define(version: 2024_03_26_200202) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -104,6 +104,7 @@ t.string "url" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false + t.boolean "group_select_enabled", default: false t.index ["consumer_config_id"], name: "index_moodle_tokens_on_consumer_config_id" end diff --git a/lib/bbb_api.rb b/lib/bbb_api.rb index 3f58e655..f6f47c14 100644 --- a/lib/bbb_api.rb +++ b/lib/bbb_api.rb @@ -14,40 +14,55 @@ def wait_for_mod?(scheduled_meeting, user) !scheduled_meeting.check_all_moderators end - def mod_in_room?(scheduled_meeting) + def mod_in_room?(scheduled_meeting, opts = {}) room = scheduled_meeting.room - bbb(room).is_meeting_running?(scheduled_meeting.meeting_id) + meeting_id = scheduled_meeting.meeting_id + meeting_id.concat("-#{opts[:moodle_group][:id]}") if opts.key?(:moodle_group) + bbb(room).is_meeting_running?(meeting_id) end - def get_participants_count(scheduled_meeting) + def get_participants_count(scheduled_meeting, opts = {}) room = scheduled_meeting.room - return 0 unless bbb(room).is_meeting_running?(scheduled_meeting.meeting_id) + meeting_id = scheduled_meeting.meeting_id + meeting_id.concat("-#{opts[:moodle_group][:id]}") if opts.key?(:moodle_group) - res = bbb(room).get_meeting_info(scheduled_meeting.meeting_id, scheduled_meeting.hash_id) + return 0 unless bbb(room).is_meeting_running?(meeting_id) + + res = bbb(room).get_meeting_info(meeting_id, scheduled_meeting.hash_id) res[:participantCount] end - def get_current_duration(scheduled_meeting) + def get_current_duration(scheduled_meeting, opts = {}) room = scheduled_meeting.room - return unless bbb(room).is_meeting_running?(scheduled_meeting.meeting_id) + meeting_id = scheduled_meeting.meeting_id + meeting_id.concat("-#{opts[:moodle_group][:id]}") if opts.key?(:moodle_group) + return unless bbb(room).is_meeting_running?(meeting_id) - res = bbb(room).get_meeting_info(scheduled_meeting.meeting_id, scheduled_meeting.hash_id) + res = bbb(room).get_meeting_info(meeting_id, scheduled_meeting.hash_id) time_ago_in_words(res[:startTime].to_datetime) end - def join_api_url(scheduled_meeting, user) + def join_api_url(scheduled_meeting, user, opts = {}) return unless scheduled_meeting.present? && user.present? room = scheduled_meeting.room + meeting_id = scheduled_meeting.meeting_id + meeting_id.concat("-#{opts[:moodle_group][:id]}") if opts.key?(:moodle_group) + + unless bbb(room).is_meeting_running?(meeting_id) + meeting_name = scheduled_meeting.name + create_options = scheduled_meeting.create_options(user).merge({ logoutURL: autoclose_url }) + + if opts.key?(:moodle_group) + meeting_name.concat(" - #{opts[:moodle_group][:name]}") + create_options.merge!({ 'meta_bbb-moodle-group-id': opts[:moodle_group][:id] }) + end - unless bbb(room).is_meeting_running?(scheduled_meeting.meeting_id) begin bbb(room).create_meeting( - scheduled_meeting.name, - scheduled_meeting.meeting_id, - scheduled_meeting.create_options(user).merge( - { logoutURL: autoclose_url } - ) + meeting_name, + meeting_id, + create_options ) rescue BigBlueButton::BigBlueButtonException => e if ['simultaneousMeetingsLimitReachedForSecret', 'simultaneousMeetingsLimitReachedForInstitution'].include? e.key.to_s @@ -67,7 +82,7 @@ def join_api_url(scheduled_meeting, user) # before actually redirecting him if Rails.configuration.check_can_join_meeting join_api_url = bbb(room, false).join_meeting_url( - scheduled_meeting.meeting_id, + meeting_id, user.username(t("default.bigbluebutton.#{role}")), room.attributes[role], { @@ -89,7 +104,7 @@ def join_api_url(scheduled_meeting, user) end join_api_url = bbb(room, false).join_meeting_url( - scheduled_meeting.meeting_id, + meeting_id, user.username(t("default.bigbluebutton.#{role}")), room.attributes[role], { @@ -101,14 +116,16 @@ def join_api_url(scheduled_meeting, user) { can_join?: true, join_api_url: join_api_url } end - def external_join_api_url(scheduled_meeting, full_name) + def external_join_api_url(scheduled_meeting, full_name, opts = {}) return unless scheduled_meeting.present? && full_name.present? room = scheduled_meeting.room + meeting_id = scheduled_meeting.meeting_id + meeting_id.concat("-#{opts[:moodle_group][:id]}") if opts.key?(:moodle_group) locale = I18n.locale == :pt ? 'pt-br' : I18n.locale join_api_url = bbb(room, false).join_meeting_url( - scheduled_meeting.meeting_id, + meeting_id, full_name, room.attributes['viewer'], { guest: true, diff --git a/lib/moodle.rb b/lib/moodle.rb index fd23d198..ef5decc8 100644 --- a/lib/moodle.rb +++ b/lib/moodle.rb @@ -17,36 +17,138 @@ def self.create_calendar_event(moodle_token, scheduled_meeting, context_id) 'events[0][visible]' => 1, 'events[0][eventtype]' => 'course' } - result = post(moodle_token.url, params) if result.nil? || result["exception"].present? - Rails.logger.error("[+++] MOODLE API EXCEPTION [+++] #{result["message"]}") unless result.nil? + Rails.logger.error("[MOODLE API][EXCEPTION - core_calendar_create_calendar_events]: #{result}") unless result.nil? return false end + Rails.logger.info "[MOODLE API][INFO - core_calendar_create_calendar_events]: Event created on Moodle calendar: #{result}" - Rails.logger.info "[MOODLE API] Event created on Moodle calendar: #{result}" true end - def self.check_token_functions(moodle_token, wsfunction) + def self.get_user_groups(moodle_token, user_id, context_id) params = { wstoken: moodle_token.token, - wsfunction: 'core_webservice_get_site_info', + wsfunction: 'core_group_get_course_user_groups', + moodlewsrestformat: 'json', + courseid: context_id, + userid: user_id + } + result = post(moodle_token.url, params) + + if result.nil? || result["exception"].present? + Rails.logger.error("[MOODLE API][EXCEPTION - core_group_get_course_user_groups]: #{result}") unless result.nil? + return nil + end + # TO-DO: Investigar melhor os warnings e como tratá-los. + if result["warnings"].present? + Rails.logger.warn("[MOODLE API][WARNING - core_group_get_course_user_groups]: #{result["warnings"].inspect}") + end + Rails.logger.info "[MOODLE API][INFO - core_group_get_course_user_groups]: User groups (userid #{user_id}, courseid #{context_id}): #{result}" + + result['groups'] + end + + def self.get_groupmode(moodle_token, resource_id) + params = { + wstoken: moodle_token.token, + wsfunction: 'core_group_get_activity_groupmode', + moodlewsrestformat: 'json', + cmid: resource_id, + } + result = post(moodle_token.url, params) + + if result.nil? || result["exception"].present? + Rails.logger.error("[MOODLE API][EXCEPTION - core_group_get_activity_groupmode]: #{result}") unless result.nil? + return nil + end + # TO-DO: Investigar melhor os warnings e como tratá-los. + if result["warnings"].present? + Rails.logger.warn("[MOODLE API][WARNING - core_group_get_activity_groupmode]: #{result["warnings"].inspect}") + end + Rails.logger.info "[MOODLE API][INFO - core_group_get_activity_groupmode]: Activity groupmode (cmid #{resource_id}): #{result}" \ + ' (0 for no groups, 1 for separate groups, 2 for visible groups)' + + result['groupmode'] + end + + def self.get_activity_data(moodle_token, instance_id) + params = { + wstoken: moodle_token.token, + wsfunction: 'core_course_get_course_module_by_instance', moodlewsrestformat: 'json', + module: 'lti', + instance: instance_id, } + result = post(moodle_token.url, params) + + if result.nil? || result["exception"].present? + Rails.logger.error("[MOODLE API][EXCEPTION - core_course_get_course_module_by_instance]: Activity with instance id = #{instance_id} " \ + "-> #{result}") unless result.nil? + return nil + end + # TO-DO: Investigar melhor os warnings e como tratá-los. + if result["warnings"].present? + Rails.logger.warn("[MOODLE API][WARNING - core_course_get_course_module_by_instance]: #{result["warnings"].inspect}") + end + Rails.logger.info "[MOODLE API][INFO - core_course_get_course_module_by_instance]: Activity data (instance #{instance_id}): #{result}" + + result['cm'] + end + + def self.get_course_groups(moodle_token, context_id) + params = { + wstoken: moodle_token.token, + wsfunction: 'core_group_get_course_groups', + moodlewsrestformat: 'json', + courseid: context_id, + } + result = post(moodle_token.url, params) + + if result.nil? || (result.is_a?(Hash) && result["exception"].present?) + Rails.logger.error("[MOODLE API][EXCEPTION - core_group_get_course_groups]: #{result}") unless result.nil? + return nil + end + # TO-DO: Investigar melhor os warnings e como tratá-los. + if (result.is_a?(Hash) && result["warnings"]).present? + Rails.logger.warn("[MOODLE API][WARNING - core_group_get_course_groups]: #{result["warnings"].inspect}") + end + Rails.logger.info "[MOODLE API][INFO - core_group_get_course_groups]: Course groups (courseid #{context_id}): #{result}" + result + end + + def self.check_token_functions(moodle_token, wsfunctions) + params = { + wstoken: moodle_token.token, + wsfunction: 'core_webservice_get_site_info', + moodlewsrestformat: 'json', + } result = post(moodle_token.url, params) return false if result.nil? if result["exception"].present? - Rails.logger.error("[+++] MOODLE API EXCEPTION [+++] #{result["message"]}") + Rails.logger.error("[MOODLE API][EXCEPTION - core_webservice_get_site_info]: #{result}") return false end - result["functions"].any? { |hash| hash["name"] == wsfunction } - end + # Gets all registered function names + function_names = result["functions"].map { |hash| hash["name"] } + # Checks if every element of wsfunctions is listed on the function_names list + missing_functions = wsfunctions - function_names + if missing_functions.empty? + Rails.logger.info "[MOODLE API][INFO - core_webservice_get_site_info]: Every necessary " \ + "function is correctly configured in the Moodle Token service." + return true + else + Rails.logger.error("[MOODLE API][EXCEPTION - core_webservice_get_site_info] The following " \ + "functions are not configured in the Moodle Token service: #{missing_functions}.") + return false + end + end def self.post(host_url, params) begin @@ -60,4 +162,4 @@ def self.post(host_url, params) end end end -end \ No newline at end of file +end diff --git a/themes/elos/assets/javascripts/theme-application-elos.js b/themes/elos/assets/javascripts/theme-application-elos.js index f03284de..d6617f73 100644 --- a/themes/elos/assets/javascripts/theme-application-elos.js +++ b/themes/elos/assets/javascripts/theme-application-elos.js @@ -9,6 +9,10 @@ $(document).on('turbolinks:load', function(){ $('[data-toggle="tooltip"]').tooltip(); + $("#group_id").on('change', function() { + $("#set-group-form").trigger('submit') + }); + $(".datepicker").each(function() { var format = $(this).data('format'); $(this).flatpickr({ diff --git a/themes/elos/config/locales/en.yml b/themes/elos/config/locales/en.yml index e113a930..219dd549 100644 --- a/themes/elos/config/locales/en.yml +++ b/themes/elos/config/locales/en.yml @@ -4,6 +4,8 @@ en: clipboard: link_copied: "Link copied to the clipboard!" schedule: "Schedule" + group_select: + title: "Select group" helpers: distance_of_time_in_hours_and_minutes: long: @@ -51,7 +53,9 @@ en: filters: no_filter: "All" recorded_only: "Recorded" - history: "History" + title: "History" + title_with_all_groups: "History | All groups" + title_with_group: "History | Group: %{group_name}" index: return: "< go back" load: "Load more conferences" @@ -132,6 +136,7 @@ en: table: footnote: "Dates shown in the time zone: %{zone}" title: "Scheduled sessions" + title_with_group: "Scheduled sessions | Group: %{group_name}" wait: retry: "Join" time: diff --git a/themes/elos/config/locales/es.yml b/themes/elos/config/locales/es.yml index 169ff3a7..025e932f 100644 --- a/themes/elos/config/locales/es.yml +++ b/themes/elos/config/locales/es.yml @@ -4,6 +4,8 @@ es: clipboard: link_copied: "Link copiado al clipboard!" schedule: "Agendar" + group_select: + title: "Seleccionar grupo" helpers: distance_of_time_in_hours_and_minutes: long: @@ -51,7 +53,9 @@ es: filters: no_filter: "Todas" recorded_only: "Grabadas" - history: "Historial" + title: "Historial" + title_with_all_groups: "Historial | Todos los grupos" + title_with_group: "Historial | Grupo: %{group_name}" index: return: "< volver" load: "Cargar más conferencias" @@ -124,6 +128,7 @@ es: table: footnote: "Fechas mostradas en la zona horaria: %{zone}" title: "Sesiones programadas" + title_with_group: "Sesiones programadas | Grupo: %{group_name}" wait: retry: "Entrar" time: diff --git a/themes/elos/config/locales/pt.yml b/themes/elos/config/locales/pt.yml index 26451b02..f9fee6a7 100644 --- a/themes/elos/config/locales/pt.yml +++ b/themes/elos/config/locales/pt.yml @@ -4,6 +4,8 @@ pt: clipboard: link_copied: "Link copiado para o clipboard!" schedule: "Agendar" + group_select: + title: "Selecionar grupo" helpers: distance_of_time_in_hours_and_minutes: long: @@ -51,7 +53,9 @@ pt: filters: no_filter: "Todas" recorded_only: "Gravadas" - history: "Histórico" + title: "Histórico" + title_with_all_groups: "Histórico | Todos os grupos" + title_with_group: "Histórico | Grupo: %{group_name}" index: return: "< voltar" load: "Carregar mais conferências" @@ -132,6 +136,7 @@ pt: table: footnote: "Datas exibidas no fuso horário: %{zone}" title: "Sessões agendadas" + title_with_group: "Sessões agendadas | Grupo: %{group_name}" wait: retry: "Entrar" time: diff --git a/themes/elos/views/rooms/meetings.html.erb b/themes/elos/views/rooms/meetings.html.erb index 28db5496..5386a9c8 100644 --- a/themes/elos/views/rooms/meetings.html.erb +++ b/themes/elos/views/rooms/meetings.html.erb @@ -3,7 +3,11 @@ <%= link_to t('meetings.index.return'), room_path(@room), - class: "btn btn-light btn-sm" + class: "btn btn-light btn-sm mb-4" %> +<% if @current_group_id && !@user.moderator?(Abilities.moderator_roles) %> + <%= render "shared/group_select", redir_url: meetings_room_path(@room) %> +<% end %> + <%= render "shared/meetings", user: @user, room: @room, meetings: @meetings, only_public: false %> diff --git a/themes/elos/views/scheduled_meetings/external.html.erb b/themes/elos/views/scheduled_meetings/external.html.erb index 6aa8bd33..45ae38db 100644 --- a/themes/elos/views/scheduled_meetings/external.html.erb +++ b/themes/elos/views/scheduled_meetings/external.html.erb @@ -42,7 +42,7 @@ <%= image_tag 'guest-wait.svg' %> <% end %> - <%= form_with url: join_room_scheduled_meeting_path(@room, @scheduled_meeting) do |form| %> + <%= form_with url: join_room_scheduled_meeting_path(@room, @scheduled_meeting, moodle_group_id: params[:moodle_group_id]) do |form| %>