Compare commits
2 Commits
add-me-sui
...
microsoft-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d29f14953 | ||
|
|
4b5238435a |
@@ -32,3 +32,10 @@ CUSTOM_API_TOKEN=
|
||||
# Required for simulcasting to Millicast for director mode
|
||||
MILLICAST_API_SECRET=
|
||||
MILLICAST_ACCOUNT_ID=
|
||||
|
||||
# Required for Microsoft Azure AD Auth
|
||||
AZURE_CLIENT_ID = Client App ID
|
||||
AZURE_CLIENT_SECRET = Client App Secret
|
||||
AZURE_TENANT_ID = Client App Tenant ID
|
||||
AZURE_REDIRECT_URI = where microsoft will redirect after login, eg. http://localhost:3000/auth/azure_ad/callback
|
||||
AZURE_SCOPES = Scopes required for Application, eg. 'openid email profile User.Read offline_access OnlineMeetings.ReadWrite'
|
||||
5
Gemfile
5
Gemfile
@@ -139,6 +139,11 @@ gem 'rack-cors'
|
||||
# Ruby wrappers for the HubSpot REST API
|
||||
gem "hubspot-ruby"
|
||||
|
||||
# OAuth
|
||||
gem 'omniauth-oauth2', '~> 1.6'
|
||||
# OmniAuth CSRF protection
|
||||
gem 'omniauth-rails_csrf_protection', '~> 0.1.2'
|
||||
|
||||
group :development, :test, :review do
|
||||
# Call "byebug" anywhere in the code to stop execution and get a debugger console
|
||||
gem "byebug", "~> 11.0.1", platforms: [:mri, :mingw, :x64_mingw]
|
||||
|
||||
19
Gemfile.lock
19
Gemfile.lock
@@ -220,6 +220,7 @@ GEM
|
||||
activesupport (>= 4.2.0)
|
||||
hashdiff (1.0.1)
|
||||
hashery (2.1.2)
|
||||
hashie (4.1.0)
|
||||
hexapdf (0.9.3)
|
||||
cmdparse (~> 3.0, >= 3.0.3)
|
||||
geom2d (~> 0.2)
|
||||
@@ -297,6 +298,7 @@ GEM
|
||||
money (~> 6.13.2)
|
||||
railties (>= 3.0)
|
||||
msgpack (1.3.1)
|
||||
multi_json (1.15.0)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.1.1)
|
||||
nio4r (2.5.2)
|
||||
@@ -308,6 +310,21 @@ GEM
|
||||
warden
|
||||
oath-generators (1.0.1)
|
||||
oath (>= 0.0.12)
|
||||
oauth2 (1.4.4)
|
||||
faraday (>= 0.8, < 2.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
omniauth (1.9.1)
|
||||
hashie (>= 3.4.6)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-oauth2 (1.6.0)
|
||||
oauth2 (~> 1.1)
|
||||
omniauth (~> 1.9)
|
||||
omniauth-rails_csrf_protection (0.1.2)
|
||||
actionpack (>= 4.2)
|
||||
omniauth (>= 1.3.1)
|
||||
parallel (1.19.1)
|
||||
parity (3.2.0)
|
||||
parser (2.6.5.0)
|
||||
@@ -552,6 +569,8 @@ DEPENDENCIES
|
||||
mux_ruby!
|
||||
oath (~> 1.1.0)
|
||||
oath-generators (~> 1.0.1)
|
||||
omniauth-oauth2 (~> 1.6)
|
||||
omniauth-rails_csrf_protection (~> 0.1.2)
|
||||
parity (~> 3.2.0)
|
||||
pdf-reader (~> 2.1.0)
|
||||
pdfkit (~> 0.8.2)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB |
@@ -24,8 +24,6 @@ $(document).on "turbolinks:load", ->
|
||||
stream_selected = $("#broadcast_video").data('videoType') == 'stream';
|
||||
if data.streamer_status == 'recording' && data.status == 'active' && stream_selected
|
||||
$("#broadcast_video").html data.video_content
|
||||
$("#live_take").html data.live_take_content
|
||||
|
||||
new (Clappr.Player)(
|
||||
<%= "baseUrl: 'http://cdn.clappr.io/latest'," if Rails.env.test? %>
|
||||
parentId: '#broadcast_video'
|
||||
@@ -37,7 +35,6 @@ $(document).on "turbolinks:load", ->
|
||||
hlsMinimumDvrSize: 1)
|
||||
if data.streamer_status == "idle" && data.status == "idle"
|
||||
$("#broadcast_video").html data.video_content
|
||||
$("#live_take").html data.live_take_content
|
||||
|
||||
showBroadcastRecordings: (data) ->
|
||||
$(".flash-message").html data.flash_content
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
$(document).on("click", "#download_releases", function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const releasable_ids = JSON.parse($("#selected_releases_form").attr('data-releasable-ids'));
|
||||
const total_entries = $('#total_entries').val();
|
||||
|
||||
const input_ids = $('<input>').attr({ type: 'hidden', name: 'release_ids', value: JSON.stringify(releasable_ids) });
|
||||
const search_query = $('<input>').attr({ type: 'hidden', name: 'search_query', value: $("form input[type='search']").val() });
|
||||
const type_filter = $('<input>').attr({ type: 'hidden', name: 'type_filter', value: $('#type_filter_value').val() });
|
||||
|
||||
const download_count = releasable_ids.length > 0 ? releasable_ids.length : total_entries;
|
||||
|
||||
$(this).parent().append(input_ids);
|
||||
$(this).parent().append(search_query);
|
||||
$(this).parent().append(type_filter);
|
||||
|
||||
if (confirm(`${download_count} release(s) will be downloaded. Is this correct?`)){
|
||||
Rails.fire($(this).parent()[0], 'submit');
|
||||
}
|
||||
});
|
||||
@@ -1,4 +1,8 @@
|
||||
$(document).on("click", "[data-behavior=play_recording]", function() {
|
||||
if ($(this).hasClass('active')){
|
||||
return false;
|
||||
}
|
||||
|
||||
$("#broadcast_video").data('videoType', 'recording');
|
||||
|
||||
var playback_url = $(this).attr("data-playback-url")
|
||||
@@ -12,6 +16,13 @@ $(document).on("click", "[data-behavior=play_recording]", function() {
|
||||
height: '100%',
|
||||
autoPlay: true
|
||||
});
|
||||
|
||||
$(".dropdown-menu").children().removeClass('active');
|
||||
$(".dropdown-menu").children().children('i').remove();
|
||||
$(this).siblings().removeClass('active');
|
||||
$(this).siblings().children("i").remove();
|
||||
$(this).addClass('active');
|
||||
$(this).prepend('<i class="fa fa-check"> </i>');
|
||||
});
|
||||
|
||||
$(document).on("click", "[data-behavior=play_stream]", function() { $("#broadcast_video").data('videoType', 'stream'); });
|
||||
@@ -1,4 +0,0 @@
|
||||
$(document).on("click", "#director_mode_switch", function() {
|
||||
// JQuery click event trigger was not working.
|
||||
document.getElementById("director_mode_link").click();
|
||||
});
|
||||
@@ -1,30 +1,4 @@
|
||||
// Do not allow file attachments in rich text content
|
||||
addEventListener("trix-file-accept", function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
Trix.config.textAttributes.underline = {
|
||||
style: { "textDecoration": "underline" },
|
||||
inheritable: true,
|
||||
parser: function (element) {
|
||||
var style = window.getComputedStyle(element);
|
||||
return style.textDecoration === "underline";
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('trix-initialize', function (e) {
|
||||
const trix = e.target;
|
||||
const toolBar = trix.toolbarElement;
|
||||
|
||||
// // Creation of the button
|
||||
const button = document.createElement("button");
|
||||
button.setAttribute("type", "button");
|
||||
button.setAttribute("class", "trix-button trix-button--icon trix-button--icon-underline");
|
||||
button.setAttribute("data-trix-attribute", "underline");
|
||||
button.setAttribute("title", "underline");
|
||||
button.setAttribute("tabindex", "-1");
|
||||
button.innerText = "U";
|
||||
|
||||
// Attachment of the button to the toolBar
|
||||
toolBar.querySelector('.trix-button-group--text-tools').appendChild(button);
|
||||
});
|
||||
})
|
||||
|
||||
@@ -412,16 +412,6 @@ a[data-behavior=seekable-timecode] {
|
||||
background-color: rgba($black, 0.05);
|
||||
}
|
||||
|
||||
// Black background
|
||||
.bg-black {
|
||||
background-color: $black;
|
||||
}
|
||||
|
||||
// White background
|
||||
.bg-white {
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
// Custom width
|
||||
.w-65 {
|
||||
width: 65% !important;
|
||||
@@ -432,39 +422,8 @@ a[data-behavior=seekable-timecode] {
|
||||
max-height: 30rem;
|
||||
}
|
||||
|
||||
// Max-width 75%
|
||||
.max-w-75 {
|
||||
max-width: 75%;
|
||||
}
|
||||
|
||||
// Fix height and width
|
||||
.fix-h-and-w {
|
||||
width: 308px;
|
||||
height:308px;
|
||||
}
|
||||
|
||||
// Dropdown shown state overrride
|
||||
.override-dropdown-show-state {
|
||||
color: $white !important;
|
||||
background-color: $black !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
// Override custom switch color
|
||||
.override-custom-control-label::before {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
// Override nav tab design
|
||||
.override-nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active {
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
border-bottom: 3px solid #ff0000;
|
||||
}
|
||||
|
||||
//Trix underline style
|
||||
trix-toolbar {
|
||||
.trix-button--icon-underline::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M12 17c3.31 0 6-2.69 6-6V3h-2.5v8c0 1.93-1.57 3.5-3.5 3.5S8.5 12.93 8.5 11V3H6v8c0 3.31 2.69 6 6 6zm-7 2v2h14v-2H5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
}
|
||||
@@ -48,10 +48,6 @@ u {
|
||||
margin-right: -30px;
|
||||
}
|
||||
|
||||
.embed-person-photo {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.do-not-copy-warning {
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ class BroadcastsChannel < ApplicationCable::Channel
|
||||
def self.broadcast_stream_updates(broadcast)
|
||||
status_content = ApplicationController.render partial: "broadcasts/broadcast_status", locals: { broadcast: broadcast }
|
||||
video_content = ApplicationController.render partial: "broadcasts/video", locals: { broadcast: broadcast }
|
||||
live_take = ApplicationController.render partial: "broadcasts/live_take", locals: { broadcast: broadcast }
|
||||
|
||||
broadcast_to broadcast, {
|
||||
event: :broadcast_stream_update,
|
||||
@@ -19,7 +18,6 @@ class BroadcastsChannel < ApplicationCable::Channel
|
||||
playback_url: broadcast.stream_playback_url,
|
||||
full_live_stream_playback_url: broadcast.full_live_stream_playback_url,
|
||||
video_content: video_content,
|
||||
live_take_content: live_take,
|
||||
status_content: status_content,
|
||||
streamer_status: broadcast.streamer_status
|
||||
}
|
||||
|
||||
@@ -45,14 +45,13 @@ class Api::ReleasesController < Api::ApiController
|
||||
if model_name == "acquired_media_release"
|
||||
mapping = {
|
||||
"#{model_name.camelize}": SerializableAcquiredMediaRelease,
|
||||
FileInfo: SerializableFileInfo,
|
||||
"ActiveStorage::Attachment".to_sym => ActiveStorage::SerializableAttachment,
|
||||
FileInfo: SerializableFileInfo
|
||||
}
|
||||
|
||||
render jsonapi: release,
|
||||
status: status,
|
||||
class: mapping,
|
||||
include: [:files, :file_infos]
|
||||
include: [:file_infos]
|
||||
else
|
||||
mapping = {
|
||||
"#{model_name.camelize}": show_serializable,
|
||||
|
||||
@@ -4,7 +4,6 @@ require './lib/knock_monkeypatch'
|
||||
|
||||
class Api::UserTokenController < Knock::AuthTokenController
|
||||
include Oath::ControllerHelpers
|
||||
include RememberMe::Controller
|
||||
|
||||
skip_before_action :verify_authenticity_token
|
||||
before_action :sign_in_user
|
||||
|
||||
@@ -6,12 +6,9 @@ class ApprovalsController < ApplicationController
|
||||
|
||||
def create
|
||||
@releasable.approve_by(current_user)
|
||||
@releasable.approved_by_user_signature.attach(approved_by_user_signature_params) if signature_data.present?
|
||||
|
||||
if @releasable.save(context: :approval)
|
||||
if @releasable.save
|
||||
redirect_to [@project, "#{@releasable_param.name.pluralize}"], notice: t('.release_approved', release_type: @releasable.model_name.human)
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
@@ -28,21 +25,4 @@ class ApprovalsController < ApplicationController
|
||||
def set_project
|
||||
@project = @releasable.project
|
||||
end
|
||||
|
||||
def releasable_params
|
||||
params.require(releasable_param.name).permit(approved_by_user_signature: :data)
|
||||
end
|
||||
|
||||
def signature_data
|
||||
releasable_params.dig(:approved_by_user_signature, :data)
|
||||
end
|
||||
|
||||
def approved_by_user_signature_params
|
||||
{
|
||||
data: signature_data,
|
||||
filename: "approved_by_user_signature.png",
|
||||
content_type: "image/png",
|
||||
identify: false,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
class BroadcastRecordingStarringsController < ApplicationController
|
||||
layout "project"
|
||||
|
||||
before_action :set_project
|
||||
before_action :set_broadcast
|
||||
before_action :set_recording
|
||||
|
||||
def create
|
||||
@recording.toggle_star
|
||||
set_recordings
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def broadcast_recording_params
|
||||
params.require(:broadcast_recording).permit(:name, :description)
|
||||
end
|
||||
|
||||
def set_project
|
||||
@project = policy_scope(Project).find(params[:project_id])
|
||||
end
|
||||
|
||||
def set_broadcast
|
||||
@broadcast = authorize policy_scope(@project.broadcasts).find(params[:broadcast_id])
|
||||
end
|
||||
|
||||
def set_recording
|
||||
@recording = authorize policy_scope(@broadcast.broadcast_recordings).find(params[:broadcast_recording_id])
|
||||
end
|
||||
|
||||
def set_recordings
|
||||
@recordings = @broadcast.broadcast_recordings.visible.order_by_recent.paginate(page: params[:page])
|
||||
end
|
||||
end
|
||||
@@ -5,25 +5,13 @@ class BroadcastRecordingsController < ApplicationController
|
||||
before_action :set_broadcast
|
||||
before_action :set_recording
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
def update
|
||||
@recording.update(broadcast_recording_params)
|
||||
set_recordings
|
||||
end
|
||||
|
||||
def destroy
|
||||
@recording.update(hidden: true)
|
||||
set_recordings
|
||||
@recordings = @broadcast.broadcast_recordings.visible.order_by_recent.paginate(page: params[:page])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def broadcast_recording_params
|
||||
params.require(:broadcast_recording).permit(:name, :description)
|
||||
end
|
||||
|
||||
def set_project
|
||||
@project = policy_scope(Project).find(params[:project_id])
|
||||
end
|
||||
@@ -35,8 +23,4 @@ class BroadcastRecordingsController < ApplicationController
|
||||
def set_recording
|
||||
@recording = authorize policy_scope(@broadcast.broadcast_recordings).find(params[:id])
|
||||
end
|
||||
|
||||
def set_recordings
|
||||
@recordings = @broadcast.broadcast_recordings.visible.order_by_recent.paginate(page: params[:page])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,7 +32,6 @@ class BroadcastsController < ApplicationController
|
||||
@conference_url = conference_url_for(@broadcast)
|
||||
@recordings = @broadcast.broadcast_recordings.visible.order_by_recent.paginate(page: params[:page])
|
||||
@files = @broadcast.files.order("created_at DESC").paginate(page: params[:files_page])
|
||||
|
||||
render layout: 'application'
|
||||
end
|
||||
|
||||
@@ -76,7 +75,7 @@ class BroadcastsController < ApplicationController
|
||||
end
|
||||
|
||||
def broadcast_params
|
||||
params.require(:broadcast).permit(:name, :shoot_location_time_zone, files: [])
|
||||
params.require(:broadcast).permit(:name, :shoot_location_time_zone, :conference_option, files: [])
|
||||
end
|
||||
|
||||
def set_project
|
||||
@@ -114,7 +113,7 @@ class BroadcastsController < ApplicationController
|
||||
end
|
||||
|
||||
def conference_url_for(broadcast)
|
||||
broadcast.video_conference_url_override.presence || url_for([broadcast.project, broadcast, :zoom_meeting])
|
||||
broadcast.video_conference_url_override || url_for([broadcast.project, broadcast, :conference_meeting])
|
||||
end
|
||||
|
||||
def log_create_analytics
|
||||
|
||||
22
app/controllers/callbacks_controller.rb
Normal file
22
app/controllers/callbacks_controller.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
class CallbacksController < ApplicationController
|
||||
skip_before_action :require_login
|
||||
skip_after_action :verify_authorized, except: :index
|
||||
skip_after_action :verify_policy_scoped, only: :index
|
||||
skip_before_action :verify_authenticity_token
|
||||
|
||||
def create
|
||||
uid = request.env['omniauth.auth'][:uid]
|
||||
token_data = request.env['omniauth.auth'][:credentials]
|
||||
|
||||
current_user&.tap do |user|
|
||||
user.microsoft_user_id = uid
|
||||
user.microsoft_access_token = token_data.token
|
||||
user.microsoft_refresh_token = token_data.refresh_token
|
||||
user.microsoft_token_expires_at = token_data.expires_at # Expiration time is returned in seconds
|
||||
user.save
|
||||
end
|
||||
|
||||
redirect_to profile_path
|
||||
end
|
||||
|
||||
end
|
||||
45
app/controllers/conference_meetings_controller.rb
Normal file
45
app/controllers/conference_meetings_controller.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
class ConferenceMeetingsController < ApplicationController
|
||||
require 'microsoft_graph'
|
||||
|
||||
def show
|
||||
authorize broadcast = Broadcast.find(params[:broadcast_id])
|
||||
case broadcast.conference_option
|
||||
when 'zoom'
|
||||
redirect_to broadcast.zoom_meeting_url
|
||||
when 'ms_teams'
|
||||
if broadcast.conference_join_url.nil?
|
||||
begin
|
||||
graph_api = MicrosoftGraph.new(
|
||||
current_user,
|
||||
ENV['AZURE_CLIENT_ID'],
|
||||
ENV['AZURE_CLIENT_SECRET'],
|
||||
ENV['AZURE_TENANT_ID'],
|
||||
ENV['AZURE_SCOPES']
|
||||
)
|
||||
|
||||
subject = "#{broadcast.name} Online Meeting"
|
||||
teams_meeting = graph_api.create_teams_meeting(subject)
|
||||
join_url = teams_meeting['joinUrl']
|
||||
|
||||
if join_url.present?
|
||||
broadcast.conference_join_url = join_url
|
||||
broadcast.save
|
||||
else
|
||||
raise StandardError, 'Failed to read teams meeting join URL'
|
||||
end
|
||||
rescue ActionController::InvalidAuthenticityToken => e
|
||||
Rails.logger.error(e.message)
|
||||
redirect_to project_broadcast_url(broadcast.project, broadcast), alert: t('.alerts.not_authenticated')
|
||||
return
|
||||
rescue StandardError => e
|
||||
Rails.logger.error(e.message)
|
||||
redirect_to project_broadcast_url(broadcast.project, broadcast), alert: t('.alerts.failed_to_join')
|
||||
return
|
||||
end
|
||||
end
|
||||
redirect_to broadcast.conference_join_url
|
||||
else
|
||||
redirect_to project_broadcast_url(broadcast.project, broadcast), alert: t('.alerts.unknown_conference_option')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -7,41 +7,28 @@ class ContractDownloadsController < ApplicationController
|
||||
|
||||
def create
|
||||
authorize policy_scope(Download).create
|
||||
|
||||
download = @project.downloads.create!(release_type: release_type)
|
||||
fetch_releases
|
||||
|
||||
download = @project.downloads.create!(release_type: params[:release_type])
|
||||
other_downloads_in_progress = @project.downloads.unfinished_desc_order.offset(1)
|
||||
|
||||
if other_downloads_in_progress.any?
|
||||
in_progress_downloads_details = render_to_string "_other_pending_downloads", locals: { downloads: other_downloads_in_progress, release_type: release_type }, :layout => false
|
||||
in_progress_downloads_details = render_to_string "_other_pending_downloads", locals: { downloads: other_downloads_in_progress, release_type: params[:release_type] }, :layout => false
|
||||
ProjectsChannel.broadcast_download_generation_update(download, in_progress_downloads_details)
|
||||
else
|
||||
ProjectsChannel.broadcast_download_generation_update(download, I18n.t("contract_downloads.download.pending", release_type: release_type.titleize))
|
||||
ProjectsChannel.broadcast_download_generation_update(download, I18n.t("contract_downloads.download.pending", release_type: params[:release_type].titleize))
|
||||
end
|
||||
|
||||
GenerateContractsZipJob.perform_later(@project, download, release_type, release_ids, search_query, type_filter)
|
||||
GenerateContractsZipJob.perform_later(@project, download, params[:release_type], @releases.ids)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def release_type
|
||||
params[:release_type]
|
||||
def fetch_releases
|
||||
@releases = policy_scope(@project.public_send(releases))
|
||||
end
|
||||
|
||||
def search_query
|
||||
params[:search_query]
|
||||
end
|
||||
|
||||
def type_filter
|
||||
params[:type_filter]
|
||||
end
|
||||
|
||||
def release_ids
|
||||
JSON.parse(params[:release_ids])
|
||||
rescue StandardError
|
||||
[]
|
||||
end
|
||||
|
||||
def release_name(release_type)
|
||||
release_type.constantize.model_name.plural
|
||||
def releases
|
||||
params[:release_type].constantize.model_name.plural
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,7 +3,7 @@ class ContractsController < ApplicationController
|
||||
respond_to do |format|
|
||||
format.pdf { send_contract_pdf }
|
||||
|
||||
if Rails.env.development? || Rails.env.test?
|
||||
if Rails.env.development?
|
||||
format.html { render_sample_html }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -106,7 +106,7 @@ class MaterialReleasesController < ApplicationController
|
||||
:term_id, :term_text,
|
||||
:restriction_id, :restriction_text,
|
||||
:description,
|
||||
:contract, files: []
|
||||
:contract, { photos: [] }
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -4,16 +4,4 @@ class PagesController < ApplicationController
|
||||
skip_after_action :verify_authorized
|
||||
skip_after_action :verify_policy_scoped
|
||||
skip_before_action :require_login
|
||||
|
||||
layout :layout_for_page
|
||||
|
||||
private
|
||||
|
||||
def layout_for_page
|
||||
case params[:id]
|
||||
when 'nanocosmos_player'
|
||||
false
|
||||
else
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,7 +44,7 @@ class Public::BroadcastsController < Public::BaseController
|
||||
end
|
||||
|
||||
def conference_url_for(broadcast)
|
||||
broadcast.video_conference_url_override.presence || broadcast_zoom_meeting_url(broadcast.token)
|
||||
broadcast.video_conference_url_override || broadcast_zoom_meeting_url(broadcast.token)
|
||||
end
|
||||
|
||||
class MultiViewBroadcast
|
||||
|
||||
@@ -92,7 +92,7 @@ class Public::MaterialReleasesController < Public::BaseController
|
||||
params.require(:material_release).permit(person_params, guardian_params, second_guardian_params, :minor,
|
||||
:name, :address_street1, :address_street2, :address_city, :address_state, :address_zip, :address_country,
|
||||
:signature_base64,
|
||||
:locale, :contract_template, :description, files: []
|
||||
:locale, :contract_template, :description, photos: []
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
class ZoomMeetingsController < ApplicationController
|
||||
def show
|
||||
authorize broadcast = Broadcast.find(params[:broadcast_id])
|
||||
redirect_to broadcast.zoom_meeting_url
|
||||
end
|
||||
end
|
||||
17
app/helpers/broadcast_conferences_helper.rb
Normal file
17
app/helpers/broadcast_conferences_helper.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
module BroadcastConferencesHelper
|
||||
def options_for_conference_select
|
||||
[
|
||||
['Zoom', 'zoom'],
|
||||
['MS Teams', 'ms_teams']
|
||||
]
|
||||
end
|
||||
|
||||
def conference_option_name_from_key(key)
|
||||
option = options_for_conference_select.find { |option| option[1] == key }
|
||||
if option.present?
|
||||
option.first
|
||||
else
|
||||
'Unknown conference option'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -17,7 +17,6 @@ module TagsHelper
|
||||
disable_with: disabled_content,
|
||||
},
|
||||
form: {
|
||||
id: "selected_releases_form",
|
||||
data: {
|
||||
releasable_ids: [],
|
||||
},
|
||||
|
||||
@@ -7,14 +7,13 @@ class GenerateContractsZipJob < ApplicationJob
|
||||
@project = job.arguments.first
|
||||
@download = job.arguments.second
|
||||
@release_type = job.arguments.third
|
||||
@release_ids = job.arguments.fourth
|
||||
@search_query = job.arguments.fifth
|
||||
@type_filter = job.arguments[5]
|
||||
@folder_name = "#{@project.name.parameterize}_#{release_name.gsub('_', '-')}"
|
||||
@folder_name = "#{@project.name.parameterize}_#{get_release_name(@release_type).gsub('_', '-')}"
|
||||
@download.update!(name: @folder_name, status: :pending)
|
||||
end
|
||||
|
||||
def perform(project, download, release_type, release_ids, search_query, type_filter)
|
||||
def perform(project, download, release_type, release_ids)
|
||||
releases = project.public_send(get_release_name(release_type)).where(id: release_ids)
|
||||
|
||||
::ReleaseContractCollectionService.new(releases, @folder_name).build do |dir, files|
|
||||
zipfile_name = "#{dir}/#{@folder_name}.zip"
|
||||
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
|
||||
@@ -32,7 +31,7 @@ class GenerateContractsZipJob < ApplicationJob
|
||||
end
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("Failed to generate download for project (##{project.id}) with release type #{release_type}\n" + e.message)
|
||||
|
||||
|
||||
@download.failure!
|
||||
ProjectsChannel.broadcast_download_generation_update(@download, I18n.t("contract_downloads.download.failure"))
|
||||
end
|
||||
@@ -62,32 +61,7 @@ class GenerateContractsZipJob < ApplicationJob
|
||||
end
|
||||
end
|
||||
|
||||
def release_name
|
||||
@release_type.constantize.model_name.plural
|
||||
end
|
||||
|
||||
def all_releases
|
||||
@project.public_send(release_name)
|
||||
end
|
||||
|
||||
def releases
|
||||
if @release_ids.any?
|
||||
return all_releases.where(id: @release_ids)
|
||||
end
|
||||
|
||||
results = all_releases
|
||||
if all_releases.respond_to?(:complete, :incomplete)
|
||||
results = case @type_filter
|
||||
when 'complete'
|
||||
all_releases.complete
|
||||
when 'incomplete'
|
||||
all_releases.incomplete
|
||||
else
|
||||
all_releases
|
||||
end
|
||||
end
|
||||
|
||||
results = results.search(@search_query) if @search_query.present?
|
||||
results
|
||||
def get_release_name(release_type)
|
||||
release_type.constantize.model_name.plural
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,8 +15,7 @@ class AcquiredMediaRelease < ApplicationRecord
|
||||
include SecondGuardianPhotoable
|
||||
include GuardianName
|
||||
include SecondGuardianName
|
||||
include FilesFilterable
|
||||
|
||||
|
||||
class << self
|
||||
def custom_csv_exportable_headers
|
||||
%i[name files_count owner_info]
|
||||
@@ -107,4 +106,16 @@ class AcquiredMediaRelease < ApplicationRecord
|
||||
def files_count
|
||||
files.any? ? files.size : I18n.t('acquired_media_releases.acquired_media_release.no_media')
|
||||
end
|
||||
|
||||
def image_files
|
||||
files_blobs.where("content_type ILIKE ?", "%image%")
|
||||
end
|
||||
|
||||
def video_files
|
||||
files_blobs.where("content_type ILIKE ?", "%video%")
|
||||
end
|
||||
|
||||
def other_files
|
||||
files_blobs.where("NOT content_type ILIKE ANY (array[?])", ["%image%", "%video%"])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,10 +7,8 @@ class BroadcastRecording < ApplicationRecord
|
||||
|
||||
scope :visible, -> { where(hidden: false) }
|
||||
|
||||
before_save :set_title_and_description
|
||||
|
||||
def download_url
|
||||
"https://stream.mux.com/#{asset_playback_uid}/#{file_name}?download=#{name}"
|
||||
"https://stream.mux.com/#{asset_playback_uid}/#{file_name}?download=#{download_file_name}"
|
||||
end
|
||||
|
||||
def playback_url
|
||||
@@ -18,21 +16,6 @@ class BroadcastRecording < ApplicationRecord
|
||||
end
|
||||
|
||||
def download_file_name
|
||||
"#{broadcast_name}_Date_#{Time.now.in_time_zone(broadcast.shoot_location_time_zone).strftime("%Y-%m-%d")}_Time_#{Time.now.in_time_zone(broadcast.shoot_location_time_zone).strftime("%T")}".parameterize
|
||||
end
|
||||
|
||||
def toggle_star
|
||||
toggle! :starred
|
||||
end
|
||||
|
||||
def thumbnail_url(width = 300)
|
||||
"https://image.mux.com/#{asset_playback_uid}/thumbnail.jpg?width=#{width}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_title_and_description
|
||||
self.name ||= download_file_name
|
||||
self.description ||= "No description provided for this recording."
|
||||
"#{broadcast_name}_Date_#{created_at.in_time_zone(broadcast.shoot_location_time_zone).strftime("%Y-%m-%d")}_Time_#{created_at.in_time_zone(broadcast.shoot_location_time_zone).strftime("%T")}".parameterize
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,24 +1,15 @@
|
||||
module Approvable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
|
||||
included do
|
||||
include ActiveStorageSupport::SupportForBase64
|
||||
|
||||
has_one_base64_attached :approved_by_user_signature
|
||||
|
||||
# Requires signature when saving in the approval context
|
||||
with_options on: :approval do
|
||||
validates :approved_by_user_signature, attached: true
|
||||
end
|
||||
|
||||
def approve_by(user)
|
||||
return unless approved_at.nil?
|
||||
|
||||
|
||||
self.approved_by_user_name = user.full_name
|
||||
self.approved_by_user_email = user.email
|
||||
self.approved_at = BigMediaTime.time_zone_now
|
||||
self.approved_at = Time.zone.now
|
||||
end
|
||||
|
||||
|
||||
def approved?
|
||||
self.approved_at.present?
|
||||
end
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
module FilesFilterable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
def image_files
|
||||
files_blobs.where("content_type ILIKE ?", "%image%")
|
||||
end
|
||||
|
||||
def video_files
|
||||
files_blobs.where("content_type ILIKE ?", "%video%")
|
||||
end
|
||||
|
||||
def other_files
|
||||
files_blobs.where("NOT content_type ILIKE ANY (array[?])", ["%image%", "%video%"])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,7 +3,7 @@ class MaterialRelease < ApplicationRecord
|
||||
include Contractable
|
||||
include Exploitable
|
||||
include Notable
|
||||
include Photoable # This association needs to be removed after changing the API. Removing it right now will cause failure in API specs.
|
||||
include Photoable
|
||||
include Releasable
|
||||
include Searchable
|
||||
include Signable
|
||||
@@ -16,11 +16,11 @@ class MaterialRelease < ApplicationRecord
|
||||
include SecondGuardianPhotoable
|
||||
include GuardianName
|
||||
include SecondGuardianName
|
||||
include FilesFilterable
|
||||
|
||||
|
||||
class << self
|
||||
def custom_csv_exportable_headers
|
||||
%i[name owner_info files_count]
|
||||
%i[name owner_info]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -56,8 +56,6 @@ class MaterialRelease < ApplicationRecord
|
||||
%w[guardian_2_address_zip zip],
|
||||
%w[guardian_2_address_country country]
|
||||
]
|
||||
|
||||
has_many_attached :files
|
||||
|
||||
# We don't care for the argument but method WILL receive option name
|
||||
# when called from inside with_option block, hence * argument
|
||||
@@ -94,8 +92,4 @@ class MaterialRelease < ApplicationRecord
|
||||
def uses_edl?
|
||||
true
|
||||
end
|
||||
|
||||
def files_count
|
||||
files.any? ? files.size : I18n.t('material_releases.material_release.no_media')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
class BroadcastRecordingPolicy < ApplicationPolicy
|
||||
def create?
|
||||
true
|
||||
end
|
||||
|
||||
def destroy?
|
||||
if user.nil? || user.user.nil?
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
user.manager? || user.account_manager?
|
||||
end
|
||||
|
||||
def edit?
|
||||
true
|
||||
end
|
||||
|
||||
def update?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,16 +19,12 @@ class MaterialReleasePolicy < ReleasePolicy
|
||||
user.manager? || user.account_manager?
|
||||
end
|
||||
|
||||
def edit_files?
|
||||
def edit_photos?
|
||||
true
|
||||
end
|
||||
|
||||
def update_files?
|
||||
edit_files?
|
||||
end
|
||||
|
||||
def update_photos?
|
||||
edit_files?
|
||||
edit_photos?
|
||||
end
|
||||
|
||||
def tag_multiple?
|
||||
|
||||
@@ -7,7 +7,7 @@ class SerializableAcquiredMediaRelease < JSONAPI::Serializable::Resource
|
||||
:person_title, :person_company, :created_at, :updated_at, :collection_uid, :territory_old, :term_old,
|
||||
:applicable_medium_id, :applicable_medium_text, :territory_id, :territory_text, :term_id, :term_text,
|
||||
:restriction_id, :restriction_text, :categories, :description, :tag_list
|
||||
|
||||
|
||||
has_many :file_infos do
|
||||
data do
|
||||
@object.file_infos
|
||||
@@ -17,14 +17,4 @@ class SerializableAcquiredMediaRelease < JSONAPI::Serializable::Resource
|
||||
{ count: @object.file_infos.size }
|
||||
end
|
||||
end
|
||||
|
||||
has_many :files do
|
||||
data do
|
||||
@object.files
|
||||
end
|
||||
|
||||
meta do
|
||||
{ count: @object.files.size }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
class SerializableUser < JSONAPI::Serializable::Resource
|
||||
type "user"
|
||||
|
||||
attributes :email, :full_name
|
||||
|
||||
attribute :company_name do
|
||||
@object.primary_account.name
|
||||
end
|
||||
attributes :email
|
||||
end
|
||||
|
||||
@@ -24,7 +24,7 @@ class ReleaseContractCollectionService
|
||||
end
|
||||
|
||||
files = Dir.entries("#{dir}/").select { |f| !File.directory? f }
|
||||
# raise StandardError.new "Contracts or Contract Templates not found." unless files.any?
|
||||
raise StandardError.new "Contracts or Contract Templates not found." unless files.any?
|
||||
yield(dir, files)
|
||||
}
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<%= form.email_field :email, class: "form-group" %>
|
||||
<%= form.password_field :password %>
|
||||
<%= form.text_field :account_name, label: 'Account Name' %>
|
||||
<%= form.select :interested_product_name, options_for_select(["I'm interested in all products", "ME Suite PRO", "DirectME", "ReleaseME", "CastME", "EditME", "DeliverME", "ExpenseME"]), { label: "What product are you most interested in?" }, { class: "form-control custom-select" } %>
|
||||
<%= form.select :interested_product_name, options_for_select(["I'm interested in all products", "DirectME", "ReleaseME", "CastME", "EditME", "DeliverME", "ExpenseME"]), { label: "What product are you most interested in?" }, { class: "form-control custom-select" } %>
|
||||
<div class="pt-3">
|
||||
<%= form.submit "Start Free Trial", class: "btn btn-block btn-danger font-weight-bold" %>
|
||||
</div>
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
<h3>Welcome To <%= suite_wordmark("d-inline-block") %></h3>
|
||||
<div class="mt-4">
|
||||
<p>Sign up for a <strong>14 Day Free Trial</strong> which includes full access to the following products. No credit card required!</p>
|
||||
<div class="d-flex justify-content-between pb-2">
|
||||
<div><%= image_tag "ME_PRO_black.png", width: "96.66%" %></div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between pb-2">
|
||||
<div><%= image_tag "logo_directme.png", width: "90%" %></div>
|
||||
<div><%= image_tag "logo_releaseme.png", width: "90%" %></div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<input id="total_entries" type=hidden value=<%= @appearance_releases.total_entries %> />
|
||||
<div id="upload-progress-container" class="mb-1"></div>
|
||||
<div class="d-md-flex d-sm-flex flex-sm-column flex-md-row flex-md-wrap mb-2">
|
||||
<% if policy(AppearanceRelease).new? %>
|
||||
@@ -17,7 +16,7 @@
|
||||
<% end %>
|
||||
|
||||
<% if @appearance_releases.any? && policy(AppearanceRelease).download_multiple? %>
|
||||
<%= button_to "Download", [@project, :contract_downloads, release_type: @appearance_releases.name], id: "download_releases", method: :post, remote: true, class: "btn btn-light border mr-2 mb-2", data: { disable_with: "Please wait..." } %>
|
||||
<%= link_to "Download All", [@project, :contract_downloads, release_type: @appearance_releases.name], method: :post, remote: true, class: "btn btn-light border mr-2 mb-2", data: { disable_with: "Please wait..." } %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,5 +3,3 @@ $("form input[type='search']").val("<%= params[:query] %>");
|
||||
$("#type_filter_actions").html("<%= j render 'type_filter_actions' %>");
|
||||
$("#appearance_releases_pagination").html("<%= j will_paginate(@appearance_releases) %>");
|
||||
$('#type_filter_value').val("<%= params[:type_filter] %>");
|
||||
$("#selected_releases_form").attr('data-releasable-ids', JSON.stringify([]));
|
||||
$("#total_entries").val(<%= @appearance_releases.total_entries %>);
|
||||
@@ -1,13 +1,9 @@
|
||||
<div class="card shadow-sm">
|
||||
<%= card_header text: t(".heading", release_type: @releasable_param.name.titleize), close_action_path: [@project, "#{@releasable_param.name.pluralize}"] %>
|
||||
<div class="card-body">
|
||||
<embed class="embeded-contract-preview mb-3" type="application/pdf" src="<%= url_for [@releasable, :contracts, format: "pdf"] %>" width="90%" height="1200" />
|
||||
<embed class="embeded-contract-preview" type="application/pdf" src="<%= url_for [@releasable, :contracts, format: "pdf"] %>" width="90%" height="1200" />
|
||||
|
||||
<%= errors_summary_for @releasable %>
|
||||
<%= bootstrap_form_with model: @releasable, method: :post, url: public_send("#{@releasable_param.name}_approvals_path", @releasable), local: true do |form| %>
|
||||
<%= card_field_set_tag 'Signature' do %>
|
||||
<%= render "shared/signature_fields", form: form, signature_field: 'approved_by_user_signature[data]' %>
|
||||
<% end %>
|
||||
<div class="row align-items-center text-center mt-4">
|
||||
<%= link_to t("shared.cancel"), [@releasable.project, "#{@releasable_param.name.pluralize}"], class: "col-3 text-reset" %>
|
||||
<div class="col-9">
|
||||
@@ -16,4 +12,4 @@
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1 +0,0 @@
|
||||
$("#broadcast_recordings").html("<%= j render(partial: 'broadcasts/broadcast_recordings', locals: { recordings: @recordings, broadcast: @broadcast }) %>");
|
||||
@@ -1,24 +0,0 @@
|
||||
<%= content_tag :div, class: "modal modal-right", id: "edit_broadcast_recording_modal", aria: { labelledby: "modalLabel", hidden: true }, role: "dialog", tabindex: -1 do %>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modalLabel">Edit Broadcast Recording</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<%= bootstrap_form_with model: [broadcast.project, broadcast, recording], layout: :horizontal, label_col: "col-3", control_col: "col-9" do |form| %>
|
||||
<div class="modal-body">
|
||||
<div id="broadcast_recording_fields">
|
||||
<%= form.text_field :name %>
|
||||
<%= form.text_area :description %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<%= form.button "Close", class: "btn btn-secondary", data: { dismiss: "modal" } %>
|
||||
<%= form.submit "Update Broadcast Recording", class: "btn btn-primary", data: { disable_with: t("shared.disable_with") } %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -1,11 +0,0 @@
|
||||
$('[data-id="<%= dom_id(@recording) %>"]').remove();
|
||||
<% if @recordings.empty? %>
|
||||
$("#broadcast_recordings_nav").append('<p class="dropdown-item text-muted">Recordings will appear here</p>')
|
||||
<% end %>
|
||||
$("#broadcast_recordings").html("<%= j render(partial: 'broadcasts/broadcast_recordings', locals: { recordings: @recordings, broadcast: @broadcast }) %>");
|
||||
|
||||
// Close and remove the modal
|
||||
$("#edit_broadcast_recording_modal").on("hidden.bs.modal", function (e) {
|
||||
$("#edit_broadcast_recording_modal").remove();
|
||||
});
|
||||
$("#edit_broadcast_recording_modal").modal("hide");
|
||||
@@ -1 +1,6 @@
|
||||
<%= render("broadcast_recordings/refresh_recordings_list") %>
|
||||
var dom_id = "<%= dom_id(@recording) %>"
|
||||
$('[data-id="' + dom_id + '"]').remove();
|
||||
<% if @recordings.empty? %>
|
||||
$("#broadcast_recordings_nav").append('<p class="dropdown-item text-muted">Recordings will appear here</p>')
|
||||
<% end %>
|
||||
$("#broadcast_recordings").html("<%= j render(partial: 'broadcasts/broadcast_recordings', locals: { recordings: @recordings, broadcast: @broadcast }) %>");
|
||||
@@ -1,6 +0,0 @@
|
||||
<% # Remove the modal if it already exists %>
|
||||
$("#edit_broadcast_recording_modal").remove();
|
||||
|
||||
<% # Create and show the modal %>
|
||||
$("body").append("<%= j render(partial: 'edit_broadcast_recording_modal', locals: { project: @project, broadcast: @broadcast, recording: @recording }) %>");
|
||||
$("#edit_broadcast_recording_modal").modal("toggle");
|
||||
@@ -1 +0,0 @@
|
||||
<%= render("broadcast_recordings/refresh_recordings_list") %>
|
||||
@@ -1 +1,3 @@
|
||||
<%= link_to broadcast_recording.name, "javascript:void(0);", class: "dropdown-item", data: { behavior: "play_recording", playback_url: broadcast_recording.playback_url, id: dom_id(broadcast_recording) } %>
|
||||
<%= link_to broadcast_recording.download_file_name, "javascript:void(0);", class: "dropdown-item", data: { behavior: "play_recording", playback_url: broadcast_recording.playback_url, id: dom_id(broadcast_recording) } %>
|
||||
|
||||
|
||||
|
||||
@@ -1,29 +1,18 @@
|
||||
<% if recordings.present? %>
|
||||
<div class="list-group">
|
||||
<p>Click below to download the recordings of the live stream.</p>
|
||||
<ul class="mt-2">
|
||||
<% recordings.each do |recording| %>
|
||||
<div class="list-group-item list-group-item-action">
|
||||
<div class="d-flex align-items-start">
|
||||
<% if policy(BroadcastRecording).update? %>
|
||||
<%= link_to fa_icon("#{recording.starred ? 'star' : 'star-o'} fw"), [broadcast.project, broadcast, recording, :broadcast_recording_starrings], method: :post, class: "text-warning mr-3", remote: true %>
|
||||
<% end %>
|
||||
<%= image_tag(recording.thumbnail_url, class: 'img-thumbnail img-fluid max-w-75', data: { behavior: "play_recording", playback_url: recording.playback_url, id: dom_id(recording) }) %>
|
||||
<div class="ml-auto">
|
||||
<% if policy(BroadcastRecording).edit? %>
|
||||
<%= link_to fa_icon('edit'), [:edit, broadcast.project, broadcast, recording], remote: true %>
|
||||
<% end %>
|
||||
<%= link_to(fa_icon('download'), recording.download_url, target: "_blank") %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-column align-items-start justify-content-start p-4">
|
||||
<h5><%= recording.name %></h5>
|
||||
<p><%= recording.description %></p>
|
||||
</div>
|
||||
</div>
|
||||
<li>
|
||||
<%= link_to(recording.download_file_name, recording.download_url, target: "_blank") %>
|
||||
<% if (controller.class.module_parent.to_s != "Public" && policy(BroadcastRecording).destroy?) %>
|
||||
<%= link_to "Hide", [broadcast.project, broadcast, recording], class: "btn-sm btn-primary ml-1 text-decoration-none", remote: true, method: :delete, data: { confirm: t('.confirm_hide') } %>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
</div>
|
||||
</ul>
|
||||
<div id="recordings_pagination" class="row mt-5 justify-content-center">
|
||||
<%= will_paginate(recordings, params: {controller: "broadcasts", action: "show", project_id: broadcast.project_id, id: broadcast.id, page: params[:page], active_tab: 'recordings'}) %>
|
||||
</div>
|
||||
<% else %>
|
||||
<p class="mt-4">Recording of the live stream will appear here.</p>
|
||||
<p>Recording of the live stream will appear here.</p>
|
||||
<% end %>
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
<% if broadcast.streamer_connected? || (broadcast.streamer_recording? && !broadcast.active?) %>
|
||||
<p class="mb-1">Live stream has connected successfully and will be available soon.</p>
|
||||
<div class="badge badge-pill badge-success mb-2 text-uppercase">Connected</div>
|
||||
<div class="alert alert-info text-center text-md-left">
|
||||
<%= fa_icon "info-circle" %>
|
||||
<strong>Live stream has connected successfully and will be available soon.</strong>
|
||||
</div>
|
||||
<% elsif broadcast.streamer_recording? && broadcast.active? %>
|
||||
<p class="mb-1">Live stream has begun, click play to watch it.</p>
|
||||
<div class="badge badge-pill badge-danger mb-2 text-uppercase">Live</div>
|
||||
<div class="alert alert-success text-center text-md-left">
|
||||
<%= fa_icon "success" %>
|
||||
<strong>Live stream has begun, click play to watch it.</strong>
|
||||
</div>
|
||||
<% elsif broadcast.streamer_disconnected? %>
|
||||
<p class="mb-1">Live stream got disconnected.</p>
|
||||
<div class="badge badge-pill badge-warning mb-2 text-uppercase">Disconnected</div>
|
||||
<div class="alert alert-warning text-center text-md-left">
|
||||
<%= fa_icon "warning" %>
|
||||
<strong>Live stream got disconnected.</strong>
|
||||
</div>
|
||||
<% elsif (broadcast.idle? && broadcast.streamer_idle?) || (broadcast.created? && broadcast.streamer_idle?) %>
|
||||
<p class="mb-1">Live stream is waiting to begin.</p>
|
||||
<div class="badge badge-pill badge-primary mb-2 text-uppercase">Idle</div>
|
||||
<% end %>
|
||||
<div class="alert alert-info text-center text-md-left">
|
||||
<%= fa_icon "info-circle" %>
|
||||
<strong>Live stream is waiting to begin.</strong>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<%= bootstrap_form_with model: model, local: true do |form| %>
|
||||
<%= form.text_field :name %>
|
||||
<%= form.select :conference_option, options_for_conference_select, { label: t('.labels.conference_option') }, class: "form-control custom-select" %>
|
||||
<%= form.time_zone_select(:shoot_location_time_zone, nil, label: "Time zone of shoot location") %>
|
||||
|
||||
<div class="row align-items-center text-center mt-4">
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<div class="list-group">
|
||||
<div class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between mb-1">
|
||||
<h5 class="mb-1"><%= broadcast.name %></h5>
|
||||
<small>Created - <%= time_ago_in_words(broadcast.created_at) + " ago" %></small>
|
||||
</div>
|
||||
<div id="broadcast_updates">
|
||||
<%= render partial: 'broadcasts/broadcast_status', locals: { broadcast: broadcast } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<table class="w-100 h-100 bg-secondary">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center align-middle text-white bg-black">
|
||||
<td class="text-center align-middle text-white">
|
||||
Video player will appear here when the stream becomes available.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -14,155 +14,159 @@
|
||||
<% end %>
|
||||
|
||||
<% content_for :header do %>
|
||||
<header></header>
|
||||
<header class="container-fluid py-3 border-bottom sticky-top bg-light">
|
||||
<div class="row align-items-center justify-content-center">
|
||||
<div class="col-4 text-center">
|
||||
<%= product_wordmark(:direct_me, class: 'navbar-brand') %>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<% end %>
|
||||
|
||||
<div class="row no-gutters m-n3">
|
||||
<div class="col-lg-8 col-md-12 bg-black">
|
||||
<div class="d-flex justify-content-start align-items-center flex-row p-3 mb-5 bg-dark">
|
||||
<%= product_wordmark(:direct_me, class: 'navbar-brand text-white') %>
|
||||
<div class="ml-4 dropdown">
|
||||
<% if @multi_view_broadcasts.present? %>
|
||||
<%= link_to "#", class: "btn btn-light dropdown-toggle text-white bg-black border-0 override-dropdown-show-state", role: "button", id: "dropdownMenuLink", data: { toggle: "dropdown" }, aria: { haspopup: "true", expanded: "false" } do %>
|
||||
<i class="fa fa-video-camera text-primary" aria-hidden="true"></i><span class="ml-2" id="selected_stream"><%= @broadcast.name %></span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= link_to "javascript:void(0);", class: "btn btn-light text-white bg-black border-0 override-dropdown-show-state", role: "button", id: "dropdownMenuLink", aria: { haspopup: "true", expanded: "false" } do %>
|
||||
<i class="fa fa-video-camera text-primary" aria-hidden="true"></i><span class="ml-2" id="selected_stream"><%= @broadcast.name %></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div class="dropdown-menu bg-black" aria-labelledby="dropdownMenuLink">
|
||||
<%= link_to fa_icon("check", text: @broadcast.name.titleize), "#", data: { behavior: "play_stream"}, class: "dropdown-item active" %>
|
||||
<% @multi_view_broadcasts.each do |broadcast| %>
|
||||
<% if broadcast.id != @broadcast.id %>
|
||||
<% if params[:director_mode] %>
|
||||
<% if controller.class.module_parent.to_s == "Public" %>
|
||||
<%= link_to broadcast.name.titleize, url_for(params.permit!.merge(director_mode: true, token: broadcast.token)), data: { behavior: "play_stream"}, class: class_string("dropdown-item", "active" => @broadcast.id == broadcast.id) %>
|
||||
<% else %>
|
||||
<%= link_to broadcast.name.titleize, url_for(params.permit!.merge(director_mode: true, id: broadcast.id)), data: { behavior: "play_stream"}, class: class_string("dropdown-item", "active" => @broadcast.id == broadcast.id) %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<% if controller.class.module_parent.to_s == "Public" %>
|
||||
<%= link_to broadcast.name.titleize, url_for(params.permit!.merge(token: broadcast.token).except(:director_mode)), data: { behavior: "play_stream"}, class: class_string("dropdown-item", "active" => @broadcast.id == broadcast.id) %>
|
||||
<% else %>
|
||||
<%= link_to broadcast.name.titleize, url_for(params.permit!.merge(id: broadcast.id).except(:director_mode)), data: { behavior: "play_stream"}, class: class_string("dropdown-item", "active" => @broadcast.id == broadcast.id) %>
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-md-12 mb-3">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h1 class="h3 m-0"><%= @broadcast.name %></h1>
|
||||
<div class="dropdown">
|
||||
<%= link_to "Switch View", "#", class: "btn btn-light border dropdown-toggle", role: "button", id: "dropdownMenuLink", data: { toggle: "dropdown" }, aria: { haspopup: "true", expanded: "false" } %>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
||||
<h5 class="dropdown-header">Live Streams</h5>
|
||||
<%= link_to fa_icon("check", text: @broadcast.name.titleize), "#", data: { behavior: "play_stream"}, class: "dropdown-item active" %>
|
||||
<% @multi_view_broadcasts.each do |broadcast| %>
|
||||
<% if broadcast.id != @broadcast.id %>
|
||||
<%= link_to broadcast.name.titleize, broadcast.url, data: { behavior: "play_stream"}, class: class_string("dropdown-item", "active" => @broadcast.id == broadcast.id) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% if @broadcast.director_mode_video_embed.present? %>
|
||||
<h5 class="dropdown-header">Director Mode</h5>
|
||||
<% unless params[:director_mode] %>
|
||||
<%= link_to "Enable Director Mode", url_for(params.permit!.merge(director_mode: true)), class: "dropdown-item" %>
|
||||
<% else %>
|
||||
<%= link_to "Disable Director Mode", url_for(params.permit!.except(:director_mode)), class: "dropdown-item" %>
|
||||
|
||||
<% end %>
|
||||
<% end %>
|
||||
<h5 class="dropdown-header">Previous Sessions</h5>
|
||||
<div id="broadcast_recordings_nav">
|
||||
<% if @recordings.any? %>
|
||||
<%= render partial: "broadcasts/broadcast_recording_nav", collection: @recordings, as: :broadcast_recording %>
|
||||
<% else %>
|
||||
<p class="dropdown-item text-muted">Recordings will appear here</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="embed-responsive embed-responsive-16by9">
|
||||
<%= render partial: 'broadcasts/video', locals: { broadcast: @broadcast } %>
|
||||
<% if @broadcast.streamer_recording? && @broadcast.active? %>
|
||||
<%= javascript_tag nonce: true do %>
|
||||
new Clappr.Player({
|
||||
parentId: '#broadcast_video',
|
||||
source: "<%= @broadcast.stream_playback_url %>",
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
mute: true,
|
||||
autoPlay: true,
|
||||
hlsMinimumDvrSize: 1
|
||||
});
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% if @broadcast.director_mode_video_embed.present? %>
|
||||
<% if params[:director_mode] %>
|
||||
<div class="custom-control custom-switch ml-auto">
|
||||
<input type="checkbox" name="director_mode" value="true" class="custom-control-input" id="director_mode_switch" checked="checked" />
|
||||
<label class="custom-control-label text-white override-custom-control-label" for="director_mode_switch">Director Mode</label>
|
||||
</div>
|
||||
<%= link_to "Disable Director Mode", url_for(params.permit!.except(:director_mode)), class: "d-none", id: "director_mode_link" %>
|
||||
<% else %>
|
||||
<div class="custom-control custom-switch ml-auto">
|
||||
<input type="checkbox" name="director_mode" value="true" class="custom-control-input" id="director_mode_switch" />
|
||||
<label class="custom-control-label text-white override-custom-control-label" for="director_mode_switch">Director Mode</label>
|
||||
</div>
|
||||
<%= link_to "Enable Director Mode", url_for(params.permit!.merge(director_mode: true)), class: "d-none", id: "director_mode_link" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="embed-responsive embed-responsive-16by9" id="video_content">
|
||||
<%= render partial: 'broadcasts/video', locals: { broadcast: @broadcast } %>
|
||||
<% if @broadcast.streamer_recording? && @broadcast.active? %>
|
||||
<%= javascript_tag nonce: true do %>
|
||||
new Clappr.Player({
|
||||
parentId: '#broadcast_video',
|
||||
source: "<%= @broadcast.full_live_stream_playback_url %>",
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
mute: true,
|
||||
autoPlay: true,
|
||||
hlsMinimumDvrSize: 1
|
||||
});
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-12 bg-white p-3 min-vh-100 overflow-auto">
|
||||
<% unless controller.class.module_parent.to_s == "Public" %>
|
||||
<% if @multi_view_broadcasts.present? %>
|
||||
<% tokens = @multi_view_broadcasts.map(&:token) %>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" id="broadcast_share_url" data-behavior="clipboard" href="<%= broadcast_url(@broadcast.token, multi_view_tokens: tokens) %>">Share URL</button>
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<%= link_to t('.actions.reset_url'), [@project, @broadcast], method: :patch, class: "dropdown-item" %>
|
||||
<div class="col-lg-4 col-md-12 mb-3">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header">
|
||||
<ul class="nav nav-tabs card-header-tabs">
|
||||
<li class="nav-item">
|
||||
<%= link_to "Home", "#home", class: class_string("nav-link", "active" => !params[:active_tab].present?), data: { toggle: "tab" } %>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<%= link_to "Previous Sessions", "#recordings", class: class_string("nav-link", "active" => params[:active_tab] == "recordings"), data: { toggle: "tab" } %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
<div class="tab-content">
|
||||
<div class="<%= class_string("tab-pane fade show", "active" => !params[:active_tab].present?) %>" id="home">
|
||||
<div id="broadcast_updates">
|
||||
<%= render partial: 'broadcasts/broadcast_status', locals: { broadcast: @broadcast } %>
|
||||
</div>
|
||||
<% unless controller.class.module_parent.to_s == "Public" %>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label for="broadcast_share_url">To share the stream, copy the URL below. Anyone with the link can view the stream.</label>
|
||||
<div class="input-group">
|
||||
<% if @multi_view_broadcasts.present? %>
|
||||
<% tokens = @multi_view_broadcasts.map(&:token) %>
|
||||
<input type="text" class="form-control" value="<%= broadcast_url(@broadcast.token, multi_view_tokens: tokens) %>" readonly>
|
||||
<div class="input-group-append">
|
||||
<button type="button" id="broadcast_share_url" class="btn btn-success" data-behavior="clipboard" href="<%= broadcast_url(@broadcast.token, multi_view_tokens: tokens) %>">
|
||||
<i class="fa fa-clipboard"></i>
|
||||
Copy URL
|
||||
</button>
|
||||
<%= link_to t('.actions.reset_url'), [@project, @broadcast], method: :patch, class: "btn btn-danger" %>
|
||||
</div>
|
||||
<% else %>
|
||||
<input type="text" class="form-control" value="<%= broadcast_url(@broadcast.token) %>" readonly>
|
||||
<div class="input-group-append">
|
||||
<button type="button" id="broadcast_share_url" class="btn btn-success" data-behavior="clipboard" href="<%= broadcast_url(@broadcast.token) %>">
|
||||
<i class="fa fa-clipboard"></i>
|
||||
Copy URL
|
||||
</button>
|
||||
<%= link_to t('.actions.reset_url'), [@project, @broadcast], method: :patch, class: "btn btn-danger" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<% end %>
|
||||
<p class="card-text"><%= "If you want to join the #{conference_option_name_from_key(@broadcast.conference_option)} meeting dedicated to this broadcast, follow the link below." %></p>
|
||||
<%= link_to 'Video Conference', @conference_url, class: "btn btn-primary btn-block", target: '_blank' %>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" id="broadcast_share_url" data-behavior="clipboard" href="<%= broadcast_url(@broadcast.token) %>">Share URL</button>
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<%= link_to t('.actions.reset_url'), [@project, @broadcast], method: :patch, class: "dropdown-item" %>
|
||||
<div class="<%= class_string("tab-pane fade show", "active" => params[:active_tab] == 'recordings') %>" id="recordings">
|
||||
<div id="broadcast_recordings">
|
||||
<%= render partial: 'broadcasts/broadcast_recordings', locals: { recordings: @recordings, broadcast: @broadcast } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= link_to 'Video Conference', @conference_url, class: 'btn btn-primary', target: '_blank' %>
|
||||
<hr/>
|
||||
<ul class="nav nav-tabs override-nav-tabs mb-3">
|
||||
<li class="nav-item">
|
||||
<%= link_to "Takes", "#recordings", class: class_string("nav-link", "active" => !params[:active_tab].present?), data: { toggle: "tab" } %>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<%= link_to "Files", "#files", class: class_string("nav-link", "active" => params[:active_tab] == "files"), data: { toggle: "tab" } %>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="<%= class_string("tab-pane fade show", "active" => !params[:active_tab].present?) %>" id="recordings">
|
||||
<div id="live_take">
|
||||
<%= render partial: 'broadcasts/live_take', locals: { broadcast: @broadcast } %>
|
||||
</div>
|
||||
<% if params[:director_mode] %>
|
||||
<% if controller.class.module_parent.to_s == "Public" %>
|
||||
<%= link_to "Play #{@broadcast.name.titleize}", url_for(params.permit!.merge(director_mode: true, token: @broadcast.token)), data: { behavior: "play_stream"}, class: "mt-2 btn btn-primary" %>
|
||||
<% else %>
|
||||
<%= link_to "Play #{@broadcast.name.titleize}", url_for(params.permit!.merge(director_mode: true, id: @broadcast.id)), data: { behavior: "play_stream"}, class: "mt-2 btn btn-primary" %>
|
||||
<% end %>
|
||||
<hr/>
|
||||
<% else %>
|
||||
<% if controller.class.module_parent.to_s == "Public" %>
|
||||
<%= link_to "Play #{@broadcast.name.titleize}", url_for(params.permit!.merge(token: @broadcast.token).except(:director_mode)), data: { behavior: "play_stream"}, class: "mt-2 btn btn-primary" %>
|
||||
<% else %>
|
||||
<%= link_to "Play #{@broadcast.name.titleize}", url_for(params.permit!.merge(id: @broadcast.id).except(:director_mode)), data: { behavior: "play_stream"}, class: "mt-2 btn btn-primary" %>
|
||||
<% end %>
|
||||
<hr/>
|
||||
<% end %>
|
||||
<div id="broadcast_recordings">
|
||||
<%= render partial: 'broadcasts/broadcast_recordings', locals: { recordings: @recordings, broadcast: @broadcast } %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="<%= class_string("tab-pane fade show", "active" => params[:active_tab] == "files") %>" id="files">
|
||||
<% if @multi_view_broadcasts.present? %>
|
||||
<ul class="nav nav-tabs">
|
||||
</div>
|
||||
|
||||
<!-- files section -->
|
||||
<div id="files" class="card shadow-sm mb-3">
|
||||
<div class="card-header">
|
||||
<h2 class="h5">Files</h2>
|
||||
<% if @multi_view_broadcasts %>
|
||||
<ul class="nav nav-tabs card-header-tabs">
|
||||
<% @multi_view_broadcasts.each_with_index do |mvb, index| %>
|
||||
<li class="nav-item">
|
||||
<%= link_to mvb.name, "#files_broadcast_#{mvb.token}", class: class_string("nav-link", "active" => (params[:active_files_tab] == mvb.token || (params[:active_files_tab].nil? && index == 0))), data: { toggle: "tab" } %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<div class="tab-content pt-4">
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
<div class="tab-content">
|
||||
<% if @multi_view_broadcasts.present? %>
|
||||
<% @multi_view_broadcasts.each_with_index do |mvb, index| %>
|
||||
<div class="<%= class_string("tab-pane fade show", "active" => (params[:active_files_tab] == mvb.token || (params[:active_files_tab].nil? && index == 0))) %>" id="files_broadcast_<%= mvb.token %>">
|
||||
<%= render partial: 'broadcasts/files_section', locals: { broadcast: mvb, files: mvb.files } %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= render partial: 'broadcasts/files_section', locals: { broadcast: @broadcast, files: @files } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="tab-pane fade show active" id="files_broadcast_<%= @broadcast.id %>">
|
||||
<%= render partial: 'broadcasts/files_section', locals: { broadcast: @broadcast, files: @files } %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,10 +10,4 @@
|
||||
<%= description_list_pair t('.description_labels.issued_to'), releasable.name %>
|
||||
<%= description_list_pair t('.description_labels.issued_by'), releasable.approved_by_user_name %>
|
||||
<%= description_list_pair t('.description_labels.date_issued'), releasable.approved_at %>
|
||||
</dl>
|
||||
|
||||
<% if preview %>
|
||||
<%= image_tag dummy_signature %>
|
||||
<% elsif releasable.approved_by_user_signature.attached? %>
|
||||
<%= image_tag releasable.approved_by_user_signature.variant(auto_orient: true, resize: "200x200") %>
|
||||
<% end %>
|
||||
</dl>
|
||||
@@ -4,34 +4,23 @@
|
||||
</div>
|
||||
<hr>
|
||||
<% end %>
|
||||
<% if preview %>
|
||||
<h1>PREVIEW ONLY</h1>
|
||||
<% end %>
|
||||
<% if contract_template.body.present? %>
|
||||
<%= contract_template.body %>
|
||||
<br/>
|
||||
<% end %>
|
||||
<% if releasable.minor? && contract_template.guardian_clause.present? %>
|
||||
<p class="text-left"><strong>Guardian Clause</strong></p>
|
||||
<%= contract_template.guardian_clause %>
|
||||
<% end %>
|
||||
|
||||
<div class="page">
|
||||
<% if preview %>
|
||||
<h1>PREVIEW ONLY</h1>
|
||||
<% end %>
|
||||
|
||||
<% if releasable.model_name == "AppearanceRelease" && releasable.person_photo.attached? %>
|
||||
<div class="embed-person-photo">
|
||||
<%= image_tag releasable.photos.first.variant(auto_orient: true, resize: "200x200"), id: "top-person-photo" %>
|
||||
</div>
|
||||
<hr>
|
||||
<% end %>
|
||||
|
||||
<% if contract_template.body.present? %>
|
||||
<%= contract_template.body %>
|
||||
<br/>
|
||||
<% end %>
|
||||
|
||||
<% if releasable.minor? && contract_template.guardian_clause.present? %>
|
||||
<p class="text-left"><strong>Guardian Clause</strong></p>
|
||||
<%= contract_template.guardian_clause %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if contract_template.present? && contract_template.has_questionnaire? %>
|
||||
<%# if releasable.model_name.in? %w(MedicalRelease MiscRelease AppearanceRelease) %>
|
||||
<% if releasable.respond_to?(:question_1_answer) %>
|
||||
<div class="page">
|
||||
<%= render "contracts/questionnaire", releasable: releasable, contract_template: contract_template, preview: preview %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="page">
|
||||
@@ -58,13 +47,14 @@
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if releasable.try(:approved?) %>
|
||||
|
||||
<% if releasable.respond_to?(:approved?) && releasable.approved? %>
|
||||
<div class="page">
|
||||
<%= render "contracts/for_office_use_only", releasable: releasable, preview: preview %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if releasable.respond_to?(:image_files) %>
|
||||
<% if releasable.class == AcquiredMediaRelease %>
|
||||
<div class="page">
|
||||
<%= render "contracts/files", release: releasable, preview: preview %>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="<%= I18n.locale %>">
|
||||
<head>
|
||||
<title>ME Suite</title>
|
||||
<title>MESuite.ai App</title>
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="<%= I18n.locale %>">
|
||||
<head>
|
||||
<title>ME Suite</title>
|
||||
<title>MESuite.ai App</title>
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
|
||||
|
||||
@@ -57,8 +57,8 @@
|
||||
|
||||
<hr>
|
||||
|
||||
<%= field_set_tag content_tag(:span, t(".files.heading"), class: "h6 text-muted text-uppercase") do %>
|
||||
<%= render "shared/releasable_files_dropzone", form: form, releasable: material_release %>
|
||||
<%= field_set_tag content_tag(:span, t(".photos.heading"), class: "h6 text-muted text-uppercase") do %>
|
||||
<%= render "shared/photos_dropzone_fields", form: form, release: material_release %>
|
||||
|
||||
<div class="<%= class_string("collapse" => !material_release.minor?) %>" data-ujs-target="guardian-fields">
|
||||
<br>
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<% if material_release.files.any? %>
|
||||
<%= material_release.files.size %>
|
||||
<% if material_release.photo.attached? %>
|
||||
<%= image_tag medium_variant(material_release.photo), class: "img-fluid" %>
|
||||
<% else %>
|
||||
<%= fa_icon("warning", text: t(".no_media"), class: "text-danger") %>
|
||||
<%= fa_icon("warning", text: t(".no_photos"), class: "text-danger") %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
@@ -41,8 +41,8 @@
|
||||
<% if policy(material_release.tags).new? %>
|
||||
<%= link_to fa_icon("tags fw", text: "Tags"), [:new, material_release, :acts_as_taggable_on_tag], class: "dropdown-item", remote: true %>
|
||||
<% end %>
|
||||
<% if policy(material_release).edit_files? %>
|
||||
<%= link_to fa_icon("file-o fw", text: "Add Media"), [:edit, material_release, :files], class: "dropdown-item" %>
|
||||
<% if policy(material_release).edit_photos? %>
|
||||
<%= link_to fa_icon("picture-o fw", text: "Photos"), [:edit, material_release, :photos], class: "dropdown-item" %>
|
||||
<% end %>
|
||||
<% if policy(Contract).show? && (material_release.contract.attached? || material_release.contract_template.present?) %>
|
||||
<%= link_to fa_icon("download fw", text: "Download"), [material_release, :contracts, format: "pdf"], class: "dropdown-item", target: "_blank" %>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<tr>
|
||||
<th data-behavior="all-selectable"><%= check_box_tag "material_release_ids[]", false, false %></th>
|
||||
<th><%= t '.table_headers.approved'%></th>
|
||||
<th><%= t(".table_headers.files_count") %></th>
|
||||
<th></th>
|
||||
<th><%= MaterialRelease.human_attribute_name(:name) %></th>
|
||||
<th><%= t(".table_headers.owner_info") %>
|
||||
<th><%= t(".table_headers.notes") %></th>
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<div>
|
||||
<div id="playerDiv"></div>
|
||||
<script type="text/javascript" src="https://demo.nanocosmos.de/nanoplayer/api/release/nanoplayer.4.min.js"></script>
|
||||
<%= javascript_tag nonce: true do %>
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
var streamName = urlParams.get('stream_name');
|
||||
console.log("streamName", streamName);
|
||||
var player;
|
||||
var config = {
|
||||
"source": {
|
||||
"entries": [
|
||||
{
|
||||
"h5live": {
|
||||
// your rtmp stream
|
||||
"rtmp": {
|
||||
"url": "rtmp://bintu-play.nanocosmos.de/play",
|
||||
"streamname": streamName,
|
||||
},
|
||||
"server": {
|
||||
"websocket": "wss://bintu-h5live.nanocosmos.de:443/h5live/stream.mp4",
|
||||
"hls": "https://bintu-h5live.nanocosmos.de:443/h5live/http/playlist.m3u8",
|
||||
"progressive": "https://bintu-h5live.nanocosmos.de:443/h5live/http/stream.mp4"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"style": {
|
||||
"width": "auto",
|
||||
}
|
||||
};
|
||||
function initPlayer() {
|
||||
player = new NanoPlayer('playerDiv');
|
||||
player.setup(config).then(function (config) {
|
||||
console.log('setup ok with config: ' + JSON.stringify(config));
|
||||
}, function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
// load player from playerDiv
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
initPlayer();
|
||||
});
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -17,6 +17,7 @@
|
||||
<%= @user.role_for(Current.account).to_s.titleize %>
|
||||
<% end %>
|
||||
</p>
|
||||
<%= link_to 'Auth to Microsoft', '/auth/azure_ad', method: :post, class: "btn btn-primary" %>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
<%= render "shared/address_fields", form: form, subject: "person", required: true %>
|
||||
<% end %>
|
||||
|
||||
<%= card_field_set_tag t(".files.heading") do %>
|
||||
<%= render "shared/releasable_files_dropzone", form: form, releasable: @material_release %>
|
||||
<%= card_field_set_tag t(".photo.heading") do %>
|
||||
<%= render "shared/photos_dropzone_fields", form: form, release: @material_release %>
|
||||
<% end %>
|
||||
|
||||
<hr>
|
||||
|
||||
@@ -9,12 +9,12 @@ Rails.application.config.content_security_policy do |policy|
|
||||
policy.font_src :self, :https, :data
|
||||
policy.img_src :self, :https, :data
|
||||
policy.object_src :self
|
||||
policy.script_src :self, :https, AppHost.new.domain_with_port, "https://stream.mux.com", "https://demo.nanocosmos.de", :blob, :unsafe_eval
|
||||
policy.script_src :self, :https, AppHost.new.domain_with_port, "https://stream.mux.com", :blob, :unsafe_eval
|
||||
policy.media_src :self, :https, AppHost.new.domain_with_port, "https://stream.mux.com", :data, :blob
|
||||
# policy.style_src :self, :https, :unsafe_inline
|
||||
# If you are using webpack-dev-server then specify webpack-dev-server host
|
||||
# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
|
||||
policy.connect_src :self, :https, "ws://#{AppHost.new.domain_with_port}", "wss://#{AppHost.new.domain_with_port}", "wss://bintu-h5live.nanocosmos.de"
|
||||
policy.connect_src :self, :https, "ws://#{AppHost.new.domain_with_port}", "wss://#{AppHost.new.domain_with_port}"
|
||||
|
||||
# Specify URI for violation reports
|
||||
# policy.report_uri "/csp-violation-report-endpoint"
|
||||
|
||||
13
config/initializers/omniauth.rb
Normal file
13
config/initializers/omniauth.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
require 'azure_ad'
|
||||
|
||||
Rails.application.config.middleware.use OmniAuth::Builder do
|
||||
provider :azure_ad,
|
||||
client_id: ENV['AZURE_CLIENT_ID'],
|
||||
client_secret: ENV['AZURE_CLIENT_SECRET'],
|
||||
redirect_uri: ENV['AZURE_REDIRECT_URI'],
|
||||
client_options: {
|
||||
token_url: "#{ENV['AZURE_TENANT_ID']}/oauth2/v2.0/token",
|
||||
authorize_url: "#{ENV['AZURE_TENANT_ID']}/oauth2/v2.0/authorize"
|
||||
},
|
||||
scope: ENV['AZURE_SCOPES']
|
||||
end
|
||||
@@ -1 +0,0 @@
|
||||
Rails.application.config.session_store :cookie_store, key: '_easy_release_session', expire_after: 1.month
|
||||
@@ -224,9 +224,6 @@ en:
|
||||
manage: Manage
|
||||
empty_bookmarks:
|
||||
empty: Notes will appear here
|
||||
broadcast_recordings:
|
||||
edit:
|
||||
heading: Edit Broadcast Recording
|
||||
broadcasts:
|
||||
broadcast:
|
||||
actions:
|
||||
@@ -278,6 +275,9 @@ en:
|
||||
stream_multiple_cameras: Stream multiple cameras at one time
|
||||
update:
|
||||
reset_notice: The Share URL has been reset, and the previous URL will no longer work. Please click "Copy URL" and share it again with those who you want to have access to this live stream
|
||||
form:
|
||||
labels:
|
||||
conference_option: Conference Option
|
||||
bulk_taggings:
|
||||
new_bulk_tag_modal:
|
||||
submit: Add
|
||||
@@ -904,8 +904,6 @@ en:
|
||||
form:
|
||||
contract_and_rights:
|
||||
heading: 3 of 4 Contract & Exploitable Rights
|
||||
files:
|
||||
heading: Files
|
||||
guardian_2_info:
|
||||
heading: Second Guardian Information (if company requires)
|
||||
guardian_info:
|
||||
@@ -913,7 +911,7 @@ en:
|
||||
material_details:
|
||||
heading: 1 of 3 Material Details
|
||||
photos:
|
||||
dropzone_label: "To Add Files to the release:<br>Drag & Drop Files<br>or<br>Click or Tap here to browse files"
|
||||
dropzone_label: Tap to take a photo of Licensed Material (optional)
|
||||
guardian_2_photo:
|
||||
heading: Second Guardian Photo
|
||||
guardian_photo:
|
||||
@@ -928,7 +926,6 @@ en:
|
||||
empty: Material Releases will appear here
|
||||
table_headers:
|
||||
approved: Approved
|
||||
files_count: No. of Files
|
||||
name: Name
|
||||
notes: Notes
|
||||
owner_info: Owner Info
|
||||
@@ -940,7 +937,6 @@ en:
|
||||
review: Review
|
||||
messages:
|
||||
approved_tooltip: Approved by %{user} on %{timestamp}
|
||||
no_media: No Media
|
||||
no_photos: Needs Photo
|
||||
new:
|
||||
heading: Import Material Release (Products / Logos)
|
||||
@@ -1259,8 +1255,6 @@ en:
|
||||
cancel: Cancel
|
||||
contact_info:
|
||||
heading: Licensor/Owner Contact Information
|
||||
files:
|
||||
heading: Files
|
||||
guardian_2_info:
|
||||
heading: Second Guardian Information (if company requires)
|
||||
guardian_2_photo:
|
||||
@@ -1651,3 +1645,9 @@ en:
|
||||
edit: Edit
|
||||
report: Report
|
||||
generating: Generating...
|
||||
conference_meetings:
|
||||
show:
|
||||
alerts:
|
||||
not_authenticated: You are not authenticated via Microsoft, please authenticate and try again
|
||||
failed_to_join: Failed to join conference
|
||||
unknown_conference_option: Unknown conference option
|
||||
|
||||
@@ -131,6 +131,9 @@ es:
|
||||
stream_multiple_cameras: Stream multiple cameras at one time
|
||||
update:
|
||||
reset_notice: The Share URL has been reset, and the previous URL will no longer work. Please click "Copy URL" and share it again with those who you want to have access to this live stream
|
||||
form:
|
||||
labels:
|
||||
conference_option: Conference Option (ES)
|
||||
contract_templates:
|
||||
blank_contracts:
|
||||
create:
|
||||
@@ -705,3 +708,9 @@ es:
|
||||
production_elements_logs: Production Elements Logs, and more (ES)
|
||||
reduces_labor_cost: Reduces labor costs (ES)
|
||||
simplifies_cue_sheets: Simplifies Music Cue Sheets, Graphic Cue Sheets (ES)
|
||||
conference_meetings:
|
||||
show:
|
||||
alerts:
|
||||
not_authenticated: You are not authenticated via Microsoft, please authenticate and try again (ES)
|
||||
failed_to_join: Failed to join conference (ES)
|
||||
unknown_conference_option: Unknown conference option (ES)
|
||||
|
||||
@@ -4,6 +4,8 @@ require 'sidekiq/web'
|
||||
Rails.application.routes.draw do
|
||||
AVAILABLE_LOCALES_REGEX = /#{I18n.available_locales.join("|")}/.freeze
|
||||
|
||||
get 'auth/azure_ad/callback', to: 'callbacks#create'
|
||||
|
||||
concern :confirmable do
|
||||
resources :video_release_confirmations, only: [:new, :create, :destroy]
|
||||
end
|
||||
@@ -55,7 +57,7 @@ Rails.application.routes.draw do
|
||||
resources :appearance_releases, except: [:show], concerns: [:contractable, :notable]
|
||||
resources :appearance_release_imports, only: [:create]
|
||||
resources :location_releases, except: [:show], concerns: [:contractable, :notable, :photoable]
|
||||
resources :material_releases, except: [:show], concerns: [:contractable, :notable, :file_uploadable]
|
||||
resources :material_releases, except: [:show], concerns: [:contractable, :notable, :photoable]
|
||||
resources :music_releases, except: [:show], concerns: [:contractable, :notable]
|
||||
resources :talent_releases, except: [:show], concerns: [:contractable, :notable, :photoable]
|
||||
resources :medical_releases, except: [:show], concerns: [:contractable, :notable, :photoable]
|
||||
@@ -100,10 +102,8 @@ Rails.application.routes.draw do
|
||||
member do
|
||||
delete :destroy_file
|
||||
end
|
||||
resource :zoom_meeting, only: [:show]
|
||||
resources :broadcast_recordings, only: [:destroy, :edit, :update] do
|
||||
resources :broadcast_recording_starrings, only: :create
|
||||
end
|
||||
resource :conference_meeting, only: [:show]
|
||||
resources :broadcast_recordings, only: :destroy
|
||||
end
|
||||
resources :directories, except: [:index] do
|
||||
member do
|
||||
@@ -200,7 +200,6 @@ Rails.application.routes.draw do
|
||||
|
||||
get "cookies_disabled" => 'pages#show', id: "cookies_disabled", as: :cookies_disabled
|
||||
get "accountless_user" => 'pages#show', id: "accountless_user", as: :accountless_user
|
||||
get "nanocosmos_player" => 'pages#show', id: "nanocosmos_player", as: :nanocosmos_player
|
||||
|
||||
resource :session, only: [:new, :create]
|
||||
resources :password_resets, only: [:new, :create, :edit, :update]
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
class MigrateMaterialPhotosToFiles < ActiveRecord::DataMigration
|
||||
def up
|
||||
photos = ActiveStorage::Attachment.where(name: "photos", record_type: "MaterialRelease")
|
||||
|
||||
photos.each do |photo|
|
||||
photo.update(name: "files")
|
||||
end
|
||||
end
|
||||
end
|
||||
8
db/migrate/20200820081251_add_microsoft_info_to_users.rb
Normal file
8
db/migrate/20200820081251_add_microsoft_info_to_users.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class AddMicrosoftInfoToUsers < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :users, :microsoft_user_id, :string
|
||||
add_column :users, :microsoft_access_token, :string
|
||||
add_column :users, :microsoft_refresh_token, :string
|
||||
add_column :users, :microsoft_token_expires_at, :integer
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
class AddConferenceDetailsToBroadcasts < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :broadcasts, :conference_option, :string
|
||||
add_column :broadcasts, :conference_join_url, :string
|
||||
end
|
||||
end
|
||||
@@ -1,6 +0,0 @@
|
||||
class AddNameAndDescriptionToBroadcastRecordings < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :broadcast_recordings, :name, :string
|
||||
add_column :broadcast_recordings, :description, :text
|
||||
end
|
||||
end
|
||||
@@ -1,5 +0,0 @@
|
||||
class AddStarToBroadcastRecordings < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :broadcast_recordings, :starred, :boolean, default: false
|
||||
end
|
||||
end
|
||||
@@ -541,10 +541,7 @@ CREATE TABLE public.broadcast_recordings (
|
||||
created_at timestamp(6) without time zone NOT NULL,
|
||||
updated_at timestamp(6) without time zone NOT NULL,
|
||||
duration double precision,
|
||||
hidden boolean DEFAULT false,
|
||||
starred boolean DEFAULT false,
|
||||
name character varying,
|
||||
description text
|
||||
hidden boolean DEFAULT false
|
||||
);
|
||||
|
||||
|
||||
@@ -589,7 +586,9 @@ CREATE TABLE public.broadcasts (
|
||||
stream_key_override character varying,
|
||||
director_mode_video_embed text,
|
||||
simulcast_uid character varying,
|
||||
video_conference_url_override character varying
|
||||
video_conference_url_override character varying,
|
||||
conference_option character varying,
|
||||
conference_join_url character varying
|
||||
);
|
||||
|
||||
|
||||
@@ -1492,6 +1491,7 @@ CREATE TABLE public.settings (
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.settings_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
@@ -1527,6 +1527,7 @@ CREATE TABLE public.taggings (
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.taggings_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
@@ -1557,6 +1558,7 @@ CREATE TABLE public.tags (
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.tags_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
@@ -1804,7 +1806,11 @@ CREATE TABLE public.users (
|
||||
remember_created_at timestamp without time zone,
|
||||
first_name character varying,
|
||||
last_name character varying,
|
||||
time_zone character varying DEFAULT 'UTC'::character varying NOT NULL
|
||||
time_zone character varying DEFAULT 'UTC'::character varying NOT NULL,
|
||||
microsoft_user_id character varying,
|
||||
microsoft_access_token character varying,
|
||||
microsoft_refresh_token character varying,
|
||||
microsoft_token_expires_at integer
|
||||
);
|
||||
|
||||
|
||||
@@ -4026,7 +4032,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20200811102720'),
|
||||
('20200812060406'),
|
||||
('20200819070738'),
|
||||
('20200820082501'),
|
||||
('20200824171649');
|
||||
('20200820081251'),
|
||||
('20200820081524');
|
||||
|
||||
|
||||
|
||||
124
lib/azure_ad.rb
Normal file
124
lib/azure_ad.rb
Normal file
@@ -0,0 +1,124 @@
|
||||
require 'omniauth-oauth2'
|
||||
|
||||
# This file is from omniauth-microsoft_graph lib (not installed)
|
||||
# It is modified to make auth work
|
||||
|
||||
module OmniAuth
|
||||
module Strategies
|
||||
class AzureAd < OmniAuth::Strategies::OAuth2
|
||||
BASE_SCOPE_URL = 'https://graph.microsoft.com/'
|
||||
BASE_SCOPES = %w[offline_access openid email profile].freeze
|
||||
DEFAULT_SCOPE = 'offline_access openid email profile User.Read'.freeze
|
||||
|
||||
option :name, :azure_ad
|
||||
|
||||
option :client_options,
|
||||
site: 'https://login.microsoftonline.com/'
|
||||
|
||||
option :authorize_options, %i[state callback_url scope response_mode]
|
||||
|
||||
option :token_params, {}
|
||||
|
||||
option :scope, DEFAULT_SCOPE
|
||||
option :authorized_client_ids, []
|
||||
|
||||
uid { raw_info["id"] }
|
||||
|
||||
info do
|
||||
{
|
||||
# 'email' => raw_info["mail"],
|
||||
# 'first_name' => raw_info["givenName"],
|
||||
# 'last_name' => raw_info["surname"],
|
||||
# 'name' => [raw_info["givenName"], raw_info["surname"]].join(' '),
|
||||
# 'nickname' => raw_info["displayName"],
|
||||
}
|
||||
end
|
||||
|
||||
extra do
|
||||
{
|
||||
# 'raw_info' => raw_info,
|
||||
# 'params' => access_token.params,
|
||||
# 'aud' => options.client_id
|
||||
}
|
||||
end
|
||||
|
||||
def authorize_params
|
||||
super.tap do |params|
|
||||
options[:authorize_options].each do |k|
|
||||
params[k] = request.params[k.to_s] unless [nil, ''].include?(request.params[k.to_s])
|
||||
end
|
||||
|
||||
params[:scope] = get_scope(params)
|
||||
|
||||
session['omniauth.state'] = params[:state] if params[:state]
|
||||
end
|
||||
end
|
||||
|
||||
def raw_info
|
||||
@raw_info ||= access_token.get('https://graph.microsoft.com/v1.0/me').parsed
|
||||
end
|
||||
|
||||
def callback_url
|
||||
options[:callback_url] || full_host + script_name + callback_path
|
||||
end
|
||||
|
||||
def custom_build_access_token
|
||||
token_response = get_access_token(request)
|
||||
session[:microsoft_graph_api_token] = token_response.token
|
||||
token_response
|
||||
end
|
||||
|
||||
alias build_access_token custom_build_access_token
|
||||
|
||||
private
|
||||
|
||||
def get_access_token(request)
|
||||
verifier = request.params['code']
|
||||
redirect_uri = request.params['redirect_uri'] || request.params['callback_url']
|
||||
if verifier && request.xhr?
|
||||
client_get_token(verifier, redirect_uri || '/auth/azure_ad/callback')
|
||||
elsif verifier
|
||||
client_get_token(verifier, redirect_uri || callback_url)
|
||||
elsif verify_token(request.params['access_token'])
|
||||
::OAuth2::AccessToken.from_hash(client, request.params.dup)
|
||||
elsif request.content_type =~ /json/i
|
||||
begin
|
||||
body = JSON.parse(request.body.read)
|
||||
request.body.rewind # rewind request body for downstream middlewares
|
||||
verifier = body && body['code']
|
||||
client_get_token(verifier, '/auth/azure_ad/callback') if verifier
|
||||
rescue JSON::ParserError => e
|
||||
warn "[omniauth google-oauth2] JSON parse error=#{e}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def client_get_token(verifier, redirect_uri)
|
||||
client.auth_code.get_token(verifier, get_token_options(redirect_uri), get_token_params)
|
||||
end
|
||||
|
||||
def get_token_params
|
||||
deep_symbolize(options.auth_token_params || {})
|
||||
end
|
||||
|
||||
def get_token_options(redirect_uri = '')
|
||||
{ redirect_uri: redirect_uri }.merge(token_params.to_hash(symbolize_keys: true))
|
||||
end
|
||||
|
||||
def get_scope(params)
|
||||
raw_scope = params[:scope] || DEFAULT_SCOPE
|
||||
scope_list = raw_scope.split(' ').map { |item| item.split(',') }.flatten
|
||||
scope_list.map! { |s| s =~ %r{^https?://} || BASE_SCOPES.include?(s) ? s : "#{BASE_SCOPE_URL}#{s}" }
|
||||
scope_list.join(' ')
|
||||
end
|
||||
|
||||
def verify_token(access_token)
|
||||
return false unless access_token
|
||||
# access_token.get('https://graph.microsoft.com/v1.0/me').parsed
|
||||
raw_response = client.request(:get, 'https://graph.microsoft.com/v1.0/me',
|
||||
params: { access_token: access_token }).parsed
|
||||
(raw_response['aud'] == options.client_id) || options.authorized_client_ids.include?(raw_response['aud'])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
107
lib/microsoft_graph.rb
Normal file
107
lib/microsoft_graph.rb
Normal file
@@ -0,0 +1,107 @@
|
||||
require 'httparty'
|
||||
|
||||
class MicrosoftGraph
|
||||
BASE_URL = 'https://graph.microsoft.com/v1.0'.freeze
|
||||
|
||||
def initialize(current_user, client_id, client_secret, tenant_id, scopes)
|
||||
@current_user = current_user
|
||||
@uid = current_user.microsoft_user_id
|
||||
@token = current_user.microsoft_access_token
|
||||
@refresh_token = current_user.microsoft_refresh_token
|
||||
@token_expires_at = current_user.microsoft_token_expires_at
|
||||
|
||||
@client_id = client_id
|
||||
@client_secret = client_secret
|
||||
@tenant_id = tenant_id
|
||||
@scopes = scopes
|
||||
end
|
||||
|
||||
def create_teams_meeting(subject)
|
||||
if @refresh_token.nil? || @token_expires_at.nil?
|
||||
raise ActionController::InvalidAuthenticityToken, 'Missing refresh token / token expiration'
|
||||
return
|
||||
end
|
||||
|
||||
# Obtain new token if token is expired or will expire in less than 5 minutes
|
||||
if 5.minutes.from_now.to_i > @token_expires_at.seconds
|
||||
refresh_access_token
|
||||
end
|
||||
|
||||
if @token.nil?
|
||||
raise ActionController::InvalidAuthenticityToken, 'Missing access token'
|
||||
return
|
||||
end
|
||||
|
||||
response = HTTParty.post(
|
||||
"#{BASE_URL}/me/onlineMeetings",
|
||||
body: {
|
||||
subject: subject,
|
||||
participants: {
|
||||
organizer: {
|
||||
identity: {
|
||||
user: {
|
||||
id: @uid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.to_json,
|
||||
headers: {
|
||||
Authorization: "Bearer #{@token}",
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
)
|
||||
|
||||
raise StandardError, 'Authenticated user does not have a permission to create Teams Online Meeting' if response.code == 403
|
||||
|
||||
if response.code != 201
|
||||
Rails.logger.error('[Microsoft Graph Error]')
|
||||
Rails.logger.error(response.inspect)
|
||||
raise StandardError, "Failed to create teams meeting [#{response.code}]"
|
||||
else
|
||||
JSON.parse(response.body)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def refresh_token_url
|
||||
"https://login.microsoftonline.com/#{@tenant_id}/oauth2/v2.0/token"
|
||||
end
|
||||
|
||||
def refresh_access_token
|
||||
response = HTTParty.post(refresh_token_url,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: {
|
||||
client_id: @client_id,
|
||||
client_secret: @client_secret,
|
||||
refresh_token: @refresh_token,
|
||||
grant_type: 'refresh_token',
|
||||
scope: @scopes
|
||||
})
|
||||
|
||||
if response.code != 200
|
||||
Rails.logger.error '[Microsoft Graph Error] Failed to obtain new access token using refresh token'
|
||||
Rails.logger.error(response.inspect)
|
||||
raise StandardError, 'Failed to obtain new access token'
|
||||
end
|
||||
|
||||
parsed_response = JSON.parse(response.body)
|
||||
|
||||
new_access_token = parsed_response['access_token']
|
||||
new_refresh_token = parsed_response['refresh_token']
|
||||
token_expires_in = parsed_response['expires_in'] # For how long access token is valid (in seconds)
|
||||
token_new_expiration_time = Time.now.to_i + token_expires_in
|
||||
|
||||
@current_user.microsoft_access_token = new_access_token
|
||||
@current_user.microsoft_refresh_token = new_refresh_token
|
||||
@current_user.microsoft_token_expires_at = token_new_expiration_time
|
||||
@current_user.save!
|
||||
|
||||
@token = new_access_token
|
||||
@refresh_token = new_refresh_token
|
||||
@token_expires_at = token_new_expiration_time
|
||||
end
|
||||
end
|
||||
@@ -17,7 +17,6 @@ RSpec.describe BroadcastsChannel, type: :channel do
|
||||
it "broadcasts to the channel" do
|
||||
status_content = ApplicationController.render partial: "broadcasts/broadcast_status", locals: { broadcast: broadcast }
|
||||
video_content = ApplicationController.render partial: "broadcasts/video", locals: { broadcast: broadcast }
|
||||
live_take = ApplicationController.render partial: "broadcasts/live_take", locals: { broadcast: broadcast }
|
||||
|
||||
expect {
|
||||
BroadcastsChannel.broadcast_stream_updates(broadcast)
|
||||
@@ -28,7 +27,6 @@ RSpec.describe BroadcastsChannel, type: :channel do
|
||||
full_live_stream_playback_url: broadcast.full_live_stream_playback_url,
|
||||
status_content: status_content,
|
||||
video_content: video_content,
|
||||
live_take_content: live_take,
|
||||
streamer_status: broadcast.streamer_status
|
||||
})
|
||||
end
|
||||
|
||||
@@ -30,16 +30,6 @@ RSpec.describe Api::AcquiredMediaReleasesController, type: :controller do
|
||||
|
||||
expect(response).to be_successful
|
||||
end
|
||||
|
||||
it 'contains files attachment data' do
|
||||
tested_release = create(:acquired_media_release, name: 'ct1', project_id: project.id)
|
||||
|
||||
sign_in_to_api(current_user)
|
||||
get :show, params: { id: tested_release.id }
|
||||
|
||||
expect(response.body).to match /file_infos/
|
||||
expect(response.body).to match /files/
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
|
||||
@@ -14,8 +14,6 @@ RSpec.describe Api::ProfilesController, type: :controller do
|
||||
expect(response).to be_successful
|
||||
expect(response_body_data).to include('id' => current_user.to_param, 'type' => 'user')
|
||||
expect(response_body_data_attributes).to include('email' => current_user.email)
|
||||
expect(response_body_data_attributes).to include('full_name' => current_user.full_name)
|
||||
expect(response_body_data_attributes).to include('company_name' => current_user.primary_account.name)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -27,17 +27,10 @@ RSpec.describe ApprovalsController, type: :controller do
|
||||
|
||||
expect(MedicalRelease.last.approved?).to eq false
|
||||
|
||||
post :create, params: { medical_release_id: medical_release, medical_release: approvable_params }
|
||||
post :create, params: { medical_release_id: medical_release }
|
||||
|
||||
expect(response).to redirect_to [project, :medical_releases]
|
||||
expect(MedicalRelease.last.approved?).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def approvable_params
|
||||
signature_base64 ||= Base64Image.from_image(file_fixture('signature.png')).data_uri
|
||||
{ approved_by_user_signature: { data: signature_base64 } }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe BroadcastRecordingStarringsController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:account) { user.primary_account }
|
||||
let(:project) { create(:project, account: user.primary_account) }
|
||||
|
||||
before do
|
||||
sign_in user
|
||||
stub_mux_live_stream
|
||||
end
|
||||
|
||||
describe "#create" do
|
||||
let(:broadcast) { create(:broadcast, project: project, name: "New Broadcast") }
|
||||
let(:recordings) { create_list(:broadcast_recording, 5, :with_random_asset_uid, broadcast: broadcast) }
|
||||
let(:starred_recordings) { create_list(:broadcast_recording, 5, :with_random_asset_uid, broadcast: broadcast, starred: true) }
|
||||
|
||||
it "sets star property to true when recording is starred" do
|
||||
recordings.each do |recording|
|
||||
expect(recording.starred).to be_falsey
|
||||
end
|
||||
|
||||
post :create, params: { project_id: project, broadcast_id: broadcast, broadcast_recording_id: recordings.first.id }, xhr: true
|
||||
|
||||
expect(recordings.first.reload.starred).to eq true
|
||||
|
||||
recordings[1..5].each do |recording|
|
||||
expect(recording.reload.starred).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
it "sets star property to false when recording is unstarred" do
|
||||
starred_recordings.each do |recording|
|
||||
expect(recording.starred).to be_truthy
|
||||
end
|
||||
|
||||
post :create, params: { project_id: project, broadcast_id: broadcast, broadcast_recording_id: starred_recordings.first.id }, xhr: true
|
||||
|
||||
expect(starred_recordings.first.reload.starred).to eq false
|
||||
|
||||
starred_recordings[1..5].each do |recording|
|
||||
expect(recording.reload.starred).to eq true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -9,66 +9,16 @@ RSpec.describe BroadcastRecordingsController, type: :controller do
|
||||
|
||||
before do
|
||||
sign_in user
|
||||
stub_mux_live_stream
|
||||
end
|
||||
|
||||
describe "#edit" do
|
||||
let(:broadcast) { create(:broadcast, project: project, name: "New Broadcast") }
|
||||
let(:recording) { create(:broadcast_recording, broadcast: broadcast) }
|
||||
|
||||
before do
|
||||
stub_mux_live_stream
|
||||
end
|
||||
|
||||
it "assigns project, broadcast, broadcast recording" do
|
||||
get :edit, params: { project_id: project, broadcast_id: broadcast, id: recording }, xhr: true
|
||||
|
||||
expect(assigns(:project)).to have_attributes({
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
account_id: project.account_id
|
||||
})
|
||||
|
||||
expect(assigns(:broadcast)).to have_attributes({
|
||||
id: broadcast.id,
|
||||
name: broadcast.name,
|
||||
project_id: project.id
|
||||
})
|
||||
|
||||
expect(assigns(:recording)).to have_attributes({
|
||||
id: recording.id,
|
||||
broadcast_id: broadcast.id,
|
||||
file_name: "high.mp4"
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
describe "#update" do
|
||||
let(:broadcast) { create(:broadcast, project: project, name: "New Broadcast") }
|
||||
let(:recording) { create(:broadcast_recording, broadcast: broadcast) }
|
||||
let(:recordings) { create_list(:broadcast_recording, 5, :with_random_asset_uid, broadcast: broadcast) }
|
||||
let(:starred_recordings) { create_list(:broadcast_recording, 5, :with_random_asset_uid, broadcast: broadcast, starred: true) }
|
||||
|
||||
before do
|
||||
stub_mux_live_stream
|
||||
end
|
||||
|
||||
it "updates the recording's name and description" do
|
||||
expect(recording.name).to eq(recording.download_file_name)
|
||||
expect(recording.description).to eq("No description provided for this recording.")
|
||||
|
||||
patch :update, params: { project_id: project, broadcast_id: broadcast, id: recording, broadcast_recording: { name: "Just for fun", description: "I had fun while making this stream." } }, xhr: true
|
||||
recording.reload
|
||||
|
||||
expect(recording.name).to eq("Just for fun")
|
||||
expect(recording.description).to eq("I had fun while making this stream.")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#destroy" do
|
||||
let(:broadcast) { create(:broadcast, project: project, name: "New Broadcast") }
|
||||
let(:recording) { create(:broadcast_recording, broadcast: broadcast) }
|
||||
|
||||
before do
|
||||
stub_mux_live_stream
|
||||
end
|
||||
|
||||
it "hides the broadcast recording" do
|
||||
expect(recording.hidden).to be false
|
||||
|
||||
|
||||
@@ -150,16 +150,31 @@ RSpec.describe BroadcastsController, type: :controller do
|
||||
expect(assigns(:broadcast)).to eq(broadcast)
|
||||
end
|
||||
|
||||
it "displays zoom meeting button" do
|
||||
it "renders readonly share url" do
|
||||
get :show, params: { project_id: project.id, id: broadcast.id }
|
||||
|
||||
expect(response.body).to have_button "Copy URL"
|
||||
expect(response.body).to have_xpath "//input[@readonly][@value='#{broadcast_url(broadcast.token)}']"
|
||||
end
|
||||
|
||||
it "displays zoom meeting button for zoom conference option" do
|
||||
get :show, params: { project_id: project.id, id: broadcast.id }
|
||||
|
||||
expect(response.body).to have_link("Video Conference", href: project_broadcast_zoom_meeting_url(project, broadcast))
|
||||
expect(response.body).to have_link("Video Conference", href: project_broadcast_conference_meeting_url(project, broadcast))
|
||||
end
|
||||
|
||||
it "displays microsoft teams meeting button for MS Teams conference option" do
|
||||
ms_teams_broadcast = create(:broadcast, :ms_teams_conference, project: project )
|
||||
get :show, params: { project_id: project.id, id: ms_teams_broadcast.id }
|
||||
|
||||
expect(response.body).to have_content 'MS Teams'
|
||||
expect(response.body).to have_link 'Video Conference', href: project_broadcast_conference_meeting_url(project, ms_teams_broadcast)
|
||||
end
|
||||
|
||||
it "assigns required variables" do
|
||||
get :show, params: { project_id: project.id, id: broadcast.id }
|
||||
|
||||
expect(assigns(:conference_url)).to eq project_broadcast_zoom_meeting_url(project, broadcast)
|
||||
expect(assigns(:conference_url)).to eq project_broadcast_conference_meeting_url(project, broadcast)
|
||||
expect(assigns(:broadcast)).to eq broadcast
|
||||
end
|
||||
|
||||
@@ -167,7 +182,7 @@ RSpec.describe BroadcastsController, type: :controller do
|
||||
it "renders the view dropdown with just the current broadcast" do
|
||||
get :show, params: { project_id: project, id: broadcast }
|
||||
|
||||
expect(response.body).to have_content broadcast.name
|
||||
expect(response.body).to have_content "Switch View"
|
||||
expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Another Broadcast")
|
||||
end
|
||||
end
|
||||
@@ -178,12 +193,32 @@ RSpec.describe BroadcastsController, type: :controller do
|
||||
|
||||
get :show, params: { project_id: project, id: broadcast, multi_view_ids: [broadcast.id, other_broadcast.id] }
|
||||
|
||||
expect(response.body).to have_content broadcast.name
|
||||
expect(response.body).to have_content "Switch View"
|
||||
expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Another Broadcast")
|
||||
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: "Some Other Broadcast")
|
||||
end
|
||||
end
|
||||
|
||||
context "when there are no recordings for the current broadcast" do
|
||||
it "renders the view dropdown with a message" do
|
||||
get :show, params: { project_id: project, id: broadcast }
|
||||
|
||||
expect(response.body).to have_content "Switch View"
|
||||
expect(response.body).to have_selector(".dropdown-menu .dropdown-item", text: "Recordings will appear here")
|
||||
end
|
||||
end
|
||||
|
||||
context "when there are recordings available" do
|
||||
it "renders the view dropdown with the recordings" do
|
||||
recording = create(:broadcast_recording, broadcast: broadcast)
|
||||
|
||||
get :show, params: { project_id: project, id: broadcast }
|
||||
|
||||
expect(response.body).to have_content "Switch View"
|
||||
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: recording.download_file_name)
|
||||
end
|
||||
end
|
||||
|
||||
context "when virtual director video embed is available" do
|
||||
let(:broadcast) { create(:broadcast, project: project, name: "Another Broadcast",
|
||||
director_mode_video_embed: "<iframe>video player</iframe>") }
|
||||
@@ -191,8 +226,9 @@ RSpec.describe BroadcastsController, type: :controller do
|
||||
it "renders the view dropdown with a director mode enable option" do
|
||||
get :show, params: { project_id: project, id: broadcast }
|
||||
|
||||
expect(response.body).to have_content broadcast.name
|
||||
expect(response.body).to have_selector(".custom-control-label", text: "Director Mode")
|
||||
expect(response.body).to have_content "Switch View"
|
||||
expect(response.body).to have_selector(".dropdown-menu h5.dropdown-header", text: "Director Mode")
|
||||
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: "Enable Director Mode")
|
||||
end
|
||||
|
||||
context "when director mode is enabled" do
|
||||
@@ -205,8 +241,9 @@ RSpec.describe BroadcastsController, type: :controller do
|
||||
it "renders the view dropdown with a director mode disable option" do
|
||||
get :show, params: { project_id: project, id: broadcast, director_mode: true }
|
||||
|
||||
expect(response.body).to have_content broadcast.name
|
||||
expect(response.body).to have_selector(".custom-control-label", text: "Director Mode")
|
||||
expect(response.body).to have_content "Switch View"
|
||||
expect(response.body).to have_selector(".dropdown-menu h5.dropdown-header", text: "Director Mode")
|
||||
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: "Disable Director Mode")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
75
spec/controllers/conference_meetings_controller_spec.rb
Normal file
75
spec/controllers/conference_meetings_controller_spec.rb
Normal file
@@ -0,0 +1,75 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ConferenceMeetingsController, type: :controller do
|
||||
let(:user) { create(:user) }
|
||||
let(:account) { user.primary_account }
|
||||
let(:project) { create(:project, account: user.primary_account) }
|
||||
let(:broadcast) { create(:broadcast, name: "Broadcast", project: project) }
|
||||
let(:ms_teams_broadcast) { create(:broadcast, :ms_teams_conference, project: project) }
|
||||
let(:unknown_option_broadcast) { create(:broadcast, project: project, conference_option: 'google') }
|
||||
|
||||
let(:meeting_start_url) { "http://meeting_start_url" }
|
||||
let(:meeting_hash) { HashWithIndifferentAccess.new(start_url: meeting_start_url) }
|
||||
let(:user_create_response) { {"id" => "new_host_id"} }
|
||||
let(:roles_assign_response) { {"ids" => ["new_host_id"]} }
|
||||
let(:roles_list_response) { {"roles" => [{"name" => "directme-host"}]} }
|
||||
|
||||
|
||||
before :each do
|
||||
allow_any_instance_of(ZoomGateway).to receive(:find_meeting).and_return(meeting_hash)
|
||||
allow_any_instance_of(ZoomGateway).to receive(:create_meeting).and_return("meeting_id")
|
||||
allow_any_instance_of(ZoomGateway).to receive(:create_host).and_return("host_id")
|
||||
stub_mux_live_stream
|
||||
end
|
||||
|
||||
describe "#show" do
|
||||
before { sign_in user }
|
||||
|
||||
it "redirects to meeting start url with Zoom conference option" do
|
||||
get :show, params: { project_id: project.id, broadcast_id: broadcast.id }
|
||||
expect(response).to redirect_to(meeting_start_url)
|
||||
end
|
||||
|
||||
it "redirects to the broadcast show page with alert if user is not authenticated via microsoft and tries to create MS Teams meeting" do
|
||||
get :show, params: { project_id: project.id, broadcast_id: ms_teams_broadcast.id }
|
||||
expect(response).to redirect_to project_broadcast_path(project, ms_teams_broadcast)
|
||||
expect(flash.alert).to eq not_authenticated_alert
|
||||
end
|
||||
|
||||
it "redirects to the broadcast show page with alert if user is authenticated via microsoft and tries to create MS Teams meeting but Graph API fails to create meeting" do
|
||||
allow_any_instance_of(MicrosoftGraph).to receive(:create_teams_meeting).and_return(nil)
|
||||
get :show, params: { project_id: project.id, broadcast_id: ms_teams_broadcast.id }
|
||||
expect(response).to redirect_to project_broadcast_path(project, ms_teams_broadcast)
|
||||
expect(flash.alert).to eq failed_to_join_alert
|
||||
end
|
||||
|
||||
it "redirects to the broadcast show page with alert if conference option is not reckognized" do
|
||||
get :show, params: { project_id: project.id, broadcast_id: unknown_option_broadcast.id }
|
||||
expect(response).to redirect_to project_broadcast_path(project, unknown_option_broadcast)
|
||||
expect(flash.alert).to eq unknown_conference_option_alert
|
||||
end
|
||||
|
||||
it "redirects to meeting start url with MS Teams conference option" do
|
||||
new_ms_teams_meeting = JSON.parse({ joinUrl: meeting_start_url }.to_json)
|
||||
|
||||
allow_any_instance_of(MicrosoftGraph).to receive(:create_teams_meeting).and_return(new_ms_teams_meeting)
|
||||
|
||||
get :show, params: { project_id: project.id, broadcast_id: ms_teams_broadcast.id }
|
||||
expect(response).to redirect_to(meeting_start_url)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def not_authenticated_alert
|
||||
t 'conference_meetings.show.alerts.not_authenticated'
|
||||
end
|
||||
|
||||
def failed_to_join_alert
|
||||
t 'conference_meetings.show.alerts.failed_to_join'
|
||||
end
|
||||
|
||||
def unknown_conference_option_alert
|
||||
t 'conference_meetings.show.alerts.unknown_conference_option'
|
||||
end
|
||||
end
|
||||
@@ -68,7 +68,13 @@ RSpec.describe PhotosController, type: :controller do
|
||||
|
||||
it_behaves_like "a photoable releases controller"
|
||||
end
|
||||
|
||||
|
||||
context "for material releases" do
|
||||
subject { create(:material_release, project: project) }
|
||||
|
||||
it_behaves_like "a photoable releases controller"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def release_params
|
||||
|
||||
@@ -45,7 +45,7 @@ RSpec.describe Public::BroadcastsController, type: :controller do
|
||||
it "renders the view dropdown with just the current broadcast" do
|
||||
get :show, params: { token: broadcast.token }
|
||||
|
||||
expect(response.body).to have_content broadcast.name
|
||||
expect(response.body).to have_content "Switch View"
|
||||
expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Broadcast")
|
||||
end
|
||||
end
|
||||
@@ -56,7 +56,7 @@ RSpec.describe Public::BroadcastsController, type: :controller do
|
||||
|
||||
get :show, params: { token: broadcast.token, multi_view_tokens: [broadcast.token, other_broadcast.token] }
|
||||
|
||||
expect(response.body).to have_content broadcast.name
|
||||
expect(response.body).to have_content "Switch View"
|
||||
expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Broadcast")
|
||||
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: "Some Other Broadcast")
|
||||
end
|
||||
@@ -66,7 +66,8 @@ RSpec.describe Public::BroadcastsController, type: :controller do
|
||||
it "renders the view dropdown with a message" do
|
||||
get :show, params: { token: broadcast.token }
|
||||
|
||||
expect(response.body).to have_content broadcast.name
|
||||
expect(response.body).to have_content "Switch View"
|
||||
expect(response.body).to have_selector(".dropdown-menu .dropdown-item", text: "Recordings will appear here")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -76,7 +77,8 @@ RSpec.describe Public::BroadcastsController, type: :controller do
|
||||
|
||||
get :show, params: { token: broadcast.token }
|
||||
|
||||
expect(response.body).to have_content broadcast.name
|
||||
expect(response.body).to have_content "Switch View"
|
||||
expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: recording.download_file_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ describe Public::MaterialReleasesController do
|
||||
it "allows photos param" do
|
||||
contract_template = create(:contract_template, project: project)
|
||||
|
||||
post :create, params: { account_id: user.primary_account.to_param, project_id: project, contract_template_id: contract_template, material_release: material_release_params_with_files }
|
||||
post :create, params: { account_id: user.primary_account.to_param, project_id: project, contract_template_id: contract_template, material_release: material_release_params_with_photos }
|
||||
|
||||
expect(response).to be_successful
|
||||
expect(MaterialRelease.last.files.attached?).to eq true
|
||||
expect(MaterialRelease.last.photos.attached?).to eq true
|
||||
end
|
||||
|
||||
it "displays validation errors" do
|
||||
@@ -63,8 +63,8 @@ describe Public::MaterialReleasesController do
|
||||
attributes_for(:material_release, :native).except(:signature).merge(signature_param)
|
||||
end
|
||||
|
||||
def material_release_params_with_files
|
||||
attributes_for(:material_release, :native, :with_file).except(:signature).merge(signature_param)
|
||||
def material_release_params_with_photos
|
||||
attributes_for(:material_release, :native, :with_photo).except(:signature).merge(signature_param)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ZoomMeetingsController, type: :controller do
|
||||
let(:user) { create(:user) }
|
||||
let(:account) { user.primary_account }
|
||||
let(:project) { create(:project, account: user.primary_account) }
|
||||
let(:broadcast) { create(:broadcast, name: "Broadcast", project: project) }
|
||||
|
||||
let(:meeting_start_url) { "http://meeting_start_url" }
|
||||
let(:meeting_hash) { HashWithIndifferentAccess.new(start_url: meeting_start_url) }
|
||||
let(:user_create_response) { {"id" => "new_host_id"} }
|
||||
let(:roles_assign_response) { {"ids" => ["new_host_id"]} }
|
||||
let(:roles_list_response) { {"roles" => [{"name" => "directme-host"}]} }
|
||||
|
||||
before :each do
|
||||
allow_any_instance_of(ZoomGateway).to receive(:find_meeting).and_return(meeting_hash)
|
||||
allow_any_instance_of(ZoomGateway).to receive(:create_meeting).and_return("meeting_id")
|
||||
allow_any_instance_of(ZoomGateway).to receive(:create_host).and_return("host_id")
|
||||
stub_mux_live_stream
|
||||
end
|
||||
|
||||
describe "#show" do
|
||||
before { sign_in user }
|
||||
|
||||
it "redirects to meeting start url" do
|
||||
get :show, params: { project_id: project.id, broadcast_id: broadcast.id }
|
||||
expect(response).to redirect_to(meeting_start_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,9 +5,5 @@ FactoryBot.define do
|
||||
asset_uid "asset_uid"
|
||||
asset_playback_uid "asset_playback_uid"
|
||||
hidden { false }
|
||||
|
||||
trait :with_random_asset_uid do
|
||||
sequence(:asset_uid, 'a')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,11 +2,16 @@ FactoryBot.define do
|
||||
factory :broadcast do
|
||||
association :project
|
||||
name "My Live Stream"
|
||||
conference_option "zoom"
|
||||
|
||||
transient do
|
||||
skip_create_callback false
|
||||
end
|
||||
|
||||
trait :ms_teams_conference do
|
||||
conference_option "ms_teams"
|
||||
end
|
||||
|
||||
trait :with_stream do
|
||||
stream_uid "mux_stream"
|
||||
stream_key "mux_key"
|
||||
|
||||
@@ -19,22 +19,6 @@ FactoryBot.define do
|
||||
amendment_clause "Amendment Legal Language"
|
||||
end
|
||||
|
||||
trait :with_questionnaire_legal_text do
|
||||
questionnaire_legal_text "Questionnaire Legal Text"
|
||||
end
|
||||
|
||||
trait :with_one_question do
|
||||
question_1_text "Is this a question?"
|
||||
end
|
||||
|
||||
trait :with_exhibits do
|
||||
exhibit_a_legal_text "Exhibit A legal text"
|
||||
exhibit_b_legal_text "Exhibit B legal text"
|
||||
|
||||
exhibit_a_question_text "Exhibit A question text"
|
||||
exhibit_b_question_text "Exhibit B question text"
|
||||
end
|
||||
|
||||
factory :appearance_release_contract_template do
|
||||
release_type "appearance"
|
||||
end
|
||||
|
||||
@@ -42,12 +42,6 @@ FactoryBot.define do
|
||||
end
|
||||
end
|
||||
|
||||
trait :with_file do
|
||||
files do
|
||||
path = Rails.root.join("spec", "fixtures", "files", "material_photo.png")
|
||||
[Rack::Test::UploadedFile.new(path, "image/png")]
|
||||
end
|
||||
end
|
||||
|
||||
trait :non_native do
|
||||
contract do
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
require "rails_helper"
|
||||
|
||||
feature "Guest account sign up" do
|
||||
scenario "guest can select ME Suite PRO in 'interested in' dropdown" do
|
||||
visit new_account_path
|
||||
|
||||
expect(page).to have_selector("img[src*='ME_PRO_black']")
|
||||
select "ME Suite PRO", from: interested_in_product_dropdown
|
||||
end
|
||||
|
||||
scenario "creates a new account and signs in successfully" do
|
||||
visit new_account_path
|
||||
|
||||
@@ -45,10 +38,4 @@ feature "Guest account sign up" do
|
||||
|
||||
expect(page).to have_content "Sign Up"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def interested_in_product_dropdown
|
||||
"user[interested_product_name]"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature 'User approving releasables' do
|
||||
shared_examples 'an approvable UI' do
|
||||
let(:current_user) { create(:user, :associate) }
|
||||
let(:project) { create(:project, members: current_user, account: current_user.primary_account) }
|
||||
|
||||
before :each do
|
||||
sign_in current_user
|
||||
end
|
||||
|
||||
shared_examples 'not authorized to review' do
|
||||
it 'does not show the review action' do
|
||||
visit polymorphic_path [project, subject.model_name.plural]
|
||||
click_on manage_button
|
||||
|
||||
expect(page).not_to have_link(review_action, exact: true)
|
||||
end
|
||||
end
|
||||
|
||||
scenario 'approval status is indicating by a checkmark' do
|
||||
approved_releasable = create("#{subject.model_name.singular}", approved_at: 1.day.ago, project: project)
|
||||
|
||||
visit polymorphic_path [project, subject.model_name.plural]
|
||||
|
||||
expect(page).not_to be_approved(subject)
|
||||
expect(page).not_to be_approved(approved_releasable)
|
||||
end
|
||||
|
||||
context 'as an account manager' do
|
||||
let(:current_user) { create(:user, :account_manager) }
|
||||
|
||||
scenario 'approving a release', js: true do
|
||||
visit polymorphic_path [project, subject.model_name.plural]
|
||||
|
||||
click_on manage_button
|
||||
click_link review_action
|
||||
|
||||
expect(page).to have_content review_page_heading(subject.model_name)
|
||||
expect(page).to have_content approve_button
|
||||
expect(page).to have_content signature_field
|
||||
|
||||
click_on approve_button
|
||||
|
||||
expect(page).not_to have_content approved_notice(subject.model_name)
|
||||
expect(page).to have_content 'is not attached'
|
||||
|
||||
by 'adding signature' do
|
||||
draw_signature file_fixture('signature.png'), signature_data_field(subject.model_name)
|
||||
click_on approve_button
|
||||
|
||||
expect(page).to have_content approved_notice(subject.model_name)
|
||||
end
|
||||
end
|
||||
|
||||
scenario 'viewing the contract PDF for an unapproved release' do
|
||||
visit polymorphic_path [project, subject.model_name.plural]
|
||||
click_on 'Manage'
|
||||
click_link 'Download'
|
||||
|
||||
expect(content_type).to eq('application/pdf')
|
||||
expect(content_disposition).to include('inline')
|
||||
expect(pdf_body).not_to have_content for_office_use_only
|
||||
end
|
||||
|
||||
scenario 'viewing the contract PDF of an approved release' do
|
||||
approver = create(:user, email: 'big.doe@test.com', first_name: 'Big', last_name: 'Joe')
|
||||
subject.approve_by(approver)
|
||||
subject.save!
|
||||
|
||||
visit polymorphic_path([subject, :contracts], format: 'pdf')
|
||||
|
||||
expect(content_type).to eq('application/pdf')
|
||||
expect(content_disposition).to include('inline')
|
||||
expect(pdf_body).to have_content for_office_use_only.upcase
|
||||
expect(pdf_body).to have_content producer_label
|
||||
expect(pdf_body).to have_content production_label
|
||||
expect(pdf_body).to have_content issued_to_label
|
||||
expect(pdf_body).to have_content issued_by_label
|
||||
expect(pdf_body).to have_content date_issued
|
||||
expect(pdf_body).to have_content 'Big Joe'
|
||||
end
|
||||
end
|
||||
|
||||
context 'as a manager' do
|
||||
let(:current_user) { create(:user, :manager) }
|
||||
|
||||
include_examples 'not authorized to review'
|
||||
end
|
||||
|
||||
context 'as an associate' do
|
||||
let(:current_user) { create(:user, :associate) }
|
||||
|
||||
include_examples 'not authorized to review'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def approve_button
|
||||
t 'approvals.new.actions.approve'
|
||||
end
|
||||
|
||||
def approved_notice(model_name)
|
||||
t('approvals.create.release_approved', release_type: model_name.human)
|
||||
end
|
||||
|
||||
def be_approved(releasable)
|
||||
releasable_dom_id = "##{releasable.model_name.singular}_#{releasable.id}"
|
||||
have_css("#{releasable_dom_id }i.fa.fa-check-circle.fa-2x", count: 1)
|
||||
end
|
||||
|
||||
def manage_button
|
||||
'Manage'
|
||||
end
|
||||
|
||||
def review_action
|
||||
'Review'
|
||||
end
|
||||
|
||||
def review_page_heading(model_name)
|
||||
t 'approvals.new.heading', release_type: model_name.human.titleize
|
||||
end
|
||||
|
||||
def signature_field
|
||||
'SIGNATURE'
|
||||
end
|
||||
|
||||
def signature_data_field(model_name)
|
||||
"#{model_name.singular}_approved_by_user_signature[data]"
|
||||
end
|
||||
|
||||
def for_office_use_only
|
||||
t('contracts.for_office_use_only.heading').upcase
|
||||
end
|
||||
|
||||
def producer_label
|
||||
t 'contracts.for_office_use_only.description_labels.producer'
|
||||
end
|
||||
|
||||
def production_label
|
||||
t 'contracts.for_office_use_only.description_labels.production'
|
||||
end
|
||||
|
||||
def issued_to_label
|
||||
t 'contracts.for_office_use_only.description_labels.issued_to'
|
||||
end
|
||||
|
||||
def issued_by_label
|
||||
t 'contracts.for_office_use_only.description_labels.issued_by'
|
||||
end
|
||||
|
||||
def date_issued
|
||||
t 'contracts.for_office_use_only.description_labels.date_issued'
|
||||
end
|
||||
end
|
||||
|
||||
context 'for an appearance release' do
|
||||
subject { create(:appearance_release_with_contract_template, :native, project: project) }
|
||||
it_behaves_like 'an approvable UI'
|
||||
end
|
||||
|
||||
context 'for a talent release' do
|
||||
subject { create(:talent_release_with_contract_template, :native, project: project) }
|
||||
it_behaves_like 'an approvable UI'
|
||||
end
|
||||
|
||||
context 'for a location release' do
|
||||
subject { create(:location_release_with_contract_template, :native, project: project) }
|
||||
it_behaves_like 'an approvable UI'
|
||||
end
|
||||
|
||||
context 'for a material release' do
|
||||
subject { create(:material_release_with_contract_template, :native, project: project) }
|
||||
it_behaves_like 'an approvable UI'
|
||||
end
|
||||
|
||||
context 'for a acquired media release' do
|
||||
subject { create(:acquired_media_release_with_contract_template, :native, project: project) }
|
||||
it_behaves_like 'an approvable UI'
|
||||
end
|
||||
|
||||
context 'for a medical release' do
|
||||
subject { create(:medical_release_with_contract_template, :native, project: project) }
|
||||
it_behaves_like 'an approvable UI'
|
||||
end
|
||||
|
||||
context 'for a misc release' do
|
||||
subject { create(:misc_release_with_contract_template, :native, project: project) }
|
||||
it_behaves_like 'an approvable UI'
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user