forked from External/greenlight
		
	Allow recordings to be 'unlisted'
Unlisted is a state between published and unpublished. They are still published in the server, but will not appear to anyone other than the user that created the recording. It is done using a metadata attribute and required several changes in how the application handles publishing and unpublishing.
This commit is contained in:
		@@ -23,17 +23,21 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      received: function(data) {
 | 
			
		||||
 | 
			
		||||
        var recordings = Recordings.getInstance();
 | 
			
		||||
        var table = recordings.table.api()
 | 
			
		||||
        var table = recordings.table.api();
 | 
			
		||||
        var row = table.row("#"+data.record_id);
 | 
			
		||||
 | 
			
		||||
        if (data.action === 'update') {
 | 
			
		||||
          var rowData = row.data();
 | 
			
		||||
          rowData.published = data.published
 | 
			
		||||
 | 
			
		||||
          rowData.published = data.published;
 | 
			
		||||
          rowData.listed = data.listed;
 | 
			
		||||
          table.row("#"+data.record_id).data(rowData);
 | 
			
		||||
          recordings.draw();
 | 
			
		||||
 | 
			
		||||
          var published = (data.published) ? 'published' : 'unpublished';
 | 
			
		||||
          showAlert(I18n['recording_'+published], 4000);
 | 
			
		||||
          var status = data.published ? (data.listed ? 'published' : 'unlisted') : 'unpublished';
 | 
			
		||||
          showAlert(I18n['recording_'+status], 4000);
 | 
			
		||||
        } else if (data.action === 'delete') {
 | 
			
		||||
          row.remove();
 | 
			
		||||
          recordings.draw();
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,8 @@
 | 
			
		||||
        .tooltip('fixTitle');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $('.center-panel-wrapper').on ('click', '.meeting-invite', function (event) {
 | 
			
		||||
    // button used to send invitations to the meeting (i.e. "mailto:" link)
 | 
			
		||||
    $('.center-panel-wrapper').on('click', '.meeting-invite', function (event) {
 | 
			
		||||
      var meetingURL = Meeting.getInstance().getURL();
 | 
			
		||||
      var subject = $(this).data("invite-subject");
 | 
			
		||||
      var body = $(this).data("invite-body").replace("&&URL&&", meetingURL);
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ class @Recordings
 | 
			
		||||
        { data: "previews", orderable: false },
 | 
			
		||||
        { data: "duration", orderable: false },
 | 
			
		||||
        { data: "playbacks", orderable: false },
 | 
			
		||||
        { data: "published", visible: false },
 | 
			
		||||
        { data: "listed", visible: false },
 | 
			
		||||
        { data: "id", orderable: false }
 | 
			
		||||
      ],
 | 
			
		||||
      columnDefs: [
 | 
			
		||||
@@ -78,18 +78,18 @@ class @Recordings
 | 
			
		||||
          render: (data, type, row) ->
 | 
			
		||||
            if type == 'display'
 | 
			
		||||
              roomName = Meeting.getInstance().getId()
 | 
			
		||||
              published = row.published
 | 
			
		||||
              publishText = if published then 'unpublish' else 'publish'
 | 
			
		||||
              recordingActions = $('.hidden-elements').find('.recording-actions')
 | 
			
		||||
              recordingActions.find('.recording-update > i.default')
 | 
			
		||||
                .removeClass(PUBLISHED_CLASSES.join(' '))
 | 
			
		||||
                .addClass(getPublishClass(published))
 | 
			
		||||
              recordingActions.find('.recording-update > i.hover')
 | 
			
		||||
                .removeClass(PUBLISHED_CLASSES.join(' '))
 | 
			
		||||
                .addClass(getPublishClass(!published))
 | 
			
		||||
              recordingActions.find('.recording-update')
 | 
			
		||||
                .attr('data-published', published)
 | 
			
		||||
                .attr('title', I18n[publishText+'_recording'])
 | 
			
		||||
              classes = ['recording-inaccessible', 'recording-unlisted', 'recording-published']
 | 
			
		||||
              if row.published
 | 
			
		||||
                if row.listed
 | 
			
		||||
                  cls = classes[2]
 | 
			
		||||
                else
 | 
			
		||||
                  cls = classes[1]
 | 
			
		||||
              else
 | 
			
		||||
                cls = classes[0]
 | 
			
		||||
              trigger = recordingActions.find('.recording-update-trigger')
 | 
			
		||||
              trigger.removeClass(classes.join(' '))
 | 
			
		||||
              trigger.addClass(cls)
 | 
			
		||||
              return recordingActions.html()
 | 
			
		||||
            return data
 | 
			
		||||
        }
 | 
			
		||||
@@ -111,9 +111,10 @@ class @Recordings
 | 
			
		||||
      html: true,
 | 
			
		||||
      trigger: 'focus',
 | 
			
		||||
      title: ->
 | 
			
		||||
        return I18n.are_you_sure;
 | 
			
		||||
        return $(this).data("popover-title");
 | 
			
		||||
      content: ->
 | 
			
		||||
        return $(".delete-popover-body").html()
 | 
			
		||||
        bodySelector = $(this).data("popover-body")
 | 
			
		||||
        return $(bodySelector).html()
 | 
			
		||||
    }
 | 
			
		||||
    $('#recordings').popover(options)
 | 
			
		||||
 | 
			
		||||
@@ -153,17 +154,25 @@ class @Recordings
 | 
			
		||||
  # setup click handlers for the action buttons
 | 
			
		||||
  setupActionHandlers: ->
 | 
			
		||||
    table_api = this.table.api()
 | 
			
		||||
 | 
			
		||||
    @getTable().on 'click', '.recording-update', (event) ->
 | 
			
		||||
      btn = $(this)
 | 
			
		||||
      row = table_api.row($(this).closest('tr')).data()
 | 
			
		||||
      url = $('.meeting-url').val()
 | 
			
		||||
      id = row.id
 | 
			
		||||
      published = btn.data('published')
 | 
			
		||||
 | 
			
		||||
      published = btn.data('visibility') == "unlisted" ||
 | 
			
		||||
        btn.data('visibility') == "published"
 | 
			
		||||
      listed = btn.data('visibility') == "published"
 | 
			
		||||
 | 
			
		||||
      btn.prop('disabled', true)
 | 
			
		||||
      $.ajax({
 | 
			
		||||
        method: 'PATCH',
 | 
			
		||||
        url: url+'/recordings/'+id,
 | 
			
		||||
        data: {published: (!published).toString()}
 | 
			
		||||
        data: {
 | 
			
		||||
          published: published.toString(),
 | 
			
		||||
          "meta_greenlight-listed": listed.toString()
 | 
			
		||||
        }
 | 
			
		||||
      }).done((data) ->
 | 
			
		||||
 | 
			
		||||
      ).fail((data) ->
 | 
			
		||||
 
 | 
			
		||||
@@ -20,12 +20,6 @@ $.ajaxSetup({
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
var PUBLISHED_CLASSES = ['fa-eye-slash', 'fa-eye']
 | 
			
		||||
 | 
			
		||||
var getPublishClass = function(published) {
 | 
			
		||||
  return PUBLISHED_CLASSES[+published];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var loopJoin = function() {
 | 
			
		||||
  var jqxhr = Meeting.getInstance().getJoinMeetingResponse();
 | 
			
		||||
  jqxhr.done(function(data) {
 | 
			
		||||
@@ -57,4 +51,4 @@ var showAlert = function(html, timeout_delay) {
 | 
			
		||||
 | 
			
		||||
var displayRoomURL = function() {
 | 
			
		||||
  $('.meeting-url').val(Meeting.getInstance().getURL());
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -86,3 +86,17 @@
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.recording-update-trigger {
 | 
			
		||||
  &.recording-inaccessible {
 | 
			
		||||
    color: red;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &.recording-unlisted {
 | 
			
		||||
    color: #e3a91e;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &.recording-published {
 | 
			
		||||
    color: green;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -106,3 +106,7 @@ html, body {
 | 
			
		||||
    text-align: center;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.popover {
 | 
			
		||||
  max-width: none;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -76,9 +76,11 @@ class BbbController < ApplicationController
 | 
			
		||||
 | 
			
		||||
  # PATCH /rooms/:id/recordings/:record_id
 | 
			
		||||
  def update_recordings
 | 
			
		||||
    bbb_res = bbb_update_recordings(params[:record_id], params[:published] == 'true')
 | 
			
		||||
    published = params[:published] == 'true'
 | 
			
		||||
    metadata = params.select{ |k, v| k.match(/^meta_/) }
 | 
			
		||||
    bbb_res = bbb_update_recordings(params[:record_id], published, metadata)
 | 
			
		||||
    if bbb_res[:returncode]
 | 
			
		||||
      RecordingUpdatesJob.perform_later(@user.encrypted_id, params[:record_id], bbb_res[:published])
 | 
			
		||||
      RecordingUpdatesJob.perform_later(@user.encrypted_id, params[:record_id])
 | 
			
		||||
    end
 | 
			
		||||
    render_bbb_response bbb_res
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -19,22 +19,13 @@ class RecordingUpdatesJob < ApplicationJob
 | 
			
		||||
 | 
			
		||||
  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",
 | 
			
		||||
          action: 'update',
 | 
			
		||||
          record_id: record_id,
 | 
			
		||||
          published: bbb_res[:recordings].first[:published]
 | 
			
		||||
        break
 | 
			
		||||
      end
 | 
			
		||||
      sleep sleep_time
 | 
			
		||||
      sleep_time = sleep_time * 2
 | 
			
		||||
      tries += 1
 | 
			
		||||
    end
 | 
			
		||||
  def perform(room, record_id)
 | 
			
		||||
    bbb_res = bbb_get_recordings(nil, record_id)
 | 
			
		||||
    recording = bbb_res[:recordings].first
 | 
			
		||||
    ActionCable.server.broadcast "#{room}_recording_updates_channel",
 | 
			
		||||
      action: 'update',
 | 
			
		||||
      record_id: record_id,
 | 
			
		||||
      published: recording[:published],
 | 
			
		||||
      listed: bbb_is_recording_listed(recording)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,8 @@
 | 
			
		||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
module BbbApi
 | 
			
		||||
  META_LISTED = "greenlight-listed"
 | 
			
		||||
 | 
			
		||||
  def bbb_endpoint
 | 
			
		||||
    Rails.configuration.bigbluebutton_endpoint || ''
 | 
			
		||||
  end
 | 
			
		||||
@@ -65,7 +67,14 @@ module BbbApi
 | 
			
		||||
        logout_url = options[:meeting_logout_url] || "#{request.base_url}"
 | 
			
		||||
        moderator_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,
 | 
			
		||||
          "meta_#{BbbApi::META_LISTED}": false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        # Create the meeting
 | 
			
		||||
        bbb.create_meeting(options[:meeting_name], meeting_id, meeting_options)
 | 
			
		||||
 | 
			
		||||
@@ -148,6 +157,8 @@ module BbbApi
 | 
			
		||||
      else
 | 
			
		||||
        []
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      recording[:listed] = bbb_is_recording_listed(recording)
 | 
			
		||||
    end
 | 
			
		||||
    res
 | 
			
		||||
  end
 | 
			
		||||
@@ -168,8 +179,11 @@ module BbbApi
 | 
			
		||||
    response_data = bbb_exception_res exc
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def bbb_update_recordings(id, published)
 | 
			
		||||
  def bbb_update_recordings(id, published, metadata)
 | 
			
		||||
    bbb_safe_execute :publish_recordings, id, published
 | 
			
		||||
 | 
			
		||||
    params = { recordID: id }.merge(metadata)
 | 
			
		||||
    bbb_safe_execute :send_api_request, "updateRecordings", params
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def bbb_delete_recordings(id)
 | 
			
		||||
@@ -191,6 +205,11 @@ module BbbApi
 | 
			
		||||
    response_data
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def bbb_is_recording_listed(recording)
 | 
			
		||||
    !recording[:metadata].blank? &&
 | 
			
		||||
      recording[:metadata][BbbApi::META_LISTED.to_sym] == "true"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def success_join_res(join_url)
 | 
			
		||||
    {
 | 
			
		||||
      returncode: true,
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ json.recordings do
 | 
			
		||||
    json.end_time recording[:endTime]
 | 
			
		||||
    json.published recording[:published]
 | 
			
		||||
    json.length recording[:length]
 | 
			
		||||
    json.listed recording[:listed]
 | 
			
		||||
    json.previews do
 | 
			
		||||
      json.array!(recording[:previews]) do |preview|
 | 
			
		||||
        json.partial! 'preview', preview: preview
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,17 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
      <%= t('no') %>
 | 
			
		||||
    </button>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="recording-visibility-popover">
 | 
			
		||||
    <button type="button" class="btn btn-default btn-danger recording-update" data-visibility="inaccessible">
 | 
			
		||||
      <%= t('innaccessible') %>
 | 
			
		||||
    </button>
 | 
			
		||||
    <button type="button" class="btn btn-default btn-warning recording-update" data-visibility="unlisted">
 | 
			
		||||
      <%= t('unlisted') %>
 | 
			
		||||
    </button>
 | 
			
		||||
    <button type="button" class="btn btn-default btn-success recording-update" data-visibility="published">
 | 
			
		||||
      <%= t('published') %>
 | 
			
		||||
    </button>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="alert-template">
 | 
			
		||||
    <div class="alert alert-success alert-dismissible fade in" role="alert">
 | 
			
		||||
      <button type="button" class="close" data-dismiss="alert" aria-label="Close">
 | 
			
		||||
@@ -59,12 +70,14 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="recording-actions">
 | 
			
		||||
    <button type="button" class="btn btn-default recording-update bottom-tooltip" data-published="">
 | 
			
		||||
        <i class="fa default" aria-hidden="true"></i>
 | 
			
		||||
        <i class="fa hover" aria-hidden="true"></i>
 | 
			
		||||
    <button type="button" class="btn btn-default has-popover recording-update-trigger"
 | 
			
		||||
            data-placement="left" data-popover-body=".recording-visibility-popover"
 | 
			
		||||
            data-popover-title="<%= t('change_recording_visibility') %>">
 | 
			
		||||
      <i class="fa fa-eye" aria-hidden="true"></i>
 | 
			
		||||
    </button>
 | 
			
		||||
    <a tabindex="0" role="button" class="btn btn-default has-popover delete-tooltip"
 | 
			
		||||
      data-placement="top">
 | 
			
		||||
       data-placement="top" data-popover-body=".delete-popover-body"
 | 
			
		||||
       data-popover-title="<%= t('are_you_sure') %>">
 | 
			
		||||
        <i class="fa fa-trash-o" aria-hidden="true"></i>
 | 
			
		||||
    </a>
 | 
			
		||||
  </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,8 @@
 | 
			
		||||
en-US:
 | 
			
		||||
  actions: Actions
 | 
			
		||||
  are_you: Are you %{name}?
 | 
			
		||||
  are_you_sure: Are you sure?
 | 
			
		||||
  change_recording_visibility: "Change visibility to:"
 | 
			
		||||
  client:
 | 
			
		||||
    are_you_sure: Are you sure?
 | 
			
		||||
    delete_recording: Delete recording
 | 
			
		||||
@@ -48,6 +50,7 @@ en-US:
 | 
			
		||||
    publish_recording: Publish recording
 | 
			
		||||
    recording_deleted: Recording was deleted
 | 
			
		||||
    recording_published: Recording was published
 | 
			
		||||
    recording_unlisted: Recording was unlisted
 | 
			
		||||
    recording_unpublished: Recording was unpublished
 | 
			
		||||
    unpublish_recording: Hide recording
 | 
			
		||||
  copied: Copied
 | 
			
		||||
@@ -61,6 +64,7 @@ en-US:
 | 
			
		||||
  greet_user: Welcome, %{name}
 | 
			
		||||
  greet_guest: Welcome to %{name} Meeting Space
 | 
			
		||||
  hi_all: Hi Everyone
 | 
			
		||||
  innaccessible: Innaccessible
 | 
			
		||||
  join: Join
 | 
			
		||||
  join_session: Join the current session
 | 
			
		||||
  join_session_id: Join %{id}
 | 
			
		||||
@@ -82,11 +86,13 @@ en-US:
 | 
			
		||||
  footer_html: "%{greenlight_link} build %{version}, Powered by %{bbb_link}"
 | 
			
		||||
  presentation: Presentation
 | 
			
		||||
  previously_joined_meetings: Previously Joined Sessions
 | 
			
		||||
  published: Published
 | 
			
		||||
  return_to_room: Return to your personal room
 | 
			
		||||
  session_url_explanation: The session will be taking place using the following URL
 | 
			
		||||
  start: Start
 | 
			
		||||
  your_personal_room: Your Personal Room
 | 
			
		||||
  thumbnails: Thumbnails
 | 
			
		||||
  unlisted: Unlisted
 | 
			
		||||
  url_copy_explanation: Copy this URL to invite others to the meeting
 | 
			
		||||
  url_refresh_hint: Generate a new meeting URL
 | 
			
		||||
  video: Video
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user