forked from External/greenlight
		
	@@ -12,6 +12,8 @@
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
//= require jquery2
 | 
					//= require jquery2
 | 
				
			||||||
//= require jquery-ui
 | 
					//= require jquery-ui
 | 
				
			||||||
 | 
					//= require dataTables/jquery.dataTables
 | 
				
			||||||
 | 
					//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
 | 
				
			||||||
//= require bootstrap-sprockets
 | 
					//= require bootstrap-sprockets
 | 
				
			||||||
//= require turbolinks
 | 
					//= require turbolinks
 | 
				
			||||||
//= require_self
 | 
					//= require_self
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										25
									
								
								app/assets/javascripts/channels/recording_update.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/assets/javascripts/channels/recording_update.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					(function() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var initRooms = function() {
 | 
				
			||||||
 | 
					    App.messages = App.cable.subscriptions.create({
 | 
				
			||||||
 | 
					      channel: 'RecordingUpdatesChannel',
 | 
				
			||||||
 | 
					      username: window.location.pathname.split('/').pop()
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      received: function(data) {
 | 
				
			||||||
 | 
					        var table = $("#recordings").DataTable();
 | 
				
			||||||
 | 
					        var rowData = table.row("#"+data.record_id).data();
 | 
				
			||||||
 | 
					        rowData.published = data.published
 | 
				
			||||||
 | 
					        table.row("#"+data.record_id).data(rowData).draw();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  $(document).on("turbolinks:load", function() {
 | 
				
			||||||
 | 
					    if ($("body[data-controller=landing]").get(0)) {
 | 
				
			||||||
 | 
					      if ($("body[data-action=rooms]").get(0)) {
 | 
				
			||||||
 | 
					        initRooms();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}).call(this);
 | 
				
			||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
(function() {
 | 
					(function() {
 | 
				
			||||||
 | 
					  var recordingsTable = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var waitForModerator = function(url) {
 | 
					  var waitForModerator = function(url) {
 | 
				
			||||||
    $.get(url + "/wait", function(html) {
 | 
					    $.get(url + "/wait", function(html) {
 | 
				
			||||||
      $(".center-panel-wrapper").html(html);
 | 
					      $(".center-panel-wrapper").html(html);
 | 
				
			||||||
@@ -72,8 +74,112 @@
 | 
				
			|||||||
      window.location.hostname +
 | 
					      window.location.hostname +
 | 
				
			||||||
      meetingURL.data('path');
 | 
					      meetingURL.data('path');
 | 
				
			||||||
    meetingURL.val(link);
 | 
					    meetingURL.val(link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // initialize recordings datatable
 | 
				
			||||||
 | 
					    recordingsTable = $('#recordings').dataTable({
 | 
				
			||||||
 | 
					      data: [],
 | 
				
			||||||
 | 
					      rowId: 'id',
 | 
				
			||||||
 | 
					      paging: false,
 | 
				
			||||||
 | 
					      searching: false,
 | 
				
			||||||
 | 
					      info: false,
 | 
				
			||||||
 | 
					      ordering: false,
 | 
				
			||||||
 | 
					      language: {
 | 
				
			||||||
 | 
					        emptyTable: "Past recordings are shown here."
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      columns: [
 | 
				
			||||||
 | 
					        { title: "Date Recorded", data: "start_time" },
 | 
				
			||||||
 | 
					        { title: "Duration", data: "duration" },
 | 
				
			||||||
 | 
					        { title: "Views", data: "playbacks" },
 | 
				
			||||||
 | 
					        { title: "Actions", data: "id" }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      columnDefs: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          targets: 2,
 | 
				
			||||||
 | 
					          render: function(data, type, row) {
 | 
				
			||||||
 | 
					            if (type === 'display') {
 | 
				
			||||||
 | 
					              var str = "";
 | 
				
			||||||
 | 
					              if (row.published) {
 | 
				
			||||||
 | 
					                for(let i in data) {
 | 
				
			||||||
 | 
					                  str += '<a href="'+data[i].url+'">'+data[i].type+'</a> ';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              return str;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return data;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          targets: -1,
 | 
				
			||||||
 | 
					          render: function(data, type, row) {
 | 
				
			||||||
 | 
					            if (type === 'display') {
 | 
				
			||||||
 | 
					              var roomName = window.location.pathname.split('/').pop();
 | 
				
			||||||
 | 
					              var published = row.published;
 | 
				
			||||||
 | 
					              var eye = getPublishClass(published);
 | 
				
			||||||
 | 
					              return '<button type="button" class="btn btn-default recording-update" data-id="'+data+'" data-room="'+roomName+'" data-published="'+published+'">' +
 | 
				
			||||||
 | 
					                '<i class="fa '+eye+'" aria-hidden="true"></i></button> ' +
 | 
				
			||||||
 | 
					                '<button type="button" class="btn btn-default recording-delete" data-id="'+data+'" data-room="'+roomName+'">' +
 | 
				
			||||||
 | 
					                '<i class="fa fa-trash-o" aria-hidden="true"></i></button>';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return data;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $('#recordings').on('click', '.recording-update', function(event) {
 | 
				
			||||||
 | 
					      var btn = $(this);
 | 
				
			||||||
 | 
					      var room = btn.data('room');
 | 
				
			||||||
 | 
					      var id = btn.data('id');
 | 
				
			||||||
 | 
					      var published = btn.data('published');
 | 
				
			||||||
 | 
					      btn.prop("disabled", true);
 | 
				
			||||||
 | 
					      $.ajax({
 | 
				
			||||||
 | 
					        method: 'PATCH',
 | 
				
			||||||
 | 
					        url: '/rooms/'+room+'/recordings/'+id,
 | 
				
			||||||
 | 
					        data: {published: (!published).toString()}
 | 
				
			||||||
 | 
					      }).done(function(data) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      }).fail(function(data) {
 | 
				
			||||||
 | 
					        btn.prop("disabled", false);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $('#recordings').on('click', '.recording-delete', function(event) {
 | 
				
			||||||
 | 
					      var room = $(this).data('room');
 | 
				
			||||||
 | 
					      var id = $(this).data('id');
 | 
				
			||||||
 | 
					      $.ajax({
 | 
				
			||||||
 | 
					        method: 'DELETE',
 | 
				
			||||||
 | 
					        url: '/rooms/'+room+'/recordings/'+id
 | 
				
			||||||
 | 
					      }).done(function() {
 | 
				
			||||||
 | 
					        recordingsTable.api().row("#"+id).remove().draw();
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    refreshRecordings();
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var refreshRecordings = function() {
 | 
				
			||||||
 | 
					    if (!recordingsTable) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    table = recordingsTable.api();
 | 
				
			||||||
 | 
					    $.get("/rooms/"+window.location.pathname.split('/').pop()+"/recordings", function(data) {
 | 
				
			||||||
 | 
					      if (!data.is_owner) {
 | 
				
			||||||
 | 
					        table.column(-1).visible( false );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      var i;
 | 
				
			||||||
 | 
					      for (i = 0; i < data.recordings.length; i++) {
 | 
				
			||||||
 | 
					        var totalMinutes = Math.round((new Date(data.recordings[i].end_time) - new Date(data.recordings[i].start_time)) / 1000 / 60);
 | 
				
			||||||
 | 
					        data.recordings[i].duration = totalMinutes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data.recordings[i].start_time = new Date(data.recordings[i].start_time)
 | 
				
			||||||
 | 
					          .toLocaleString([], {month: 'long', day: 'numeric', year: 'numeric', hour12: 'true', hour: '2-digit', minute: '2-digit'});
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      table.clear();
 | 
				
			||||||
 | 
					      table.rows.add(data.recordings);
 | 
				
			||||||
 | 
					      table.columns.adjust().draw();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  $(document).on("turbolinks:load", function() {
 | 
					  $(document).on("turbolinks:load", function() {
 | 
				
			||||||
    init();
 | 
					    init();
 | 
				
			||||||
    if ($("body[data-controller=landing]").get(0)) {
 | 
					    if ($("body[data-controller=landing]").get(0)) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,15 @@
 | 
				
			|||||||
 | 
					$.ajaxSetup({
 | 
				
			||||||
 | 
					  headers: {
 | 
				
			||||||
 | 
					    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var PUBLISHED_CLASSES = ['fa-eye-slash', 'fa-eye']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var getPublishClass = function(published) {
 | 
				
			||||||
 | 
					  return PUBLISHED_CLASSES[+published];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var meetingInstance = null;
 | 
					var meetingInstance = null;
 | 
				
			||||||
class Meeting {
 | 
					class Meeting {
 | 
				
			||||||
  constructor(url, name) {
 | 
					  constructor(url, name) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@
 | 
				
			|||||||
 * It is generally better to create a new file per style scope.
 | 
					 * It is generally better to create a new file per style scope.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *= require jquery-ui
 | 
					 *= require jquery-ui
 | 
				
			||||||
 *= require dataTables/jquery.dataTables
 | 
					 *= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
 | 
				
			||||||
 *= require_tree .
 | 
					 *= require_tree .
 | 
				
			||||||
 *= require_self
 | 
					 *= require_self
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,9 @@
 | 
				
			|||||||
// Place all the styles related to the landing controller here.
 | 
					// Place all the styles related to the landing controller here.
 | 
				
			||||||
// They will automatically be included in application.css.
 | 
					// They will automatically be included in application.css.
 | 
				
			||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
 | 
					// You can use Sass (SCSS) here: http://sass-lang.com/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.rooms {
 | 
				
			||||||
 | 
					  .table-wrapper {
 | 
				
			||||||
 | 
					    padding: 40px 50px 10px 50px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								app/channels/recording_updates_channel.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/channels/recording_updates_channel.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					class RecordingUpdatesChannel < ApplicationCable::Channel
 | 
				
			||||||
 | 
					  def subscribed
 | 
				
			||||||
 | 
					    stream_from "#{params[:username]}_recording_updates_channel"
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@@ -1,17 +1,19 @@
 | 
				
			|||||||
class BbbController < ApplicationController
 | 
					class BbbController < ApplicationController
 | 
				
			||||||
 | 
					  include BbbApi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  before_action :authorize_owner_recording, only: [:update_recordings, :delete_recordings]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # GET /:resource/:id/join
 | 
					  # GET /:resource/:id/join
 | 
				
			||||||
  def join
 | 
					  def join
 | 
				
			||||||
    if ( params[:id].blank? )
 | 
					    if params[:name].blank?
 | 
				
			||||||
      render_response("missing_parameter", "meeting token was not included", :bad_request)
 | 
					      render_bbb_response("missing_parameter", "user name was not included", :unprocessable_entity)
 | 
				
			||||||
    elsif ( params[:name].blank? )
 | 
					 | 
				
			||||||
      render_response("missing_parameter", "user name was not included", :bad_request)
 | 
					 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      user = User.find_by username: params[:id]
 | 
					      user = User.find_by username: params[:id]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      options = if user
 | 
					      options = if user
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          wait_for_moderator: true,
 | 
					          wait_for_moderator: true,
 | 
				
			||||||
 | 
					          meeting_recorded: true,
 | 
				
			||||||
          user_is_moderator: current_user == user
 | 
					          user_is_moderator: current_user == user
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      else
 | 
					      else
 | 
				
			||||||
@@ -19,28 +21,72 @@ class BbbController < ApplicationController
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
      options[:meeting_logout_url] = "#{request.base_url}/#{params[:resource]}/#{params[:id]}"
 | 
					      options[:meeting_logout_url] = "#{request.base_url}/#{params[:resource]}/#{params[:id]}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      bbb_res = helpers.bbb_join_url(
 | 
					      bbb_res = bbb_join_url(
 | 
				
			||||||
        params[:id],
 | 
					        params[:id],
 | 
				
			||||||
        params[:name],
 | 
					        params[:name],
 | 
				
			||||||
        options
 | 
					        options
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
      if bbb_res[:returncode] && current_user && current_user == user
 | 
					      if bbb_res[:returncode] && current_user && current_user == user
 | 
				
			||||||
        ActionCable.server.broadcast "moderator_#{user.username}_join_channel",
 | 
					        ActionCable.server.broadcast "moderator_#{user.username}_join_channel",
 | 
				
			||||||
          moderator: "joined"
 | 
					          moderator: "joined"
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      render_response bbb_res[:messageKey], bbb_res[:message], bbb_res[:status], bbb_res[:response]
 | 
					      render_bbb_response bbb_res, bbb_res[:response]
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # GET /rooms/:id/recordings
 | 
				
			||||||
 | 
					  def recordings
 | 
				
			||||||
 | 
					    @user = User.find_by username: params[:id]
 | 
				
			||||||
 | 
					    if !@user
 | 
				
			||||||
 | 
					      render head(:not_found) && return
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bbb_res = bbb_get_recordings @user.username
 | 
				
			||||||
 | 
					    render_bbb_response bbb_res, bbb_res[:recordings]
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # PATCH /rooms/:id/recordings/:record_id
 | 
				
			||||||
 | 
					  def update_recordings
 | 
				
			||||||
 | 
					    bbb_res = bbb_update_recordings(params[:record_id], params[:published] == 'true')
 | 
				
			||||||
 | 
					    if bbb_res[:returncode]
 | 
				
			||||||
 | 
					      RecordingUpdatesJob.perform_later(@user.username, params[:record_id], bbb_res[:published])
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    render_bbb_response bbb_res
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # DELETE /rooms/:id/recordings/:record_id
 | 
				
			||||||
 | 
					  def delete_recordings
 | 
				
			||||||
 | 
					    bbb_res = bbb_delete_recordings(params[:record_id])
 | 
				
			||||||
 | 
					    render_bbb_response bbb_res
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private
 | 
					  private
 | 
				
			||||||
  def render_response(messageKey, message, status, response={})
 | 
					
 | 
				
			||||||
    @messageKey = messageKey
 | 
					  def authorize_owner_recording
 | 
				
			||||||
    @message = message
 | 
					    user = User.find_by username: params[:id]
 | 
				
			||||||
    @status = status
 | 
					    if !user
 | 
				
			||||||
 | 
					      render head(:not_found) && return
 | 
				
			||||||
 | 
					    elsif !current_user || current_user != user
 | 
				
			||||||
 | 
					      render head(:unauthorized) && return
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    recordings = bbb_get_recordings(params[:id])[:recordings]
 | 
				
			||||||
 | 
					    recordings.each do |recording|
 | 
				
			||||||
 | 
					      if recording[:recordID] == params[:record_id]
 | 
				
			||||||
 | 
					        @user = user
 | 
				
			||||||
 | 
					        return true
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    render head(:not_found) && return
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def render_bbb_response(bbb_res, response={})
 | 
				
			||||||
 | 
					    @messageKey = bbb_res[:messageKey]
 | 
				
			||||||
 | 
					    @message = bbb_res[:message]
 | 
				
			||||||
 | 
					    @status = bbb_res[:status]
 | 
				
			||||||
    @response = response
 | 
					    @response = response
 | 
				
			||||||
    render status: @status
 | 
					    render status: @status && return
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								app/jobs/recording_updates_job.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app/jobs/recording_updates_job.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					class RecordingUpdatesJob < ApplicationJob
 | 
				
			||||||
 | 
					  include BbbApi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  queue_as :default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def perform(room, record_id, published)
 | 
				
			||||||
 | 
					    tries = 0
 | 
				
			||||||
 | 
					    sleep_time = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while tries < 4
 | 
				
			||||||
 | 
					      bbb_res = bbb_get_recordings(nil, record_id)
 | 
				
			||||||
 | 
					      if bbb_res[:recordings].first[:published].to_s == published
 | 
				
			||||||
 | 
					        ActionCable.server.broadcast "#{room}_recording_updates_channel",
 | 
				
			||||||
 | 
					          record_id: record_id,
 | 
				
			||||||
 | 
					          published: bbb_res[:recordings].first[:published]
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					      sleep sleep_time
 | 
				
			||||||
 | 
					      sleep_time = sleep_time * 2
 | 
				
			||||||
 | 
					      tries += 1
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
module BbbHelper
 | 
					module BbbApi
 | 
				
			||||||
  def bbb_endpoint
 | 
					  def bbb_endpoint
 | 
				
			||||||
    Rails.application.secrets[:bbb_endpoint]
 | 
					    Rails.application.secrets[:bbb_endpoint]
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
@@ -7,6 +7,10 @@ module BbbHelper
 | 
				
			|||||||
    Rails.application.secrets[:bbb_secret]
 | 
					    Rails.application.secrets[:bbb_secret]
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def bbb
 | 
				
			||||||
 | 
					    @bbb ||= BigBlueButton::BigBlueButtonApi.new(bbb_endpoint + "api", bbb_secret, "0.8", true)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def random_password(length)
 | 
					  def random_password(length)
 | 
				
			||||||
    o = [('a'..'z'), ('A'..'Z')].map { |i| i.to_a }.flatten
 | 
					    o = [('a'..'z'), ('A'..'Z')].map { |i| i.to_a }.flatten
 | 
				
			||||||
    password = (0...length).map { o[rand(o.length)] }.join
 | 
					    password = (0...length).map { o[rand(o.length)] }.join
 | 
				
			||||||
@@ -19,7 +23,6 @@ module BbbHelper
 | 
				
			|||||||
    options[:wait_for_moderator] ||= false
 | 
					    options[:wait_for_moderator] ||= false
 | 
				
			||||||
    options[:meeting_logout_url] ||= nil
 | 
					    options[:meeting_logout_url] ||= nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bbb ||= BigBlueButton::BigBlueButtonApi.new(bbb_endpoint + "api", bbb_secret, "0.8", true)
 | 
					 | 
				
			||||||
    if !bbb
 | 
					    if !bbb
 | 
				
			||||||
      return call_invalid_res
 | 
					      return call_invalid_res
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
@@ -41,8 +44,7 @@ module BbbHelper
 | 
				
			|||||||
        logout_url = options[:meeting_logout_url] || "#{request.base_url}"
 | 
					        logout_url = options[:meeting_logout_url] || "#{request.base_url}"
 | 
				
			||||||
        moderator_password = random_password(12)
 | 
					        moderator_password = random_password(12)
 | 
				
			||||||
        viewer_password = random_password(12)
 | 
					        viewer_password = random_password(12)
 | 
				
			||||||
        meeting_options = {:record => options[:meeting_recorded].to_s, :logoutURL => logout_url, :moderatorPW => moderator_password, :attendeePW => viewer_password }
 | 
					        meeting_options = {record: options[:meeting_recorded].to_s, logoutURL: logout_url, moderatorPW: moderator_password, attendeePW: viewer_password}
 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Create the meeting
 | 
					        # Create the meeting
 | 
				
			||||||
        bbb.create_meeting(meeting_token, meeting_id, meeting_options)
 | 
					        bbb.create_meeting(meeting_token, meeting_id, meeting_options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -65,6 +67,40 @@ module BbbHelper
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def bbb_get_recordings(meeting_id, record_id=nil)
 | 
				
			||||||
 | 
					    options={}
 | 
				
			||||||
 | 
					    if record_id
 | 
				
			||||||
 | 
					      options[:recordID] = record_id
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    if meeting_id
 | 
				
			||||||
 | 
					      options[:meetingID] = (Digest::SHA1.hexdigest(Rails.application.secrets[:secret_key_base]+meeting_id)).to_s
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    bbb_safe_execute :get_recordings, options
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def bbb_update_recordings(id, published)
 | 
				
			||||||
 | 
					    bbb_safe_execute :publish_recordings, id, published
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def bbb_delete_recordings(id)
 | 
				
			||||||
 | 
					    bbb_safe_execute :delete_recordings, id
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # method must be a symbol of the method's name
 | 
				
			||||||
 | 
					  def bbb_safe_execute(method, *args)
 | 
				
			||||||
 | 
					    if !bbb
 | 
				
			||||||
 | 
					      return call_invalid_res
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      begin
 | 
				
			||||||
 | 
					        response_data = bbb.send(method, *args)
 | 
				
			||||||
 | 
					        response_data[:status] = :ok
 | 
				
			||||||
 | 
					      rescue BigBlueButton::BigBlueButtonException => exc
 | 
				
			||||||
 | 
					        response_data = bbb_exception_res exc
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    response_data
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def success_res(join_url)
 | 
					  def success_res(join_url)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      returncode: true,
 | 
					      returncode: true,
 | 
				
			||||||
@@ -94,4 +130,13 @@ module BbbHelper
 | 
				
			|||||||
      status: :internal_server_error
 | 
					      status: :internal_server_error
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def bbb_exception_res(exc)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      returncode: false,
 | 
				
			||||||
 | 
					      messageKey: 'BBB'+exc.key.capitalize.underscore,
 | 
				
			||||||
 | 
					      message: exc.message,
 | 
				
			||||||
 | 
					      status: :internal_server_error
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
							
								
								
									
										3
									
								
								app/views/bbb/_bbb.jbuilder
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/views/bbb/_bbb.jbuilder
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					json.messageKey messageKey
 | 
				
			||||||
 | 
					json.message message
 | 
				
			||||||
 | 
					json.status status
 | 
				
			||||||
							
								
								
									
										1
									
								
								app/views/bbb/delete_recordings.jbuilder
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/views/bbb/delete_recordings.jbuilder
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					json.partial! 'bbb', messageKey: @messageKey, message: @message, status: @status
 | 
				
			||||||
@@ -1,7 +1,5 @@
 | 
				
			|||||||
json.messageKey @messageKey
 | 
					json.partial! 'bbb', messageKey: @messageKey, message: @message, status: @status
 | 
				
			||||||
json.message @message
 | 
					unless @response.blank?
 | 
				
			||||||
json.status @status
 | 
					 | 
				
			||||||
if @response
 | 
					 | 
				
			||||||
  json.response do
 | 
					  json.response do
 | 
				
			||||||
    json.join_url(@response[:join_url]) if @response[:join_url]
 | 
					    json.join_url(@response[:join_url]) if @response[:join_url]
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										25
									
								
								app/views/bbb/recordings.jbuilder
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/views/bbb/recordings.jbuilder
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					json.partial! 'bbb', messageKey: @messageKey, message: @message, status: @status
 | 
				
			||||||
 | 
					unless @response.blank?
 | 
				
			||||||
 | 
					  json.is_owner current_user == @user
 | 
				
			||||||
 | 
					  json.recordings do
 | 
				
			||||||
 | 
					    unless @response.is_a? Array
 | 
				
			||||||
 | 
					      @response = [@response]
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    json.array!(@response) do |recording|
 | 
				
			||||||
 | 
					      json.id recording[:recordID]
 | 
				
			||||||
 | 
					      json.name recording[:name]
 | 
				
			||||||
 | 
					      json.start_time recording[:startTime]
 | 
				
			||||||
 | 
					      json.end_time recording[:endTime]
 | 
				
			||||||
 | 
					      json.published recording[:published]
 | 
				
			||||||
 | 
					      json.playbacks do
 | 
				
			||||||
 | 
					        unless recording[:playback][:format].is_a? Array
 | 
				
			||||||
 | 
					          recording[:playback][:format] = [recording[:playback][:format]]
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        json.array!(recording[:playback][:format]) do |playback|
 | 
				
			||||||
 | 
					          json.type playback[:type]
 | 
				
			||||||
 | 
					          json.url playback[:url]
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										1
									
								
								app/views/bbb/update_recordings.jbuilder
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/views/bbb/update_recordings.jbuilder
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					json.partial! 'bbb', messageKey: @messageKey, message: @message, status: @status
 | 
				
			||||||
@@ -15,7 +15,7 @@
 | 
				
			|||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
<% end %>
 | 
					<% end %>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="page-wrapper meeting">
 | 
					<div class="page-wrapper meetings">
 | 
				
			||||||
  <div class='container-fluid'>
 | 
					  <div class='container-fluid'>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <%= render 'shared/title', title: 'Start A New Session' %>
 | 
					  <%= render 'shared/title', title: 'Start A New Session' %>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@
 | 
				
			|||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
<% end %>
 | 
					<% end %>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="page-wrapper room">
 | 
					<div class="page-wrapper rooms">
 | 
				
			||||||
  <div class="container-fluid">
 | 
					  <div class="container-fluid">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <%= render 'shared/title', title: page_title %>
 | 
					    <%= render 'shared/title', title: page_title %>
 | 
				
			||||||
@@ -34,5 +34,9 @@
 | 
				
			|||||||
      <% end %>
 | 
					      <% end %>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="table-wrapper">
 | 
				
			||||||
 | 
					      <h3>Past Recordings</h3>
 | 
				
			||||||
 | 
					      <table id="recordings" class="table" width="100%"></table>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,11 +31,13 @@ Rails.application.configure do
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  config.action_mailer.perform_caching = false
 | 
					  config.action_mailer.perform_caching = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  config.active_job.queue_adapter = :async
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # action cable socket URI
 | 
					  # action cable socket URI
 | 
				
			||||||
  config.action_cable.url = "ws://localhost/cable"
 | 
					  config.action_cable.url = "ws://localhost/cable"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # allowed action cable origins
 | 
					  # allowed action cable origins
 | 
				
			||||||
  Rails.application.config.action_cable.allowed_request_origins = ['http://localhost']
 | 
					  config.action_cable.allowed_request_origins = ['http://localhost']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Print deprecation notices to the Rails logger.
 | 
					  # Print deprecation notices to the Rails logger.
 | 
				
			||||||
  config.active_support.deprecation = :log
 | 
					  config.active_support.deprecation = :log
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,6 +55,8 @@ Rails.application.configure do
 | 
				
			|||||||
  # Use a real queuing backend for Active Job (and separate queues per environment)
 | 
					  # Use a real queuing backend for Active Job (and separate queues per environment)
 | 
				
			||||||
  # config.active_job.queue_adapter     = :resque
 | 
					  # config.active_job.queue_adapter     = :resque
 | 
				
			||||||
  # config.active_job.queue_name_prefix = "greenlight_#{Rails.env}"
 | 
					  # config.active_job.queue_name_prefix = "greenlight_#{Rails.env}"
 | 
				
			||||||
 | 
					  config.active_job.queue_adapter = :async
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  config.action_mailer.perform_caching = false
 | 
					  config.action_mailer.perform_caching = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Ignore bad email addresses and do not raise email delivery errors.
 | 
					  # Ignore bad email addresses and do not raise email delivery errors.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,8 +11,11 @@ Rails.application.routes.draw do
 | 
				
			|||||||
  # meetings offer a landing page for NON authenticated users to create and join session in BigBlueButton
 | 
					  # meetings offer a landing page for NON authenticated users to create and join session in BigBlueButton
 | 
				
			||||||
  # rooms offer a customized landing page for authenticated users to create and join session in BigBlueButton
 | 
					  # rooms offer a customized landing page for authenticated users to create and join session in BigBlueButton
 | 
				
			||||||
  get '/:resource/:id', to: 'landing#index', as: :resource
 | 
					  get '/:resource/:id', to: 'landing#index', as: :resource
 | 
				
			||||||
  get '/:resource/:id/join', to: 'bbb#join', as: :bbb_join, defaults: { :format => 'json' }
 | 
					  get '/:resource/:id/join', to: 'bbb#join', as: :bbb_join, defaults: {format: 'json'}
 | 
				
			||||||
  get '/:resource/:id/wait', to: 'landing#wait_for_moderator'
 | 
					  get '/:resource/:id/wait', to: 'landing#wait_for_moderator'
 | 
				
			||||||
 | 
					  get '/rooms/:id/recordings', to: 'bbb#recordings', defaults: {format: 'json'}
 | 
				
			||||||
 | 
					  patch '/rooms/:id/recordings/:record_id', to: 'bbb#update_recordings', defaults: {format: 'json'}
 | 
				
			||||||
 | 
					  delete '/rooms/:id/recordings/:record_id', to: 'bbb#delete_recordings', defaults: {format: 'json'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  root to: 'landing#index', :resource => "meetings"
 | 
					  root to: 'landing#index', :resource => "meetings"
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								test/jobs/recording_updates_job_test.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								test/jobs/recording_updates_job_test.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					require 'test_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RecordingUpdatesJobTest < ActiveJob::TestCase
 | 
				
			||||||
 | 
					  # test "the truth" do
 | 
				
			||||||
 | 
					  #   assert true
 | 
				
			||||||
 | 
					  # end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
		Reference in New Issue
	
	Block a user