Compare commits

..

4 Commits

Author SHA1 Message Date
Bilal
1d29f14953 add conference options to the broadcasts; implement auth to microsoft 2020-08-20 13:09:10 +03:00
Bilal
4b5238435a structure 2020-08-20 12:01:41 +03:00
Senad Uka
41bf88e358 Upstream sync 2020-08-20 06:50:51 +02:00
Senad Uka
190ff2854b Upstream sync 2020-08-06 16:56:40 +00:00
157 changed files with 1938 additions and 617 deletions

View File

@@ -7,8 +7,6 @@ AWS_SECRET_ACCESS_KEY=
AWS_REGION=
BRAYNIAC_AI_API_ENDPOINT=https://z99cprg2eg.execute-api.us-east-1.amazonaws.com/dev/v0.0.1
SOURCE_AUDIO_API_ENDPOINT=https://bigmedia.sourceaudio.com/api
SOURCE_AUDIO_TOKEN=
# Optional
REDIS_URL=
@@ -16,7 +14,6 @@ REDIS_URL=
# Required for Zoom.us integration
ZOOM_API_KEY=
ZOOM_API_SECRET=
ZOOM_ACCOUNT_NUMBER=
ZOOM_PRO_USERS_LIMIT= # defaults to 3
ZOOM_USER_TYPE= # 'pro' / 'basic'
ZOOM_ENABLE_RECORDINGS= # true / false (default: false)
@@ -28,3 +25,17 @@ MUX_TOKEN_ID=
MUX_TOKEN_SECRET=
MUX_BROADCAST_SERVER_URL=rtmp://global-live.mux.com:5222/app
MUX_TEST_MODE_DISABLED=
# Required for creating user through API
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'

View File

@@ -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]

View File

@@ -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)

View File

@@ -18,7 +18,6 @@ class AcquiredMediaReleasesController < ApplicationController
@acquired_media_release = build_acquired_media_release(acquired_media_release_params)
if @acquired_media_release.save
log_create_analytics
SetTagsForReleasableJob.perform_later(@acquired_media_release)
redirect_to [@project, :acquired_media_releases], notice: t(".notice")
else
@@ -119,11 +118,8 @@ class AcquiredMediaReleasesController < ApplicationController
:term_id, :term_text,
:restriction_id, :restriction_text,
categories: [],
file_infos_attributes: %i[
filename
content_type
byte_size
])
files: []
)
end
def build_acquired_media_release(attrs = {})
@@ -139,8 +135,4 @@ class AcquiredMediaReleasesController < ApplicationController
results
end
def log_create_analytics
TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_non_native_release, release_type: AcquiredMediaRelease.to_s, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -19,6 +19,11 @@ class Admin::BroadcastsController < Admin::ApplicationController
end
def broadcast_update_params
params.require(:broadcast).permit(:stream_url_override, :stream_key_override, :director_mode_video_embed)
params.require(:broadcast).permit(
:video_conference_url_override,
:stream_url_override,
:stream_key_override,
:director_mode_video_embed
)
end
end

View File

@@ -21,7 +21,6 @@ class Api::ReleasesController < Api::ApiController
release.contract_template_id = @contract_template.id
handle_attachments(release, release_create_params[:photos])
release.save!(context: :native)
log_create_analytics
after_create(release)
handle_response(release, :created)
end
@@ -181,8 +180,4 @@ class Api::ReleasesController < Api::ApiController
parameters = parameters.slice(*keys).except(:created_at, :updated_at, :id, :user_id, :signature)
parameters
end
def log_create_analytics
TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_native_release, release_type: model_constant.to_s, account: @account, user_agent: request.user_agent, user_ip: request.remote_ip, application: :ios)
end
end

View File

@@ -1,5 +1,12 @@
# frozen_string_literal: true
require './lib/knock_monkeypatch'
class Api::UserTokenController < Knock::AuthTokenController
include Oath::ControllerHelpers
skip_before_action :verify_authenticity_token
before_action :sign_in_user
rescue_from Exception, :with => :return_error
@@ -10,7 +17,7 @@ class Api::UserTokenController < Knock::AuthTokenController
logger.error "==Handled======="
logger.error exception.message
logger.error exception.backtrace.join("\n")
logger.error "==Handled======="
logger.error "==Handled======="
case exception
when ActiveRecord::RecordNotFound
@status = 404
@@ -27,12 +34,18 @@ class Api::UserTokenController < Knock::AuthTokenController
end
# for some reason render json_errors is not working
# simulating JSON API support
render json: {
# simulating JSON API support
render json: {
errors: [{
status: @status.to_s,
title: @message
}]
}
end
private
def sign_in_user
sign_in(entity)
end
end

View File

@@ -0,0 +1,33 @@
# frozen_string_literal: true
class Api::UsersController < Api::ApiController
skip_before_action :authenticate_user
before_action :verify_custom_token, only: :create
def create
if user_params[:email].nil? || user_params[:password].nil?
raise ActionController::ParameterMissing.new 'Missing email or password'
end
user = Oath::Services::SignUp.new(user_params).perform
render json: user.slice(:email, :created_at, :first_name, :last_name)
end
private
def user_params
params.require(:user).permit(%i[
email
password
first_name
last_name
])
end
def verify_custom_token
if token.blank? || token != ENV['CUSTOM_API_TOKEN']
unauthorized_entity(:user)
end
end
end

View File

@@ -18,7 +18,6 @@ class AppearanceReleasesController < ApplicationController
@appearance_release = build_appearance_release(appearance_release_params)
if @appearance_release.save(context: :non_native)
log_create_analytics
AddHeadshotCollectionUidToProjectJob.perform_later(@project)
SetTagsForReleasableJob.perform_later(@appearance_release)
redirect_to [@project, :appearance_releases], notice: "The release has been imported. #{link_to_import_another}"
@@ -127,8 +126,4 @@ class AppearanceReleasesController < ApplicationController
def link_to_import_another
view_context.link_to "Import Another", [:new, @project, :appearance_release]
end
def log_create_analytics
TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_non_native_release, release_type: AppearanceRelease.to_s, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -23,10 +23,13 @@ class BroadcastsController < ApplicationController
else
render :new
end
rescue MuxRuby::ApiError, ActiveResource::ConnectionError => e
Raven.capture_exception(e)
redirect_to [@project, :broadcasts], alert: t(".alert")
end
def show
@conference_url = url_for [@broadcast.project, @broadcast, :zoom_meeting]
@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'
@@ -72,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
@@ -109,6 +112,10 @@ class BroadcastsController < ApplicationController
results
end
def conference_url_for(broadcast)
broadcast.video_conference_url_override || url_for([broadcast.project, broadcast, :conference_meeting])
end
def log_create_analytics
TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_live_stream, user_agent: request.user_agent, user_ip: request.remote_ip)
end

View 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

View 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

View File

@@ -26,6 +26,7 @@ class ContractTemplatesController < ApplicationController
contract = Contract.new releasable_instance, true
send_file contract.to_pdf, download_attributes
elsif @contract_template.save
log_create_analytics
redirect_to [@project, :contract_templates], notice: t('.notice')
else
@release_type = contract_template_params[:release_type]
@@ -94,7 +95,9 @@ class ContractTemplatesController < ApplicationController
:question_9_text, :question_10_text,
:question_11_text, :question_12_text,
:question_13_text, :question_14_text,
:question_15_text)
:question_15_text, :questionnaire_legal_text,
:exhibit_a_legal_text, :exhibit_a_question_text,
:exhibit_b_legal_text, :exhibit_b_question_text)
end
def download_attributes
@@ -104,4 +107,8 @@ class ContractTemplatesController < ApplicationController
type: 'application/pdf'
}
end
def log_create_analytics
TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_contract_template, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -1,4 +1,4 @@
class FileInfosController < ApplicationController
class FilesController < ApplicationController
before_action :set_releasable
layout "project"
@@ -16,7 +16,7 @@ class FileInfosController < ApplicationController
if @releasable.update(releasable_params)
SetTagsForReleasableJob.perform_later(@releasable)
redirect_to [@project, @releasable.model_name.plural], notice: t(".notice")
redirect_to [@project, @releasable.model_name.plural], notice: "Files added successfully to the release."
else
render :edit
end
@@ -29,16 +29,10 @@ class FileInfosController < ApplicationController
end
def set_releasable
@releasable = authorize policy_scope(releasable_param.type).find(releasable_param.id), :"#{action_name}_file_infos?"
@releasable = authorize policy_scope(releasable_param.type).find(releasable_param.id), :edit_files?
end
def releasable_params
params.fetch(releasable_param.name, {}).permit(
file_infos_attributes: [
:filename,
:content_type,
:byte_size
],
)
params.fetch(releasable_param.name, {}).permit(files: [])
end
end

View File

@@ -18,7 +18,6 @@ class LocationReleasesController < ApplicationController
@location_release = build_location_release(location_release_params)
if @location_release.save
log_create_analytics
SetTagsForReleasableJob.perform_later(@location_release)
redirect_to [@project, :location_releases], notice: t(".notice")
else
@@ -90,8 +89,4 @@ class LocationReleasesController < ApplicationController
results
end
def log_create_analytics
TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_non_native_release, release_type: LocationRelease.to_s, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -18,7 +18,6 @@ class MaterialReleasesController < ApplicationController
@material_release = build_material_release(material_release_params)
if @material_release.save
log_create_analytics
SetTagsForReleasableJob.perform_later(@material_release)
redirect_to [@project, :material_releases], notice: t(".notice")
else
@@ -136,8 +135,4 @@ class MaterialReleasesController < ApplicationController
results
end
def log_create_analytics
TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_non_native_release, release_type: MaterialRelease.to_s, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -22,7 +22,6 @@ class MusicReleasesController < ApplicationController
@music_release = build_music_release(music_release_params)
if @music_release.save
log_create_analytics
SetTagsForReleasableJob.perform_later(@music_release)
redirect_to [@project, :music_releases], notice: t(".notice")
else
@@ -111,8 +110,4 @@ class MusicReleasesController < ApplicationController
results
end
def log_create_analytics
TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_non_native_release, release_type: MusicRelease.to_s, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -6,7 +6,7 @@ class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
def index
@projects = policy_scope(Current.account.projects).order_by_name
@projects = filtered_projects.order_by_name
end
def new
@@ -46,6 +46,20 @@ class ProjectsController < ApplicationController
private
def filtered_projects
results = projects
if params[:query].present?
results = results.search(params[:query])
end
results
end
def projects
authorize policy_scope(Current.account.projects)
end
def set_project
@project = authorize projects.find(params[:id])
end

View File

@@ -10,7 +10,6 @@ class Public::AcquiredMediaReleasesController < Public::BaseController
@acquired_media_release = build_acquired_media_release(acquired_media_release_params_with_locale_and_contract_template)
if @acquired_media_release.save(context: :native)
log_create_analytics
after_create(@acquired_media_release)
else
render :new
@@ -100,11 +99,8 @@ class Public::AcquiredMediaReleasesController < Public::BaseController
:signature_base64,
:locale, :contract_template,
categories: [],
file_infos_attributes: %i[
filename
content_type
byte_size
])
files: []
)
end
def acquired_media_release_params_with_locale
@@ -114,8 +110,4 @@ class Public::AcquiredMediaReleasesController < Public::BaseController
def acquired_media_release_params_with_locale_and_contract_template
acquired_media_release_params_with_locale.merge(contract_template: @contract_template)
end
def log_create_analytics
TrackAnalyticsJob.perform_later(nil, nil, :track_create_native_release, release_type: AcquiredMediaRelease.to_s, account: @account, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -10,7 +10,6 @@ class Public::AppearanceReleasesController < Public::BaseController
@appearance_release = build_appearance_release(appearance_release_params_with_locale_and_contract_template)
if @appearance_release.save(context: :native)
log_create_analytics
after_create(@appearance_release)
else
render :new
@@ -87,12 +86,35 @@ class Public::AppearanceReleasesController < Public::BaseController
]
end
def questionnaire_params
[
:question_1_answer,
:question_2_answer,
:question_3_answer,
:question_4_answer,
:question_5_answer,
:question_6_answer,
:question_7_answer,
:question_8_answer,
:question_9_answer,
:question_10_answer,
:question_11_answer,
:question_12_answer,
:question_13_answer,
:question_14_answer,
:question_15_answer,
]
end
def appearance_release_params
params.require(:appearance_release).permit(person_params, guardian_params,
second_guardian_params,
questionnaire_params,
:minor, :signature_base64,
:person_date_of_birth,
:locale, :contract_template)
:locale, :contract_template,
:exhibit_a_answer, :exhibit_b_answer
)
end
def appearance_release_params_with_locale
@@ -102,8 +124,4 @@ class Public::AppearanceReleasesController < Public::BaseController
def appearance_release_params_with_locale_and_contract_template
appearance_release_params_with_locale.merge(contract_template: @contract_template)
end
def log_create_analytics
TrackAnalyticsJob.perform_later(nil, nil, :track_create_native_release, release_type: AppearanceRelease.to_s, account: @account, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -3,7 +3,7 @@ class Public::BroadcastsController < Public::BaseController
before_action :set_broadcast, only: [:show, :update]
def show
@conference_url = broadcast_zoom_meeting_url(@broadcast.token)
@conference_url = conference_url_for(@broadcast)
@multi_view_broadcasts = multi_view_broadcasts
@recordings = @broadcast.broadcast_recordings.visible.order_by_recent.paginate(page: params[:page])
@files = @broadcast.files.order("created_at DESC").paginate(page: params[:files_page])
@@ -43,6 +43,10 @@ class Public::BroadcastsController < Public::BaseController
end
end
def conference_url_for(broadcast)
broadcast.video_conference_url_override || broadcast_zoom_meeting_url(broadcast.token)
end
class MultiViewBroadcast
include Rails.application.routes.url_helpers

View File

@@ -10,7 +10,6 @@ class Public::LocationReleasesController < Public::BaseController
@location_release = build_location_release(location_release_params_with_locale_and_contract_template)
if @location_release.save(context: :native)
log_create_analytics
after_create(@location_release)
else
render :new
@@ -76,8 +75,4 @@ class Public::LocationReleasesController < Public::BaseController
def location_release_params_with_locale_and_contract_template
location_release_params_with_locale.merge(contract_template: @contract_template)
end
def log_create_analytics
TrackAnalyticsJob.perform_later(nil, nil, :track_create_native_release, release_type: LocationRelease.to_s, account: @account, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -10,7 +10,6 @@ class Public::MaterialReleasesController < Public::BaseController
@material_release = build_material_release(material_release_params_with_locale_and_contract_template)
if @material_release.save(context: :native)
log_create_analytics
after_create(@material_release)
else
render :new
@@ -104,8 +103,4 @@ class Public::MaterialReleasesController < Public::BaseController
def material_release_params_with_locale_and_contract_template
material_release_params_with_locale.merge(contract_template: @contract_template)
end
def log_create_analytics
TrackAnalyticsJob.perform_later(nil, nil, :track_create_native_release, release_type: MaterialRelease.to_s, account: @account, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -12,7 +12,6 @@ class Public::MedicalReleasesController < Public::BaseController
if @medical_release.contract_template.present?
AttachContractToReleasableJob.perform_later(@medical_release)
end
log_create_analytics
else
render :new
end
@@ -116,8 +115,4 @@ class Public::MedicalReleasesController < Public::BaseController
def medical_release_params_with_locale_and_contract_template
medical_release_params_with_locale.merge(contract_template: @contract_template)
end
def log_create_analytics
TrackAnalyticsJob.perform_later(nil, nil, :track_create_native_release, release_type: MedicalRelease.to_s, account: @account, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -12,7 +12,6 @@ class Public::MiscReleasesController < Public::BaseController
if @misc_release.contract_template.present?
AttachContractToReleasableJob.perform_later(@misc_release)
end
log_create_analytics
else
render :new
end
@@ -50,6 +49,14 @@ class Public::MiscReleasesController < Public::BaseController
:signature_base64,
:locale,
:contract_template,
:applicable_medium_id,
:applicable_medium_text,
:territory_id,
:territory_text,
:term_id,
:term_text,
:restriction_id,
:restriction_text,
photos: [],
)
end
@@ -113,8 +120,4 @@ class Public::MiscReleasesController < Public::BaseController
def misc_release_params_with_locale_and_contract_template
misc_release_params_with_locale.merge(contract_template: @contract_template)
end
def log_create_analytics
TrackAnalyticsJob.perform_later(nil, nil, :track_create_native_release, release_type: MiscRelease.to_s, account: @account, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -10,7 +10,6 @@ class Public::TalentReleasesController < Public::BaseController
@talent_release = build_talent_release(talent_release_params_with_locale_and_contract_template)
if @talent_release.save(context: :native)
log_create_analytics
after_create(@talent_release)
else
render :new
@@ -108,8 +107,4 @@ class Public::TalentReleasesController < Public::BaseController
def talent_release_params_with_locale_and_contract_template
talent_release_params_with_locale.merge(contract_template: @contract_template)
end
def log_create_analytics
TrackAnalyticsJob.perform_later(nil, nil, :track_create_native_release, release_type: TalentRelease.to_s, account: @account, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -18,7 +18,6 @@ class TalentReleasesController < ApplicationController
@talent_release = build_talent_release(talent_release_params)
if @talent_release.save
log_create_analytics
AddHeadshotCollectionUidToProjectJob.perform_later(@project)
SetTagsForReleasableJob.perform_later(@talent_release)
redirect_to [@project, :talent_releases], notice: t(".notice")
@@ -137,8 +136,4 @@ class TalentReleasesController < ApplicationController
results
end
def log_create_analytics
TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_non_native_release, release_type: TalentRelease.to_s, user_agent: request.user_agent, user_ip: request.remote_ip)
end
end

View File

@@ -1,6 +0,0 @@
class ZoomMeetingsController < ApplicationController
def show
authorize broadcast = Broadcast.find(params[:broadcast_id])
redirect_to broadcast.zoom_meeting_url
end
end

View 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

View File

@@ -6,6 +6,6 @@ module MailHelper
"1. Tell us the name of the video: \n\n" \
"2. Attach each EDL to this email. Please make sure to indicate whether the EDL is a Video Only, Graphics Only, or Audio Only EDL."
mail_to "info@bigmedia.ai", content, subject: subject, body: body
mail_to "info@mesuite.ai", content, subject: subject, body: body
end
end

View File

@@ -67,6 +67,23 @@ class ActiveStorageDropzone {
var upload = new that.UploaderClass(file, url, delegate);
createUpload(this, upload);
});
this.on("addedfile", function(file) {
// Show download link in dropzone previews
let div = document.createElement("div");
let anchor = document.createElement("a");
let download_icon = "<i class='fa fa-download' aria-hidden='true'></i> Download";
anchor.innerHTML = download_icon;
anchor.href = file.dataURL;
anchor.setAttribute('target', '_blank');
anchor.setAttribute('style', 'background-color: rgba(255, 255, 255, 0.4); padding: 0 0.4em; border: 1px solid transparent; text-decoration: none;');
div.append(anchor);
div.setAttribute('class', 'dz-download-link')
div.setAttribute('style', 'margin-top: 1em;')
$(file.previewElement.childNodes[3]).append(div);
});
},
});

View File

@@ -1,5 +1,5 @@
class AdminMailer < ApplicationMailer
default to: %w(bray@bigmedia.ai lee@bigmedia.ai)
default to: %w[bray@mesuite.ai]
def new_video(video)
@video = video

View File

@@ -1,4 +1,4 @@
class ApplicationMailer < ActionMailer::Base
default from: ENV.fetch("MAILER_FROM_ADDRESS") { "support@bigmedia.ai" }
default from: ENV.fetch("MAILER_FROM_ADDRESS") { "support@mesuite.ai" }
layout "mailer"
end

View File

@@ -64,6 +64,19 @@ class Account < ApplicationRecord
])).sum(:byte_size).to_f
end
def total_number_of_releases
[
MiscRelease.where(project: projects).size,
AppearanceRelease.where(project: projects).size,
TalentRelease.where(project: projects).size,
MaterialRelease.where(project: projects).size,
MedicalRelease.where(project: projects).size,
LocationRelease.where(project: projects).size,
AcquiredMediaRelease.where(project: projects).size,
MusicRelease.where(project: projects).size
].sum
end
def to_param
slug
end
@@ -85,7 +98,7 @@ class Account < ApplicationRecord
end
def taskme_enabled?
ENV["TASKME_ENABLED"] && (plan_uid.to_s == "me_suite" || plan_uid.to_s == "taskme")
plan_uid.to_s == "me_suite" || plan_uid.to_s == "taskme"
end
def plan_name

View File

@@ -18,13 +18,14 @@ class AcquiredMediaRelease < ApplicationRecord
class << self
def custom_csv_exportable_headers
%i[name file_infos_count]
%i[name files_count owner_info]
end
end
# This association needs to be removed after changing the API. Removing it right now will cause failure in API specs.
has_many :file_infos, as: :releasable, dependent: :destroy
accepts_nested_attributes_for :file_infos
has_many_attached :files
composed_of :person_address,
class_name: "Address",
@@ -102,7 +103,19 @@ class AcquiredMediaRelease < ApplicationRecord
true
end
def file_infos_count
file_infos.any? ? file_infos.size : I18n.t('acquired_media_releases.acquired_media_release.no_media')
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

View File

@@ -40,7 +40,7 @@ class AppHost
use_ssl: false,
},
production: {
host: "bigmedia.ai",
host: "mesuite.ai",
use_ssl: true,
}
}

View File

@@ -19,6 +19,8 @@ class AppearanceRelease < ApplicationRecord
include Approvable
include Amendmenable
NUMBER_OF_CUSTOM_FIELDS = 15
class << self
def custom_csv_exportable_headers
%i[name contact_info]

View File

@@ -49,11 +49,13 @@ class Broadcast < ApplicationRecord
private
def create_mux_live_stream
stream = MuxLiveStream.new
stream = MuxLiveStream.create_with_simulcast
self.stream_uid = stream.id
self.stream_key = stream.key
self.stream_playback_uid = stream.playback_id
self.simulcast_uid = stream.simulcast_id
self.director_mode_video_embed ||= stream.simulcast_destination.try(:playback_embed)
self.save!
end

View File

@@ -3,8 +3,8 @@
module CsvExportable
extend ActiveSupport::Concern
COMMON_HEADERS = %i[approved? notes tags signed_at].freeze
COMMON_VALUES = %w[clean_notes clean_tags signed_on].freeze
COMMON_HEADERS = %i[approved notes tags signed_at].freeze
COMMON_VALUES = %w[approved? clean_notes clean_tags signed_on].freeze
included do
class << self
@@ -29,11 +29,20 @@ module CsvExportable
private
def owner_info
compact_contact_info(name: person_name, address: person_address, phone: person_phone, email: person_email)
end
def contact_info
owner_info
end
def compact_contact_info(name: nil, address: nil, phone: nil, email: nil)
contact_info = ''
contact_info += "#{person_address}; " if person_address.present?
contact_info += "P: #{person_phone}; " if person_phone.present?
contact_info += "E: #{person_email}" if person_email.present?
contact_info += "#{name}; " if name.present?
contact_info += "#{address}; " if address.present?
contact_info += "P: #{phone}; " if phone.present?
contact_info += "E: #{email}" if email.present?
contact_info.delete_suffix '; '
end

View File

@@ -23,6 +23,9 @@ class ContractTemplate < ApplicationRecord
has_rich_text :guardian_clause
has_rich_text :signature_legal_text
has_rich_text :amendment_clause
has_rich_text :exhibit_a_legal_text
has_rich_text :exhibit_b_legal_text
has_rich_text :questionnaire_legal_text
validates :name, presence: true
validates :release_type, presence: true
@@ -70,6 +73,14 @@ class ContractTemplate < ApplicationRecord
(1..NUMBER_OF_CUSTOM_FIELDS).any? { |n| public_send("question_#{n}_text").presence }
end
def has_exhibit_a?
exhibit_a_legal_text.present?
end
def has_exhibit_b?
exhibit_b_legal_text.present?
end
def editable?
releases.size.zero?
end

View File

@@ -16,7 +16,7 @@ class LocationRelease < ApplicationRecord
class << self
def custom_csv_exportable_headers
%i[name address]
%i[location_info owner_info amendment_signed_column]
end
end
@@ -67,6 +67,18 @@ class LocationRelease < ApplicationRecord
true
end
def location_info
compact_contact_info(name: name, address: address)
end
def amendment_signed_column
if amendment_signable?
amendment_signed?
else
I18n.t('location_releases.csv.no_amendment_clause')
end
end
private
def end_date_after_start_date

View File

@@ -20,7 +20,7 @@ class MaterialRelease < ApplicationRecord
class << self
def custom_csv_exportable_headers
%i[name]
%i[name owner_info]
end
end

View File

@@ -0,0 +1,41 @@
class MillicastDestination
attr_reader :name, :token
def self.create
token_stream_name = SecureRandom.urlsafe_base64
publish_token = Millicast::PublishToken.create(
label: SecureRandom.urlsafe_base64,
streams: [
{ streamName: token_stream_name }
]
)
new(token_stream_name, publish_token.data.token)
end
def initialize(name, token)
@name = name
@token = token
end
def account_id
ENV["MILLICAST_ACCOUNT_ID"]
end
def key
"#{name}?token=#{token}"
end
def url
"rtmp://live-rtmp-pub.millicast.com:1935/v2/pub"
end
def playback_url
"https://viewer.millicast.com/v2?streamId=#{account_id}/#{name}"
end
def playback_embed
"<iframe src=\"#{playback_url}\" allowfullscreen width=\"640\" height=\"480\"></iframe>"
end
end

View File

@@ -11,6 +11,7 @@ class MiscRelease < ApplicationRecord
include GuardianPhotoable
include CsvExportable
include Approvable
include Exploitable
class << self
def custom_csv_exportable_headers

View File

@@ -1,4 +1,15 @@
class MuxLiveStream
attr_accessor :simulcast_id, :simulcast_destination
def self.create_with_simulcast(destination = nil)
destination ||= MillicastDestination.create
MuxLiveStream.new.tap do |stream|
stream.id # force the live stream to be created by calling for its id
stream.create_simulcast(destination)
end
end
def id
live_stream.data.id
end
@@ -15,6 +26,15 @@ class MuxLiveStream
client.delete_live_stream(stream_uid)
end
def create_simulcast(destination)
return if test_mode_enabled?
request = MuxRuby::CreateSimulcastTargetRequest.new(stream_key: destination.key, url: destination.url)
result = client.create_live_stream_simulcast_target(id, request)
self.simulcast_destination = destination
self.simulcast_id = result.data.id
end
private
def live_stream

View File

@@ -2,6 +2,7 @@ class Project < ApplicationRecord
include Archivable
include Filterable
include Syncable
include PgSearch
SIGNABLE_RELEASE_TYPES = %w(talent appearance acquired_media location material medical misc)
AVAILABLE_RELEASE_TYPES = %w(appearance location material acquired_media talent music medical misc)
@@ -42,6 +43,15 @@ class Project < ApplicationRecord
}
end
pg_search_scope :search, {
against: [:name],
using: {
tsearch: {any_word: true, prefix: true},
trigram: {},
dmetaphone: {any_word: true}
}
}
validates :name, presence: true, uniqueness: { scope: :account_id }
filterable_by :active, :inactive, :archived

View File

@@ -19,11 +19,7 @@ class AcquiredMediaReleasePolicy < ApplicationPolicy
true
end
def edit_file_infos?
true
end
def update_file_infos?
def edit_files?
true
end

View File

@@ -16,5 +16,5 @@ class SerializableAcquiredMediaRelease < JSONAPI::Serializable::Resource
meta do
{ count: @object.file_infos.size }
end
end
end
end

View File

@@ -169,6 +169,24 @@ class Analytics
)
end
end
def track_create_contract_template(user_agent:, user_ip:)
if analytics_enabled?
identify
track(
{
user_id: user.id,
event: "Contract Template Created",
properties: {
account: account.try(:name),
account_id: account.try(:id),
user_agent: user_agent,
ip: user_ip,
},
}
)
end
end
private

View File

@@ -1,4 +1,4 @@
<%= bootstrap_form_with model: account, url: account_path, html: { autocorrect: :off, autocapitalize: :none, autocomplete: :off, spellcheck: false }, layout: :inline, remote: true do |form| %>
<%= form.file_field :logo, hide_label: true, accept: "image/*", placeholder: "Upload Logo", direct_upload: true, wrapper_class: "mr-1" %>
<%= form.file_field :logo, hide_label: true, accept: "image/*", placeholder: "Upload Logo", direct_upload: true, wrapper_class: "mr-1", required: true %>
<%= form.button(fa_icon("upload", text: t(".submit")), class: "btn btn-md btn-primary", data: { disable_with: t("shared.disable_with") }) %>
<% end %>

View File

@@ -12,12 +12,15 @@
<%= acquired_media_release.name %>
</td>
<td>
<% if acquired_media_release.file_infos.any? %>
<%= acquired_media_release.file_infos.size %>
<% if acquired_media_release.files.any? %>
<%= acquired_media_release.files.size %>
<% else %>
<%= fa_icon("warning", text: t(".no_media"), class: "text-danger") %>
<% end %>
</td>
<td>
<%= contact_info_for(acquired_media_release.contact_person) %>
</td>
<td>
<%= notes_preview acquired_media_release.notes.order_by_recent %>
</td>
@@ -37,8 +40,8 @@
<% if policy(acquired_media_release.tags).new? %>
<%= link_to fa_icon("tags fw", text: "Tags"), [:new, acquired_media_release, :acts_as_taggable_on_tag], class: "dropdown-item", remote: true %>
<% end %>
<% if policy(acquired_media_release).edit_file_infos? %>
<%= link_to fa_icon("file fw", text: "Add Media"), [:edit, acquired_media_release, :file_infos], class: "dropdown-item" %>
<% if policy(acquired_media_release).edit_files? %>
<%= link_to fa_icon("file fw", text: "Media"), [:edit, acquired_media_release, :files], class: "dropdown-item" %>
<% end %>
<% if policy(Contract).show? && (acquired_media_release.contract.attached? || acquired_media_release.contract_template.present?) %>
<%= link_to fa_icon("download fw", text: "Download"), [acquired_media_release, :contracts, format: "pdf"], class: "dropdown-item", target: "_blank" %>

View File

@@ -44,7 +44,7 @@
<%= fa_icon "warning" %>
<strong>For optimal accuracy, please ensure video file names and photo file names match the source file name in the editing sequence.</strong>
</div>
<%= render "shared/file_infos_dropzone", form: form, releasable: acquired_media_release %>
<%= render "shared/releasable_files_dropzone", form: form, releasable: acquired_media_release %>
<div class="<%= class_string("collapse" => !acquired_media_release.minor?) %>" data-ujs-target="guardian-fields">
<br>

View File

@@ -28,7 +28,8 @@
<th data-behavior="all-selectable"><%= check_box_tag "acquired_media_release_ids[]", false, false %></th>
<th><%= t '.table_headers.approved'%></th>
<th><%= AcquiredMediaRelease.human_attribute_name(:name) %></th>
<th><%= t(".table_headers.file_infos_count") %></th>
<th><%= t(".table_headers.files_count") %></th>
<th><%= t(".table_headers.owner_info") %></th>
<th><%= t(".table_headers.notes") %></th>
<th><%= t(".table_headers.tags") %></th>
<th><%= t(".table_headers.signed_at") %></th>

View File

@@ -8,6 +8,9 @@
<td>
<%= account.projects.size %>
</td>
<td>
<%= account.total_number_of_releases %>
</td>
<td>
<%= number_with_delimiter convert_duration(account.current_month_video_duration_total, from: :seconds, to: :minutes).round %> minutes
</td>

View File

@@ -15,6 +15,7 @@
<th>Name</th>
<th>Plan</th>
<th># Projects</th>
<th># Releases Signed</th>
<th>Monthly Video Upload Minutes</th>
<th>Total Video Upload Minutes</th>
<th>Total Storage</th>

View File

@@ -9,6 +9,8 @@
<dd class="col-sm-10"><%= @account.users.size %></dd>
<dt class="col-sm-2">Created at</dt>
<dd class="col-sm-10"><%= time_ago_in_words(@account.created_at) %> ago</dd>
<dt class="col-sm-2"># of Releases</dt>
<dd class="col-sm-10"><%= @account.total_number_of_releases %></dd>
</dl>
<% end %>

View File

@@ -1,6 +1,7 @@
<%= errors_summary_for broadcast %>
<%= bootstrap_form_with model: model, local: true do |form| %>
<%= form.text_field :video_conference_url_override %>
<%= form.text_field :stream_url_override %>
<%= form.text_field :stream_key_override %>
<%= form.text_area :director_mode_video_embed %>

View File

@@ -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">

View File

@@ -1,9 +1,9 @@
<% if broadcast.streamer_recording? && broadcast.active? %>
<div id="broadcast_video" class="embed-responsive-item" data-video-type="stream"></div>
<% elsif broadcast.director_mode_video_embed.present? && params[:director_mode] == "true" %>
<% if broadcast.director_mode_video_embed.present? && params[:director_mode].present? %>
<div class="embed-responsive-item" data-video-type="stream">
<%= raw broadcast.director_mode_video_embed %>
</div>
<% elsif broadcast.streamer_recording? && broadcast.active? %>
<div id="broadcast_video" class="embed-responsive-item" data-video-type="stream"></div>
<% else %>
<div id="broadcast_video" class="embed-responsive-item" data-video-type="stream">
<table class="w-100 h-100 bg-secondary">

View File

@@ -127,8 +127,8 @@
</div>
<hr>
<% end %>
<p class="card-text">If you want to join the ZOOM meeting dedicated to this broadcast, follow the link below.</p>
<%= link_to 'Video Conference', @conference_url, class: 'btn btn-primary btn-block', target: '_blank' %>
<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 class="<%= class_string("tab-pane fade show", "active" => params[:active_tab] == 'recordings') %>" id="recordings">
<div id="broadcast_recordings">

View File

@@ -2,7 +2,7 @@
<%= field_set_tag content_tag(:span, t(".release_info.heading"), class: "h6 text-muted text-uppercase") do %>
<div class="form-row">
<%= form.text_field :name, wrapper_class: "col-sm-6" %>
<%= form.select :release_type, options_for_release_type_select(project, @release_type), { wrapper_class: "col-sm-6" }, data: { toggle: "collapse-select", target_show_values_mapping: { "#guardian_clause": %w(acquired_media appearance talent material misc medical), "#fee_field": %w(appearance talent location material acquired_media), "#exploitable_rights_fields": %w(appearance talent location material acquired_media), "#custom_fields": %w(medical misc), "#amendment_clause": %w(appearance location) } }, class: "form-control custom-select" %>
<%= form.select :release_type, options_for_release_type_select(project, @release_type), { wrapper_class: "col-sm-6" }, data: { toggle: "collapse-select", target_show_values_mapping: { "#guardian_clause": %w(acquired_media appearance talent material misc medical), "#fee_field": %w(appearance talent location material acquired_media), "#exploitable_rights_fields": %w(appearance talent location material acquired_media misc), "#custom_fields": %w(medical misc appearance), "#amendment_clause": %w(appearance location), "#exhibit_fields": %w(appearance) } }, class: "form-control custom-select" %>
</div>
<div class="form-row mb-3">
<%= form.radio_button :accessibility, :public_template, label: "Public", wrapper_class: "mr-3" %>
@@ -34,6 +34,25 @@
<%= form.rich_text_area :amendment_clause %>
<% end %>
</div>
<div id="exhibit_fields">
<%= form.form_group do %>
<%= form.rich_text_area :exhibit_a_legal_text %>
<small class="form-text text-muted mb-4"><%= t(".exhibits.help.option_field")%></small>
<% end %>
<%= form.form_group do %>
<%= form.text_area :exhibit_a_question_text %>
<small class="form-text text-muted mb-4"><%= t(".exhibits.help.option_field")%></small>
<% end %>
<%= form.form_group do %>
<%= form.rich_text_area :exhibit_b_legal_text %>
<small class="form-text text-muted mb-4"><%= t(".exhibits.help.option_field")%></small>
<% end %>
<%= form.form_group do %>
<%= form.text_area :exhibit_b_question_text %>
<small class="form-text text-muted mb-4"><%= t(".exhibits.help.option_field")%></small>
<% end %>
</div>
<div id="signature_legal_text">
<%= form.form_group do %>
<%= form.rich_text_area :signature_legal_text %>

View File

@@ -0,0 +1,8 @@
<p class="heading"><strong><u><%= t ".heading.#{releasable.model_name.param_key}" %></u></strong></p>
<h4>Legal</h4>
<p><%= releasable.contract_template.exhibit_a_legal_text %></p>
<% if releasable.contract_template.exhibit_a_question_text.present? %>
<h4>Question</h4>
<p><strong><%= releasable.contract_template.exhibit_a_question_text %></strong></p>
<p><%= releasable.exhibit_a_answer %></p>
<% end %>

View File

@@ -0,0 +1,8 @@
<p class="heading"><strong><u><%= t ".heading.#{releasable.model_name.param_key}" %></u></strong></p>
<h4>Legal</h4>
<p><%= releasable.contract_template.exhibit_b_legal_text %></p>
<% if releasable.contract_template.exhibit_b_question_text.present? %>
<h4>Question</h4>
<p><strong><%= releasable.contract_template.exhibit_b_question_text %></strong></p>
<p><%= releasable.exhibit_b_answer %></p>
<% end %>

View File

@@ -8,38 +8,37 @@
Description: <%= release.description %>
</p>
<% end %>
<% photos = release.file_infos.photo %>
<% photos = release.image_files %>
<% unless photos.empty? %>
<h3>Photos</h3>
<ul>
<% photos.each do |file_info| %>
<% photos.each do |blob| %>
<li>
<%= file_info.filename %>
<%= blob.filename %>
</li>
<% end %>
</ul>
<% end %>
<% videos = release.file_infos.video %>
<% videos = release.video_files %>
<% unless videos.empty? %>
<h3>Videos</h3>
<ul>
<% videos.each do |file_info| %>
<% videos.each do |blob| %>
<li>
<%= file_info.filename %>
<%= blob.filename %>
</li>
<% end %>
</ul>
<% end %>
<% other = release.file_infos.other %>
<% other = release.other_files %>
<% unless other.empty? %>
<h3>Other files</h3>
<ul>
<% other.each do |file_info| %>
<% other.each do |blob| %>
<li>
<%= file_info.filename %>
<%= blob.filename %>
</li>
<% end %>
</ul>

View File

@@ -6,3 +6,5 @@
<p><%= releasable.public_send("question_#{n}_answer") %></p>
<% end %>
<% end %>
<%= contract_template.questionnaire_legal_text %>

View File

@@ -16,7 +16,8 @@
<%= contract_template.guardian_clause %>
<% end %>
<% if releasable.model_name.in? %w(MedicalRelease MiscRelease) %>
<%# 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>
@@ -30,7 +31,22 @@
<div class="page">
<%= render "contracts/amendment_page", releasable: releasable, preview: preview %>
</div>
<%end %>
<% end %>
<% if releasable.respond_to?(:exhibit_a_answer) %>
<% if contract_template.has_exhibit_a? %>
<div class="page">
<%= render "contracts/exhibit_a_page", releasable: releasable, preview: preview %>
</div>
<% end %>
<% if contract_template.has_exhibit_b? %>
<div class="page">
<%= render "contracts/exhibit_b_page", releasable: releasable, preview: preview %>
</div>
<% end %>
<% end %>
<% if releasable.respond_to?(:approved?) && releasable.approved? %>
<div class="page">

View File

@@ -1,7 +1,7 @@
<div class="card shadow-sm">
<%= card_header text: t(".heading"), subtext: @releasable.name, close_action_path: [@project, @releasable.model_name.plural] %>
<div class="card-body">
<% if @releasable.file_infos.any? %>
<% if @releasable.files.any? %>
<div class="alert alert-info text-center text-md-left">
<%= fa_icon "info-circle" %>
<strong>To Add Photos & Videos to the release: Drag & Drop Files or Click or Tap here to browse media.</strong>
@@ -11,8 +11,8 @@
<%= fa_icon "warning" %>
<strong>For optimal accuracy, please ensure video file names and photo file names match the source file name in the editing sequence.</strong>
</div>
<%= bootstrap_form_with model: @releasable, url: [@releasable, :file_infos] do |form| %>
<%= render "shared/file_infos_dropzone", form: form, releasable: @releasable %>
<%= bootstrap_form_with model: @releasable, url: [@releasable, :files] do |form| %>
<%= render "shared/releasable_files_dropzone", form: form, releasable: @releasable %>
<div class="row align-items-center text-center mt-4">
<%= link_to t("shared.cancel"), [@project, @releasable.model_name.plural], class: "col-3 text-reset" %>

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="<%= I18n.locale %>">
<head>
<title>BiGMedia.ai App</title>
<title>MESuite.ai App</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="<%= I18n.locale %>">
<head>
<title>BiGMedia.ai App</title>
<title>MESuite.ai App</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>

View File

@@ -18,6 +18,9 @@
<td>
<%= material_release.name %>
</td>
<td>
<%= contact_info_for(material_release.contact_person) %>
</td>
<td>
<%= notes_preview material_release.notes.order_by_recent %>
</td>

View File

@@ -29,10 +29,10 @@
<th><%= t '.table_headers.approved'%></th>
<th></th>
<th><%= MaterialRelease.human_attribute_name(:name) %></th>
<th><%= t(".table_headers.owner_info") %>
<th><%= t(".table_headers.notes") %></th>
<th><%= t(".table_headers.tags") %></th>
<th><%= t(".table_headers.signed_at") %></th>
<th><%= t(".table_headers.signed_at") %></th>
<th></th>
</tr>
</thead>

View File

@@ -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">

View File

@@ -0,0 +1,10 @@
<% if policy(Project).new? %>
<div class="col-sm-6 col-md-4 col-lg-3 mt-4">
<li class="card h-100 shadow-sm">
<div class="card-body d-flex flex-column justify-content-center align-items-center">
<%= fa_icon("plus-circle", class: "text-success", style: "font-size:4rem") %>
<%= link_to t(".actions.new"), [:new, :project], class: "mt-4 text-reset text-decoration-none stretched-link" %>
</div>
</li>
</div>
<% end %>

View File

@@ -1,17 +1,14 @@
<% if @projects.any? %>
<section class="container mt-5">
<h1 class="h3"><%= t(".heading") %> (<%= @projects.size %>)</h1>
<ul class="list-unstyled mt-2 row">
<% if policy(Project).new? %>
<div class="col-sm-6 col-md-4 col-lg-3 mt-4">
<li class="card h-100 shadow-sm">
<div class="card-body d-flex flex-column justify-content-center align-items-center">
<%= fa_icon("plus-circle", class: "text-success", style: "font-size:4rem") %>
<%= link_to t(".actions.new"), [:new, :project], class: "mt-4 text-reset text-decoration-none stretched-link" %>
</div>
</li>
</div>
<div class="d-flex flex-row justify-content-between align-items-center mb-3">
<h1 class="h3"><%= t(".heading") %> (<%= @projects.size %>)</h1>
<%= bootstrap_form_with url: [@account, :projects], method: :get, remote: true, layout: :inline, id: "search" do |form| %>
<%= form.search_field :query, hide_label: true, placeholder: t(".actions.search"), class: "rounded-pill-right", value: params[:query], prepend: form.button(fa_icon("search"), class: "btn btn-light border rounded-pill-left") %>
<% end %>
</div>
<ul id="projects" class="list-unstyled mt-2 row">
<%= render partial: "new_project_button" %>
<%= render @projects %>
</ul>
</section>

View File

@@ -0,0 +1,4 @@
$("#projects").html("")
$("#projects").append("<%= j render(partial: "new_project_button") %>");
$("#projects").append("<%= j render(@projects) %>");
$("form input[type='search']").val("<%= params[:query] %>");

View File

@@ -55,7 +55,7 @@
<hr>
<%= card_field_set_tag t(".files.heading") do %>
<%= render "shared/file_infos_dropzone", form: form, releasable: @acquired_media_release %>
<%= render "shared/releasable_files_dropzone", form: form, releasable: @acquired_media_release %>
<% end %>
<hr>

View File

@@ -13,6 +13,29 @@
<% end %>
<hr>
<% if @contract_template.has_exhibit_a? %>
<%= card_field_set_tag t(".exhibits.exhibit_a.heading") do %>
<p><%= @contract_template.exhibit_a_legal_text %></p>
<% if @contract_template.exhibit_a_question_text.present? %>
<div class="form-row">
<%= form.text_area :exhibit_a_answer, label: @contract_template.exhibit_a_question_text, wrapper_class: "col-sm-12" %>
</div>
<% end %>
<% end %>
<hr>
<% end %>
<% if @contract_template.has_exhibit_b? %>
<%= card_field_set_tag t(".exhibits.exhibit_b.heading") do %>
<p><%= @contract_template.exhibit_b_legal_text %></p>
<% if @contract_template.exhibit_b_question_text.present? %>
<div class="form-row">
<%= form.text_area :exhibit_b_answer, label: @contract_template.exhibit_b_question_text, wrapper_class: "col-sm-12" %>
</div>
<% end %>
<% end %>
<hr>
<% end %>
<% unless @contract_template.guardian_clause.blank? %>
<%= form.form_group :minor do %>
@@ -25,6 +48,19 @@
<hr>
<% end %>
<% if @contract_template.has_questionnaire? %>
<%= card_field_set_tag t(".questionnaire.heading") do %>
<% (1..AppearanceRelease::NUMBER_OF_CUSTOM_FIELDS).each do |n| %>
<% if @contract_template.public_send("question_#{n}_text").present? %>
<div class="form-row">
<%= form.text_area "question_#{n}_answer", wrapper_class: "col-sm-12", label: @contract_template.public_send("question_#{n}_text") %>
</div>
<% end %>
<% end %>
<% end %>
<hr>
<% end %>
<%= card_field_set_tag t(".personal_info.heading") do %>
<div class="alert alert-warning font-weight-bold"><%= t ".personal_info.instructions" %></div>
<div class="form-row">

View File

@@ -4,7 +4,7 @@
<%= bootstrap_form_with model: [@account, @project, @contract_template, @medical_release], local: true, validation_context: :native do |form| %>
<div class="alert alert-warning font-weight-bold"><%= t ".instructions_html", name: @project.name %></div>
<%= card_field_set_tag t(".legal.heading") do %>
<p><%= @contract_template.body %></p>
<%= @contract_template.body %>
<% if @contract_template.fee? %>
<p>
Fee <span class="font-weight-bold text-success"><%= number_to_currency @contract_template.fee %></span>
@@ -34,6 +34,9 @@
</div>
<% end %>
<% end %>
<% if @contract_template.questionnaire_legal_text.present? %>
<%= @contract_template.questionnaire_legal_text %>
<% end %>
<% end %>
<hr>
<% end %>
@@ -153,4 +156,4 @@
</div>
<% end %>
</div>
</div>
</div>

View File

@@ -3,3 +3,6 @@
<%= form.text_area "question_#{n}_text", wrapper_class: "col-sm-12" %>
</div>
<% end%>
<%= form.form_group do %>
<%= form.rich_text_area :questionnaire_legal_text, hint: true %>
<% end %>

View File

@@ -15,4 +15,3 @@
data-placeholder="<%= dropzone_placeholder_message_for(releasable) %>"
data-submit-button="#submit_release">
</div>

View File

@@ -0,0 +1,17 @@
<div class="field d-none">
<%= form.label :files %>
<%= form.file_field :files, disable: true, direct_upload: true, multiple: true, id: "releasable_files", hide_label: true %>
<% releasable.files.each do |file| %>
<% unless file.persisted? %>
<%= hidden_field_tag "#{releasable.model_name.param_key}[files][]", file.signed_id %>
<% end %>
<% end %>
</div>
<div class="dropzone field border-dashed"
data-accepted-files="audio/*,image/*,video/*,application/*"
data-behavior="dropzone"
data-file-input-id="releasable_files"
data-existing-files="<%= mock_photos_json(releasable.files) %>"
data-placeholder="<%= dropzone_placeholder_message_for(releasable) %>"
data-submit-button="#submit_folder"></div>

View File

@@ -18,15 +18,7 @@
<div class="card-body p-0">
<div class="embed-responsive embed-responsive-16by9">
<div class="embed-responsive-item">
<table class="w-100 h-100 bg-secondary">
<tbody>
<tr>
<td class="text-center align-middle text-white">
Video tutorial will be available soon
</td>
</tr>
</tbody>
</table>
<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/444718363" style="position:absolute;top:0;left:0;width:100%;height:100%;" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe></div><script src="https://player.vimeo.com/api/player.js"></script>
</div>
</div>
</div>

View File

@@ -10,5 +10,5 @@
</p>
<p>
If you have questions about how to use the software, please visit <%= link_to "BiGMedia.ai", "https://www.bigmedia.ai/contact" %>.
If you have questions about how to use the software, please visit <%= link_to "MESuite.ai", "https://www.mesuite.ai/contact" %>.
</p>

View File

@@ -7,4 +7,4 @@ Please click below to set your password.
<%= edit_password_reset_url(id: @user.password_reset_token, host: AppHost.new.domain_with_port) %>
<% end %>
If you have questions about how to use the software, please visit https://www.bigmedia.ai/contact.
If you have questions about how to use the software, please visit https://www.mesuite.ai/contact.

View File

@@ -49,4 +49,5 @@ Rails.application.configure do
ENV["ENABLE_ANALYTICS"] = "true"
ENV["BRAYNIAC_AI_API_ENDPOINT"] ||= ""
ENV["MILLICAST_API_SECRET"] ||= ""
end

View File

@@ -0,0 +1,2 @@
require "millicast"

View 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

View File

@@ -62,9 +62,10 @@ en:
empty: Acquired Media Releases will appear here
table_headers:
approved: Approved
file_infos_count: No. Files
files_count: No. Files
name: Name
notes: Notes
owner_info: Owner Info
signed_at: Date Signed
tags: Tags
new:
@@ -142,8 +143,8 @@ en:
review: Review
sign_amendment: Sign Additional Clause
messages:
amendment_not_signed_tooltip: Secondary Clause Not Yet Signed
amendment_signed_tooltip: Secondary Clause Signed
amendment_not_signed_tooltip: Additional Clause Not Yet Signed
amendment_signed_tooltip: Additional Clause Signed
approved_tooltip: Approved by %{user} on %{timestamp}
no_photos: Needs Photo
create:
@@ -230,6 +231,7 @@ en:
broadcast_recordings:
confirm_hide: Are you sure you want to hide this recording from everyone?
create:
alert: A live stream could not be created. Please try again later or contact support.
notice: A live stream has been created
destroy:
alert: A live stream has been deleted
@@ -273,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
@@ -310,6 +315,10 @@ en:
custom_fields:
heading: Questionnaire
instructions: Please list the questions you wish the signer to answer. Any unused question fields will be hidden.
exhibits:
heading: Exhibits
help:
option_field: Leave blank if not required for this contract.
exploitable_rights:
heading: Exploitable Rights
legal:
@@ -350,10 +359,16 @@ en:
contracts:
amendment_page:
description_labels:
amendment_clause: Amendment Clause
amendment_signature: Amendment Signature
amendment_signer_name: Amendment Signer Name
heading: Secondary Clause
amendment_clause: Clause
amendment_signature: Clause Signature
amendment_signer_name: Clause Signer Name
heading: Additional Clause
exhibit_a_page:
heading:
appearance_release: Exhibit A
exhibit_b_page:
heading:
appearance_release: Exhibit B
for_office_use_only:
description_labels:
date_issued: Date Issued
@@ -373,6 +388,7 @@ en:
other: Minor photos
questionnaire:
heading:
appearance_release: Questionnaire
medical_release: Medical Questionnaire
misc_release: Questionnaire
signature_page:
@@ -434,17 +450,16 @@ en:
download_type: Type
errors_helper:
failure_message: "The following errors have prevented this %{model_name} from being submitted:"
file_infos:
files:
edit:
heading: Add Media
update:
notice: The release has been updated
heading: Media
helpers:
help:
contract_template:
amendment_clause: Leave blank if not required for this contract
fee: Leave at $0.00 for no-fee
guardian_clause: Leave blank if not required for this contract
questionnaire_legal_text: Leave blank if not required for this contract
signature_legal_text: Leave blank if not required for this contract
task_request:
time_allowed: Minimum of 2 hours, no partial hours allowed
@@ -523,7 +538,7 @@ en:
person_name: Name
person_phone: Phone number
contract_template:
amendment_clause: Secondary Clause
amendment_clause: Additional Contract Clause
location_release:
address_city: City
address_country: Country
@@ -832,6 +847,8 @@ en:
location_releases:
create:
notice: The location release has been created
csv:
no_amendment_clause: No additional contract clause
destroy:
alert: The location release has been deleted
edit:
@@ -854,7 +871,9 @@ en:
search: Search
empty: Location Releases will appear here
table_headers:
amendment_signed: Amendment
address: Address
amendment_signed: Additional Clause
amendment_signed_column: Additional Clause
approved: Approved
location_info: Location Info
notes: Notes
@@ -865,10 +884,10 @@ en:
actions:
manage: Manage
review: Review
sign_amendment: Sign Amendment
sign_amendment: Sign Additional Clause
messages:
amendment_not_signed_tooltip: Amendment not yet signed
amendment_signed_tooltip: Amendment Signed
amendment_not_signed_tooltip: Additional clause not yet signed
amendment_signed_tooltip: Additional clause signed
approved_tooltip: Approved by %{user} on %{timestamp}
no_photos: Needs Photo
new:
@@ -909,6 +928,7 @@ en:
approved: Approved
name: Name
notes: Notes
owner_info: Owner Info
signed_at: Date Signed
tags: Tags
material_release:
@@ -933,7 +953,6 @@ en:
empty: Medical releases will appear here
table_headers:
approved: Approved
approved?: Approved
contact_info: Contact info
name: Person name
notes: Notes
@@ -1083,9 +1102,13 @@ en:
actions:
folder: Add Folder
new: Create New Project
search: Search
heading: Open Projects
new:
heading: Create New Project
new_project_button:
actions:
new: Create New Project
project:
actions:
delete: Delete
@@ -1143,12 +1166,12 @@ en:
heading: Signature
amendments:
create:
amendment_already_signed_message: Release amendment is already signed!
amendment_signed_message: Release amendment signed successfully! Thank you
amendment_already_signed_message: Release additional clause is already signed!
amendment_signed_message: Release additional clause signed successfully! Thank you
new:
amendment:
heading: Amendment
copy_url: Copy sign amendment URL
heading: Additional Clause
copy_url: Copy sign additional clause URL
signature:
heading: Signature
signed_contract_preview: Signed Contract Preview
@@ -1157,6 +1180,11 @@ en:
notice: Your release has been signed. Thank you!
new:
cancel: Cancel
exhibits:
exhibit_a:
heading: Exhibit A
exhibit_b:
heading: Exhibit B
guardian_2_info:
heading: Second Guardian Information (if company requires)
guardian_2_photo:
@@ -1186,6 +1214,8 @@ en:
no_photo: No photo yet
take_photo: Take Photo
warning: If your photo appears sideways, it will be autocorrected when you submit your release.
questionnaire:
heading: Questionnaire
signature:
heading: Sign Below
broadcasts:
@@ -1615,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

View File

@@ -16,9 +16,11 @@ es:
heading: Guardian Photo
index:
table_headers:
approved: Appproved (ES)
file_infos_count: No. Files (ES)
name: Name (ES)
notes: Notes (ES)
owner_info: Owner Info (ES)
signed_at: Date Signed (ES)
tags: Tags (ES)
activerecord:
@@ -47,8 +49,8 @@ es:
appearance_releases:
appearance_release:
actions:
sign_amendment: Sign Additional Clause (ES)
manage: Manage (ES)
sign_amendment: Sign Additional Clause (ES)
messages:
amendment_not_signed_tooltip: Amendment not yet signed (ES)
amendment_signed_tooltip: Amendment signed (ES)
@@ -72,6 +74,7 @@ es:
imported_appearance_release_missing_attachment: Person photo or contract missing for imported appearance release (ES)
table_headers:
amendment_signed: Additional Clause (ES)
approved: Approved (ES)
contact_info: ""
name: ""
notes: ""
@@ -128,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:
@@ -293,6 +299,8 @@ es:
person_email: Dirección de correo electrónico
person_name: Nómbre
person_phone: Número de teléfono
contract_template:
amendment_clause: Additional Contract Clause (ES)
material_release:
guardian_2_address_city: Guardian 2 city (ES)
guardian_2_address_country: Guardian 2 country (ES)
@@ -410,19 +418,23 @@ es:
update: Approve (ES)
update: 'Actualizar %{model}'
location_releases:
csv:
no_amendment_clause: No amendment clause (ES)
form:
photos:
dropzone_label: Tap to take a photo of the Property (optional) (ES)
index:
table_headers:
address: Address (ES)
amendment_signed: Amendment (ES)
amendment_signed: Additional Clause (ES)
amendment_signed_column: Amendment signed (ES)
approved: Approved (ES)
notes: Notes (ES)
signed_at: Date Signed (ES)
tags: Tags (ES)
location_release:
actions:
sign_amendment: Sign Amendment (ES)
sign_amendment: Sign Additional Clause (ES)
messages:
amendment_not_signed_tooltip: Amendment not yet signed (ES)
amendment_signed_tooltip: Amendment Signed (ES)
@@ -442,8 +454,10 @@ es:
heading: Guardian Photo
index:
table_headers:
approved: Approved (ES)
name: Name (ES)
notes: Notes (ES)
owner_info: Owner Info
signed_at: Date Signed (ES)
tags: Tags (ES)
medical_releases:
@@ -452,7 +466,6 @@ es:
index:
table_headers:
approved: Approved (ES)
approved?: Approved (ES)
contact_info: Contact info (ES)
name: Person name (ES)
notes: Notes (ES)
@@ -466,6 +479,7 @@ es:
misc_releases:
index:
table_headers:
approved: Approved (ES)
contact_info: Contact info (ES)
name: Person name (ES)
notes: Notes (ES)
@@ -474,6 +488,7 @@ es:
music_releases:
index:
table_headers:
approved: Approved (ES)
composers_count: No. Composers (ES)
file_infos_count: No. Files (ES)
name: Name (ES)
@@ -508,7 +523,7 @@ es:
amendment_signed_message: Release amendment signed successfully! Thank you (ES)
new:
amendment:
heading: Amendment
heading: Additional Clause (ES)
copy_url: Copy sign amendment URL (ES)
signature:
heading: Signature (ES)
@@ -635,6 +650,7 @@ es:
heading: Guardian Photo (ES)
index:
table_headers:
approved: Approved (ES)
email: Email (ES)
name: Name (ES)
notes: Notes (ES)
@@ -692,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)

View File

@@ -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
@@ -19,8 +21,8 @@ Rails.application.routes.draw do
concern :taggable do
resources :acts_as_taggable_on_tags, only: [:new, :create, :destroy], controller: "tags"
end
concern :file_infoable do
resource :file_infos, only: [:edit, :update]
concern :file_uploadable do
resource :files, only: [:edit, :update]
end
concern :approvable do
resource :approvals, only: [:new, :create]
@@ -51,7 +53,7 @@ Rails.application.routes.draw do
resource :account, only: [:new, :create, :update]
resources :account_auths, only: [:index, :create, :update, :destroy]
resources :projects, shallow: true do
resources :acquired_media_releases, except: [:show], concerns: [:contractable, :notable, :file_infoable]
resources :acquired_media_releases, except: [:show], concerns: [:contractable, :notable, :file_uploadable]
resources :appearance_releases, except: [:show], concerns: [:contractable, :notable]
resources :appearance_release_imports, only: [:create]
resources :location_releases, except: [:show], concerns: [:contractable, :notable, :photoable]
@@ -100,7 +102,7 @@ Rails.application.routes.draw do
member do
delete :destroy_file
end
resource :zoom_meeting, only: [:show]
resource :conference_meeting, only: [:show]
resources :broadcast_recordings, only: :destroy
end
resources :directories, except: [:index] do
@@ -162,6 +164,7 @@ Rails.application.routes.draw do
scope 'v1' do
get 'sync' => 'sync#index'
post 'user_token' => 'user_token#create'
post 'users' => 'users#create'
resource :profiles, only: [:show]
resources :projects, only: [:index] do
resources :broadcasts, only: [:index, :show, :update]

View File

@@ -0,0 +1,5 @@
class AddSimulcastUidToBroadcasts < ActiveRecord::Migration[6.0]
def change
add_column :broadcasts, :simulcast_uid, :string
end
end

View File

@@ -0,0 +1,11 @@
class AddExhibitFieldsToContractTemplates < ActiveRecord::Migration[6.0]
def change
add_column :contract_templates, :exhibit_a_legal_text, :text
add_column :contract_templates, :exhibit_a_question_text, :text
add_column :contract_templates, :exhibit_b_legal_text, :text
add_column :contract_templates, :exhibit_b_question_text, :text
add_column :appearance_releases, :exhibit_a_answer, :text
add_column :appearance_releases, :exhibit_b_answer, :text
end
end

View File

@@ -0,0 +1,19 @@
class AddQuestionAnswersToAppearanceReleases < ActiveRecord::Migration[6.0]
def change
add_column :appearance_releases, :question_1_answer, :text
add_column :appearance_releases, :question_2_answer, :text
add_column :appearance_releases, :question_3_answer, :text
add_column :appearance_releases, :question_4_answer, :text
add_column :appearance_releases, :question_5_answer, :text
add_column :appearance_releases, :question_6_answer, :text
add_column :appearance_releases, :question_7_answer, :text
add_column :appearance_releases, :question_8_answer, :text
add_column :appearance_releases, :question_9_answer, :text
add_column :appearance_releases, :question_10_answer, :text
add_column :appearance_releases, :question_11_answer, :text
add_column :appearance_releases, :question_12_answer, :text
add_column :appearance_releases, :question_13_answer, :text
add_column :appearance_releases, :question_14_answer, :text
add_column :appearance_releases, :question_15_answer, :text
end
end

View File

@@ -0,0 +1,5 @@
class AddVideoConferenceUrlOverrideToBroadcasts < ActiveRecord::Migration[6.0]
def change
add_column :broadcasts, :video_conference_url_override, :string
end
end

View 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

View File

@@ -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

View File

@@ -366,7 +366,24 @@ CREATE TABLE public.appearance_releases (
approved_by_user_name text,
approved_by_user_email text,
approved_at timestamp without time zone,
amendment_signer_name character varying
amendment_signer_name character varying,
exhibit_a_answer text,
exhibit_b_answer text,
question_1_answer text,
question_2_answer text,
question_3_answer text,
question_4_answer text,
question_5_answer text,
question_6_answer text,
question_7_answer text,
question_8_answer text,
question_9_answer text,
question_10_answer text,
question_11_answer text,
question_12_answer text,
question_13_answer text,
question_14_answer text,
question_15_answer text
);
@@ -567,7 +584,11 @@ CREATE TABLE public.broadcasts (
full_live_stream_playback_uid character varying,
stream_url_override character varying,
stream_key_override character varying,
director_mode_video_embed text
director_mode_video_embed text,
simulcast_uid character varying,
video_conference_url_override character varying,
conference_option character varying,
conference_join_url character varying
);
@@ -665,7 +686,11 @@ CREATE TABLE public.contract_templates (
question_13_text text,
question_14_text text,
question_15_text text,
accessibility integer DEFAULT 0
accessibility integer DEFAULT 0,
exhibit_a_legal_text text,
exhibit_a_question_text text,
exhibit_b_legal_text text,
exhibit_b_question_text text
);
@@ -1781,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
);
@@ -3998,6 +4027,12 @@ INSERT INTO "schema_migrations" (version) VALUES
('20200730050903'),
('20200803145912'),
('20200803150138'),
('20200804093409');
('20200804093409'),
('20200807190607'),
('20200811102720'),
('20200812060406'),
('20200819070738'),
('20200820081251'),
('20200820081524');

124
lib/azure_ad.rb Normal file
View 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

7
lib/knock_monkeypatch.rb Normal file
View File

@@ -0,0 +1,7 @@
module Knock
class AuthTokenController < ApplicationController
skip_before_action :authenticate
alias authenticate_with_token authenticate
before_action :authenticate_with_token
end
end

107
lib/microsoft_graph.rb Normal file
View 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

Some files were not shown because too many files have changed in this diff Show More