Compare commits

...

9 Commits

Author SHA1 Message Date
bilal
94b417d4c4 do not show archived templates when importing contract templates 2020-06-17 15:08:01 +02:00
Senad Uka
a7b90c223b Upstream sync 2020-06-17 14:39:10 +02:00
Senad Uka
9a540efc74 Upstream sync 2020-06-16 17:11:04 +02:00
Senad Uka
028e946fcf Upstream sync 2020-06-15 08:33:23 +02:00
Senad Uka
fbf3173747 Upstream sync 2020-06-12 16:38:59 +02:00
Senad Uka
5f5e6c18b5 Upstream sync 2020-06-11 16:56:29 +02:00
Senad Uka
dc9ba08e1b Upstream sync 2020-06-10 11:19:35 +02:00
Senad Uka
cd5bbaeb62 Upstream sync master 2020-06-09 06:35:40 +02:00
Senad Uka
64bda6eab6 Upstream sync 2020-06-03 17:14:04 +02:00
128 changed files with 2508 additions and 469 deletions

View File

@@ -95,14 +95,28 @@ rake i18n:sort
```
## Zoom.us integration
DirectMe app offers live broadcasting. Users are offered to paralelly connect to the Zoom meeting to have a video conference while the streaming happens. In order to use the Zoom functionality, the app needs to have the API keys provided. You need Zoom PRO account for this feature.
DirectMe app offers live broadcasting feature. Users are offered to paralelly connect to the Zoom meeting to have a video conference while the streaming happens. In order to use the Zoom functionality, the app needs to have the API and verification token keys provided along with the account number that is available after login into the Zoom account. You need to have Zoom PRO subscription in order to use this feature.
#### Zoom.us api keys
1. Log in to you zoom.us account
2. Go to https://marketplace.zoom.us/develop/create
3. Choose JWT application
4. Copy API Key and API Secret
5. Set up ZOOM_API_KEY and ZOOM_API_SECRET environment variables
#### Setup
There is some configuration that has to be done through the API on the Zoom account so that you can use the feature. Run `rails zoom:setup` rake task to do it.
#### Zoom.us webhooks
To ensure integrity in between different Zoom environments, the app uses Zoom webhooks. To set them up, go to https://marketplace.czoom.us -> Develop -> JWT app -> Feature -> Event Subscriptions and enable following hooks:
* Start Meeting
* End Meeting
* All Recordings have completed
* User has been created
* User had been deleted
#### Syncing app with Zoom account configuration
If you are setting up the app to use Zoom account that has been previously used with DirectME, it is a good idea to make sure that the db state reflects the account situation. To do that, run `rails zoom:sync` rake task.
## Working Locally

View File

@@ -1,23 +1,23 @@
$(document).on "turbolinks:load", ->
# Only connect if a broadcast-token meta tag is present
broadcast_meta = document.querySelector("meta[name=broadcast-token]")
return unless broadcast_meta
broadcastToken = broadcast_meta.getAttribute("content")
App.public = App.cable.subscriptions.create { channel: "BroadcastsChannel", token: broadcastToken },
subscribeToBroadcast = (broadcastToken) -> App.cable.subscriptions.create { channel: "BroadcastsChannel", token: broadcastToken },
connected: ->
# Called when the subscription is ready for use on the server
console.info "Subscribed to channel for broadcast:#{broadcastToken}"
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
return unless document.querySelector("meta[name=broadcast-token][content='#{broadcastToken}']")
switch data.event
when "broadcast_stream_update" then @refreshBroadcastVideo(data)
when "stream_recording_ready" then @showBroadcastRecordings(data)
when "file_upload_update" then @refreshFilesTab(data)
when "broadcast_stream_update"
return unless document.querySelector("meta[name=broadcast-token][current=true][content='#{broadcastToken}']")
@refreshBroadcastVideo(data)
when "stream_recording_ready"
return unless document.querySelector("meta[name=broadcast-token][current=true][content='#{broadcastToken}']")
@showBroadcastRecordings(data)
when "file_upload_update"
return unless document.querySelector("meta[name=broadcast-token][content='#{broadcastToken}']")
@refreshBroadcastFilesTab(data)
refreshBroadcastVideo: (data) ->
$("#broadcast_updates").html data.status_content
@@ -38,8 +38,12 @@ $(document).on "turbolinks:load", ->
$(".flash-message").html data.flash_content
$("#broadcast_recordings").html data.recordings_content
$("#broadcast_recordings_nav").html data.recordings_nav_content
refreshFilesTab: (data) ->
$("#broadcast_file_list").html data.files_content
$("#broadcast_files_pagination").html data.pagination_content
refreshBroadcastFilesTab: (data) ->
$("#broadcast_file_list_#{data.broadcast_token}").html data.files_content
$("#broadcast_files_pagination_#{data.broadcast_token}").html data.pagination_content
# Create a channel subscription for every broadcast included in the meta tags
get_token = (meta) -> meta.getAttribute("content")
broadcast_tokens = (get_token broadcast_meta for broadcast_meta in document.querySelectorAll("meta[name=broadcast-token]"))
subscribeToBroadcast token for token in broadcast_tokens

View File

@@ -1,11 +1,16 @@
$(document).on("change", "[data-toggle=collapse-select]", function(event) {
const select = event.target;
const target = select.dataset.target;
const showValues = JSON.parse(select.dataset.showValues);
if (showValues.indexOf(select.value) > -1) {
$(target).show("fast");
} else {
$(target).hide("fast");
}
const mappings = JSON.parse(select.dataset.targetShowValuesMapping);
$.each(mappings, function( key, value ) {
if (value.indexOf(select.value) > -1) {
$(key).show("fast");
} else {
$(key).hide("fast");
}
});
});
$(document).on("turbolinks:load", function() {
$("[data-toggle=collapse-select]").trigger("change");
});

View File

@@ -39,7 +39,7 @@ function selectBroadcast(clicked_element, checkbox) {
if (broadcast_ids.length >= 2) {
multi_view_ids = $.param({multi_view_ids: broadcast_ids});
broadcast_url = "/en/projects/" + project_id + "/broadcasts/" + broadcast_ids[0] + "?" + multi_view_ids
$("#multi_view_broadcasts").attr("href", broadcast_url);
$("#multi_view_broadcasts").attr("target", "_blank");
$("#multi_view_broadcasts").removeClass('disabled');

View File

@@ -6,6 +6,11 @@
@import "dropzone/dist/dropzone";
@import 'bootstrap-datepicker';
// Global styles
label {
text-transform: capitalize;
}
// Wordmarks
.suite-wordmark {
font-weight: normal;

View File

@@ -31,7 +31,7 @@ u {
}
.page {
page-break-before: always;
page-break-before: always;
}
.logo {
@@ -44,6 +44,14 @@ u {
text-align: right;
}
.qr-code {
margin-right: -30px;
}
.do-not-copy-warning {
padding-right: 15px;
}
.heading-table td {
width: 50%;
}

View File

@@ -35,11 +35,12 @@ class BroadcastsChannel < ApplicationCable::Channel
recordings_nav_content: recordings_nav_content
end
def self.file_upload_updates(broadcast, files, pagination_content)
def self.broadcast_file_upload_updates(broadcast, files, pagination_content)
files_content = ApplicationController.render partial: "broadcasts/file", collection: files
broadcast_to broadcast, {
event: :file_upload_update,
broadcast_token: broadcast.token,
files_content: files_content,
pagination_content: pagination_content
}

View File

@@ -61,7 +61,8 @@ class AcquiredMediaReleasesController < ApplicationController
:name,
:territory,
:term,
:person_name,
:person_first_name,
:person_last_name,
:person_phone,
:person_email,
:person_company,

View File

@@ -0,0 +1,50 @@
# Duplicated from ActiveStorage::DirectUploadsController
# https://github.com/rails/rails/blob/v6.0.0/activestorage/app/controllers/active_storage/direct_uploads_controller.rb
class Api::DirectUploadsController < Api::ApiController
include ActiveStorage::SetCurrent
deserializable_resource :direct_upload, only: [:create]
def create
blob = ActiveStorage::Blob.create_before_direct_upload!(blob_params)
render jsonapi: DirectUpload.new(blob)
end
private
def blob_params
params.
require(:direct_upload).
permit(:type, :filename, :byte_size, :checksum, :content_type, :metadata).
except(:type).
to_h.symbolize_keys
end
class DeserializableDirectUpload < JSONAPI::Deserializable::Resource
attributes :filename, :byte_size, :checksum, :content_type, :metadata
end
class SerializableDirectUpload < JSONAPI::Serializable::Resource
type 'direct_upload'
attributes :id, :key, :signed_id, :url, :headers
end
class DirectUpload
delegate :id, :key, :signed_id, to: :blob
attr_reader :blob
def initialize(blob)
@blob = blob
end
def url
blob.service_url_for_direct_upload
end
def headers
blob.service_headers_for_direct_upload
end
end
end

View File

@@ -3,7 +3,6 @@
class AppearanceReleaseImportsController < ApplicationController
include AppearanceReleaseContext
include ProjectContext
include CreateReleasableJobs
before_action :set_project, only: [:create]
@@ -11,24 +10,16 @@ class AppearanceReleaseImportsController < ApplicationController
def create
authorize AppearanceRelease
@failed_files = []
attachments = appearance_release_params
if attachments.nil?
alert_message = t 'appearance_releases.create.no_attachments'
redirect_to [@project, :appearance_releases], alert: alert_message
else
attachments.each do |attachment|
create_imported_appearance_release attachment
end
MatchAppearanceReleasesJob.perform_later(@project, attachments)
notice_message = t 'appearance_releases.create.matching_started'
redirect_to [@project, :appearance_releases], notice: notice_message
end
unless @failed_files.empty?
alert_message = t 'appearance_releases.create.failed_import'
alert_message += '<br><ul>'
@failed_files.each { |file_name| alert_message += "<li>#{file_name}</li>" }
alert_message += '</ul>'
end
redirect_to [@project, :appearance_releases], alert: alert_message
end
private
@@ -45,45 +36,7 @@ class AppearanceReleaseImportsController < ApplicationController
params.require(:attachments)
end
def build_appearance_release(params = {})
authorize appearance_releases.build(params)
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
def create_imported_appearance_release(attachment)
blob = ActiveStorage::Blob.find_signed(attachment)
return if blob.nil?
extension = blob.filename.extension_with_delimiter
unless AppearanceRelease.acceptable_import_file_extensions.include? extension
blob.purge
@failed_files << blob.filename
return
end
random_contract_no = AppearanceRelease.random_contract_number.to_s
appearance_release_params = {
person_last_name: random_contract_no
}
if blob.image?
appearance_release_params[:person_photo] = attachment
appearance_release_params[:person_first_name] = I18n.t('appearance_releases.shared.imported_appearance_release_headshot_name')
elsif extension == '.pdf'
appearance_release_params[:contract] = attachment
appearance_release_params[:person_first_name] = I18n.t('appearance_releases.shared.imported_appearance_release_contract_name')
end
appearance_release = build_appearance_release(appearance_release_params)
if appearance_release.save(context: :non_native)
log_create_analytics
after_create appearance_release
else
@failed_files << blob.filename
end
def acceptable_extensions
AppearanceRelease.acceptable_import_file_extensions
end
end

View File

@@ -78,7 +78,7 @@ class AppearanceReleasesController < ApplicationController
end
def appearance_release_params
params.require(:appearance_release).permit(:contract, :guardian_address, :guardian_first_name, :guardian_last_name, :guardian_phone, :guardian_photo, :minor,
params.require(:appearance_release).permit(:contract, :guardian_address, :guardian_first_name, :guardian_last_name, :guardian_phone, :guardian_photo, :guardian_email, :minor,
:person_address, :person_first_name, :person_last_name, :person_phone, :person_email, :person_photo,
:applicable_medium_id, :applicable_medium_text,
:territory_id, :territory_text,

View File

@@ -1,6 +1,6 @@
class BroadcastsController < ApplicationController
layout "project"
before_action :set_project
before_action :build_broadcast, only: [:new, :create]
before_action :set_broadcast, only: [:show, :destroy, :update]
@@ -24,19 +24,19 @@ class BroadcastsController < ApplicationController
end
end
def show
@conference_url = url_for [@broadcast.project, @broadcast, :zoom_meeting]
@recordings = @broadcast.broadcast_recordings.order_by_recent.paginate(page: params[:page])
@files = @broadcast.files.order("created_at DESC").paginate(page: params[:files_page])
render layout: 'application'
end
def update
@broadcast.update(broadcast_params)
@files = @broadcast.files.order("created_at DESC").paginate(page: 1)
pagination_content = ApplicationController.render html: helpers.will_paginate(@files, params: { active_tab: params[:active_tab] })
BroadcastsChannel.file_upload_updates(@broadcast, @files, pagination_content)
end
def show
@conference_url = url_for [@broadcast.project, @broadcast, :zoom_meeting]
@recordings = @broadcast.broadcast_recordings.order_by_recent.paginate(page: params[:page])
@files = @broadcast.files.order("created_at DESC").paginate(page: params[:page])
render layout: 'application'
pagination_content = ApplicationController.render html: helpers.will_paginate(@files, params: { active_tab: params[:active_tab], page: params[:page], active_files_tab: params[:active_files_tab] })
BroadcastsChannel.broadcast_file_upload_updates(@broadcast, @files, pagination_content)
end
def destroy
@@ -71,7 +71,10 @@ class BroadcastsController < ApplicationController
def set_multi_view_broadcasts
authorized_broadcasts = authorize policy_scope(Broadcast).where(id: params[:multi_view_ids]).order_by_recent
@multi_view_broadcasts = authorized_broadcasts.map { |b| MultiViewBroadcast.new(b, params[:multi_view_ids]) }
@multi_view_broadcasts = authorized_broadcasts.map do |b|
files_page = params[:files_page] if params[:active_files_tab] == b.token
MultiViewBroadcast.new(b, params[:multi_view_ids], files_page)
end
end
def filtered_broadcasts
@@ -90,20 +93,29 @@ class BroadcastsController < ApplicationController
class MultiViewBroadcast
include Rails.application.routes.url_helpers
delegate_missing_to :@broadcast
def initialize(broadcast, multi_view_ids)
def initialize(broadcast, multi_view_ids, paginate_page)
@broadcast = broadcast
@multi_view_ids = multi_view_ids
@paginate_page = paginate_page
end
def url
project_broadcast_path(@broadcast.project, @broadcast, multi_view_ids: @multi_view_ids, locale: I18n.locale)
end
def files
@broadcast.files.order("created_at DESC").paginate(page: @paginate_page)
end
def uid
id
end
def self.model_name
Broadcast.model_name
end
end
end

View File

@@ -0,0 +1,13 @@
module MedicalReleaseContext
extend ActiveSupport::Concern
def medical_releases
policy_scope(MedicalRelease)
end
def set_medical_release
medical_release_id = params[:medical_release_id] || params[:id]
@medical_release = authorize medical_releases.find(medical_release_id)
end
end

View File

@@ -61,7 +61,12 @@ class ContractTemplatesController < ApplicationController
:applicable_medium_id, :applicable_medium_text,
:territory_id, :territory_text,
:term_id, :term_text,
:restriction_id, :restriction_text)
:restriction_id, :restriction_text,
:question_1_text, :question_2_text,
:question_3_text, :question_4_text,
:question_5_text, :question_6_text,
:question_7_text, :question_8_text,
:question_9_text, :question_10_text)
end
def download_attributes

View File

@@ -68,7 +68,8 @@ class LocationReleasesController < ApplicationController
:territory_id, :territory_text,
:term_id, :term_text,
:restriction_id, :restriction_text,
:filming_started_on, :filming_ended_on
:filming_started_on, :filming_ended_on,
:filming_hours
)
end

View File

@@ -0,0 +1,40 @@
class MedicalReleasesController < ApplicationController
include ProjectContext, MedicalReleaseContext
before_action :set_project, only: [:index]
before_action :set_medical_release, only: [:destroy]
include ProjectLayout
def index
@medical_releases = filtered_medical_releases.order_by_recent.paginate(page: params[:page])
end
def destroy
@project = @medical_release.project
if @medical_release.destroy
redirect_to [@project, :medical_releases], alert: t(".alert")
end
end
private
def medical_releases
if @project
policy_scope(@project.medical_releases)
else
policy_scope(MedicalRelease)
end
end
def filtered_medical_releases
results = medical_releases
if params[:query].present?
results = results.search(params[:query])
end
results
end
end

View File

@@ -60,7 +60,7 @@ class ProjectsController < ApplicationController
end
def features_settings_params
%i(appearance_release location_release material_release acquired_media_release music_release talent_release video_analysis)
%i(appearance_release location_release material_release acquired_media_release music_release talent_release medical_release video_analysis)
end
def project_params_with_current_account

View File

@@ -45,6 +45,9 @@ class Public::AcquiredMediaReleasesController < Public::BaseController
params.require(:acquired_media_release).permit(
:name,
:description,
:person_first_name,
:person_last_name,
:person_email,
:person_title,
:person_phone,
:person_fax,

View File

@@ -41,7 +41,7 @@ class Public::AppearanceReleasesController < Public::BaseController
def appearance_release_params
params.require(:appearance_release).permit(:person_address, :person_first_name, :person_last_name, :person_phone, :person_email, :person_photo,
:guardian_address, :guardian_first_name, :guardian_last_name, :guardian_phone, :guardian_photo, :minor,
:guardian_address, :guardian_first_name, :guardian_last_name, :guardian_phone, :guardian_email, :guardian_photo, :minor,
:signature_base64, :person_date_of_birth,
:locale, :contract_template,)
end

View File

@@ -6,8 +6,8 @@ class Public::BroadcastsController < Public::BaseController
@conference_url = broadcast_zoom_meeting_url(@broadcast.token)
@multi_view_broadcasts = multi_view_broadcasts
@recordings = @broadcast.broadcast_recordings.order_by_recent.paginate(page: params[:page])
@files = @broadcast.files.order("created_at DESC").paginate(page: params[:page])
@files = @broadcast.files.order("created_at DESC").paginate(page: params[:files_page])
render 'broadcasts/show'
end
@@ -15,8 +15,8 @@ class Public::BroadcastsController < Public::BaseController
@broadcast.update(broadcast_params)
@files = @broadcast.files.order("created_at DESC").paginate(page: 1)
pagination_content = ApplicationController.render html: helpers.will_paginate(@files, params: { active_tab: params[:active_tab] })
BroadcastsChannel.file_upload_updates(@broadcast, @files, pagination_content)
pagination_content = ApplicationController.render html: helpers.will_paginate(@files,params: { active_tab: params[:active_tab], page: params[:page], active_files_tab: params[:active_files_tab] })
BroadcastsChannel.broadcast_file_upload_updates(@broadcast, @files, pagination_content)
end
private
@@ -29,7 +29,10 @@ class Public::BroadcastsController < Public::BaseController
Broadcast.
where(token: params[:multi_view_tokens]).
order_by_recent.
map { |b| MultiViewBroadcast.new(b, params[:multi_view_tokens]) }
map do |b|
files_page = params[:files_page] if params[:active_files_tab] == b.token
MultiViewBroadcast.new(b, params[:multi_view_tokens], files_page)
end
end
def set_broadcast
@@ -38,20 +41,29 @@ class Public::BroadcastsController < Public::BaseController
class MultiViewBroadcast
include Rails.application.routes.url_helpers
delegate_missing_to :@broadcast
def initialize(broadcast, multi_view_tokens)
def initialize(broadcast, multi_view_tokens, paginate_page)
@broadcast = broadcast
@multi_view_tokens = multi_view_tokens
@paginate_page = paginate_page
end
def url
broadcast_url(uid, multi_view_tokens: @multi_view_tokens, host: AppHost.new.domain_with_port, locale: I18n.locale)
end
def files
@broadcast.files.order("created_at DESC").paginate(page: @paginate_page)
end
def uid
token
end
def self.model_name
Broadcast.model_name
end
end
end

View File

@@ -63,7 +63,9 @@ class Public::LocationReleasesController < Public::BaseController
:person_address_zip,
:person_address_country,
:signature_base64,
:locale, :contract_template, :filming_started_on, :filming_ended_on
:locale, :contract_template, :filming_started_on, :filming_ended_on,
:filming_hours,
photos: []
)
end

View File

@@ -45,7 +45,7 @@ class Public::MaterialReleasesController < Public::BaseController
:person_first_name, :person_last_name, :person_title, :person_company, :person_phone, :person_email,
:person_address_street1, :person_address_street2, :person_address_city, :person_address_state, :person_address_zip, :person_address_country,
:signature_base64,
:locale, :contract_template, :description
:locale, :contract_template, :description, photos: []
)
end

View File

@@ -0,0 +1,86 @@
class Public::MedicalReleasesController < Public::BaseController
before_action :set_account, :set_project, :set_contract_template
def new
@medical_release = build_medical_release
end
def create
@medical_release = build_medical_release(medical_release_params_with_locale_and_contract_template)
if @medical_release.save(context: :native)
if @medical_release.contract_template.present?
AttachContractToReleasableJob.perform_later(@medical_release)
end
log_create_analytics
else
render :new
end
end
private
def set_project
@project = @account.projects.find(params[:project_id])
end
def set_account
@account = Account.find_by(slug: params[:account_id])
end
def set_contract_template
@contract_template = @project.contract_templates.find(params[:contract_template_id])
end
def medical_releases
policy_scope(@project.medical_releases)
end
def build_medical_release(params = {})
authorize medical_releases.build(params)
end
def medical_release_params
params
.require(:medical_release)
.permit(
person_params,
:signature_base64,
:locale,
:contract_template,
: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,
photos: [],
)
end
def person_params
[
:person_first_name,
:person_last_name,
:person_phone,
:person_email,
:person_address_street1,
:person_address_street2,
:person_address_city,
:person_address_state,
:person_address_zip,
:person_address_country,
]
end
def medical_release_params_with_locale
medical_release_params.merge(locale: I18n.locale)
end
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

@@ -11,7 +11,9 @@ class ReleaseTemplateImportsController < ApplicationController
templates = []
filtered_contract_templates.each do |contract_template|
next if contract_template.duplicated? || contract_template.project == @project
next if contract_template.duplicated? ||
contract_template.archived? ||
contract_template.project == @project
already_imported = contract_template.duplicates.non_archived.pluck(:project_id).include?(@project.id)
templates << OpenStruct.new(template: contract_template, already_imported?: already_imported)

View File

@@ -5,7 +5,7 @@ class ZoomNotificationsController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authorize_zoom
before_action :set_zoom_meeting, only: :create
before_action :set_zoom_meeting, only: [:create], if: :meeting_event?
def create
case notification_event
@@ -16,6 +16,12 @@ class ZoomNotificationsController < ApplicationController
when 'recording.completed'
recording = notification.dig(:payload, :object, :recording_files).first
AttachRecordingToZoomMeetingJob.perform_later(@zoom_meeting, recording, notification['download_token'])
when 'user.deleted'
zoom_user = ZoomUser.find_by(api_id: notification.dig(:payload, :object, :id))
if zoom_user.present?
zoom_user.api_id = nil
zoom_user.destroy
end
else
Rails.logger.info notification_event
Rails.logger.info notification
@@ -42,6 +48,10 @@ class ZoomNotificationsController < ApplicationController
notification.dig(:payload, :object, :host_id)
end
def meeting_event?
notification_event.split(".").first.to_s.in? %w(meeting recording)
end
def set_zoom_meeting
@zoom_meeting = ZoomMeeting.find_by!(api_meeting_id: notification_meeting_id)
end

View File

@@ -3,8 +3,12 @@ module DropzoneHelper
case releasable.model_name.param_key
when "acquired_media_release"
"To Add Photos & Videos to the release:<br>Drag & Drop Files<br>or<br>Click or Tap here to browse photos and connect to Camera"
when "material_release"
t 'material_releases.form.photos.dropzone_label'
when "music_release"
"To Add Audio Files to the release:<br>Drag & Drop Files<br>or<br>Click or Tap here to browse files"
when "location_release"
t 'location_releases.form.photos.dropzone_label'
when "directory"
"To Add Files to the Folder:<br>Drag & Drop Files<br>or<br>Click or Tap here to browse files"
else

View File

@@ -1,4 +1,3 @@
require 'zoom_gateway'
class AttachRecordingToZoomMeetingJob < ApplicationJob
queue_as :default

View File

@@ -0,0 +1,100 @@
# frozen_string_literal: true
class MatchAppearanceReleasesJob < ApplicationJob
queue_as :default
def perform(project, attachments)
filtered_attachments_object = filter_attachments attachments
return if filtered_attachments_object[:keys].blank?
matching_request = MatchingRequest.create project: project, attachments: filtered_attachments_object[:signed_ids]
payload = { request_id: matching_request.id, bucket: aws_bucket_name, files: filtered_attachments_object[:keys]}
response = BrayniacAI::QrMatching.create! payload
matches = response.matches || []
key_signed_id_hash = Hash[filtered_attachments_object[:keys].zip(filtered_attachments_object[:signed_ids])]
handle_matches matches, project, key_signed_id_hash
matching_request.destroy
end
private
def handle_matches(matches, project, key_signed_id_hash)
matches.each do |match|
contract_key = Array.wrap(match.contracts).first
headshot_key = Array.wrap(match.headshots).first
identifier = match.identifier
contract = key_signed_id_hash[contract_key]
headshot = key_signed_id_hash[headshot_key]
next if contract.nil? && headshot.nil?
identified_release = identifier.blank? ? nil : AppearanceRelease.find_by(identifier: identifier)
if identified_release.nil?
create_release project, contract, headshot, identifier
else
update_release identified_release, contract, headshot
end
end
end
def create_release(project, contract, headshot, identifier)
random_contract_no = AppearanceRelease.random_contract_number.to_s
is_incomplete = contract.nil? || headshot.nil?
params = {
project: project,
person_first_name: appearance_first_name(is_incomplete),
person_last_name: random_contract_no
}
params[:person_photo] = headshot unless headshot.nil?
params[:contract] = contract unless contract.nil?
params[:identifier] = identifier unless blank?
return if AppearanceRelease.create(params)
logger.error "Failed to create AppearanceRelease with params : \r\n#{params}"
end
def update_release(release, contract, headshot)
release.contract = contract unless contract.nil?
release.person_photo = headshot unless headshot.nil?
release.save
end
def appearance_first_name(incomplete)
if incomplete
I18n.t('appearance_releases.shared.incomplete_match')
else
I18n.t('appearance_releases.shared.matched_import')
end
end
def aws_bucket_name
ENV.fetch 'AWS_BUCKET'
end
def filter_attachments(attachments)
filtered_attachments_keys = []
filtered_attachments_signed_ids = []
attachments.each do |attachment|
blob = ActiveStorage::Blob.find_signed attachment
next if blob.nil?
extension = blob.filename.extension
next unless blob.image? || extension == 'pdf'
filtered_attachments_keys << blob.key
filtered_attachments_signed_ids << attachment
end
{
keys: filtered_attachments_keys,
signed_ids: filtered_attachments_signed_ids
}
end
end

View File

@@ -55,6 +55,8 @@ class Account < ApplicationRecord
User.joins(:project_memberships).where(project_memberships: { project: projects }),
Broadcast.where(project: projects),
ZoomMeeting.where(project: projects),
MedicalRelease.where(project: projects),
MatchingRequest.where(project: projects),
self
])).sum(:byte_size).to_f
end

View File

@@ -43,7 +43,8 @@ class AcquiredMediaRelease < ApplicationRecord
person_address_street1 person_address_street2 person_address_city person_address_state person_address_zip person_address_country
]
CATEGORIES = ["Artwork", "Film Footage", "Video Footage", "Still Photograph"].freeze
# CATEGORIES = ["Artwork", "Film Footage", "Video Footage", "Still Photograph"].freeze
CATEGORIES = ["Film Footage", "Video Footage", "Still Photograph"].freeze
def minor?
false

View File

@@ -39,6 +39,7 @@ class AppearanceRelease < ApplicationRecord
# These validations apply to releases being signed by a minor
with_options if: :minor? do
validates :guardian_first_name, :guardian_last_name, presence: true
validates :guardian_email, email: true, allow_blank: true
end
validates :person_photo, content_type: face_photo_acceptable_content_types

View File

@@ -8,7 +8,7 @@ class BlankContract
end
def to_pdf
kit = PDFKit.new(as_html)
kit = PDFKit.new(as_html, margin_right: 1, margin_left: 1, margin_top: 10, margin_bottom: 1)
kit.to_file("tmp/#{filename}")
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
module Attachable
extend ActiveSupport::Concern
included do
has_many_attached :attachments
end
end

View File

@@ -13,6 +13,7 @@ class ContractTemplate < ApplicationRecord
has_many :acquired_media_releases, dependent: :restrict_with_error
has_many :location_releases, dependent: :restrict_with_error
has_many :material_releases, dependent: :restrict_with_error
has_many :medical_releases, dependent: :restrict_with_error
monetize :fee_cents
has_rich_text :body
@@ -50,6 +51,10 @@ class ContractTemplate < ApplicationRecord
parent.present?
end
def archived?
archived_at.present?
end
def archive
update(archived_at: Time.zone.now)
end

View File

@@ -23,7 +23,7 @@ class HeadshotCollection
collection_uid: collection_uid.to_s,
bucket_name: aws_bucket_name,
ids_to_images: map_ids_to_images,
}.reject { |_, v| v.blank? }
}.reject { |k, v| v.blank? && k != :ids_to_images }
end
private

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
class MatchingRequest < ApplicationRecord
include Attachable
belongs_to :project
end

View File

@@ -0,0 +1,71 @@
class MedicalRelease < ApplicationRecord
include Contractable
include Notable
include Photoable
include Releasable
include Searchable
include Signable
include Syncable
include PersonName
composed_of :person_address,
class_name: "Address",
mapping: [
%w(person_address_street1 street1),
%w(person_address_street2 street2),
%w(person_address_city city),
%w(person_address_state state),
%w(person_address_zip zip),
%w(person_address_country country)
]
def self.face_photo_acceptable_content_types
["image/png", "image/jpeg"]
end
# These validations apply to all releases
validates :person_first_name, :person_last_name, presence: true
validates :person_email, email: true, allow_blank: true
acts_as_taggable_on :internal_tags, :tags
# These validations apply to releases created natively by the system (i.e. not imported from elsewhere)
with_options on: :native do
validates :signature, attached: true
end
# These validations apply to releases imported to the system from an outside source
with_options on: :non_native do
validates :contract, attached: true
end
searchable_on %i[
person_first_name person_last_name person_email person_phone
person_address_street1 person_address_street2 person_address_city person_address_state person_address_zip person_address_country
]
# All releases must respond to the following messages
def name
person_name
end
def filename_suffix
"#{person_last_name} #{person_first_name}"
end
def contact_person
@contact_person ||= Contact.new(person_name, person_address, person_email, person_phone)
end
def uses_edl?
false
end
def minor?
false
end
def contract_file_name
"#{project.name.parameterize}_#{contract_template.release_type}_#{(signed_at || created_at).strftime("%Y.%m.%d")}_#{release_number}_#{filename_suffix.parameterize}"
end
end

View File

@@ -3,8 +3,8 @@ class Project < ApplicationRecord
include Filterable
include Syncable
SIGNABLE_RELEASE_TYPES = %w(talent appearance acquired_media location material)
AVAILABLE_RELEASE_TYPES = %w(appearance location material acquired_media talent music)
SIGNABLE_RELEASE_TYPES = %w(talent appearance acquired_media location material medical)
AVAILABLE_RELEASE_TYPES = %w(appearance location material acquired_media talent music medical)
belongs_to :account
has_many :acquired_media_releases, dependent: :destroy
@@ -13,6 +13,7 @@ class Project < ApplicationRecord
has_many :material_releases, dependent: :destroy
has_many :music_releases, dependent: :destroy
has_many :talent_releases, dependent: :destroy
has_many :medical_releases, dependent: :destroy
has_many :videos, dependent: :destroy
has_many :imports, dependent: :destroy
has_many :contract_templates, dependent: :destroy
@@ -33,6 +34,7 @@ class Project < ApplicationRecord
material_release: false,
music_release: false,
talent_release: false,
medical_release: false,
video_analysis: false,
}
end
@@ -65,6 +67,7 @@ class Project < ApplicationRecord
material_release: true,
music_release: true,
talent_release: true,
medical_release: true,
video_analysis: true,
}
when "nat_geo"
@@ -76,6 +79,7 @@ class Project < ApplicationRecord
material_release: true,
music_release: true,
talent_release: true,
medical_release: true,
video_analysis: true,
}
else
@@ -117,7 +121,7 @@ class Project < ApplicationRecord
current_zoom_meeting = zoom_meetings.active.first
unless current_zoom_meeting.present?
zoom_user = ZoomUser.free.first || ZoomUser.create
zoom_user = ZoomUser.for_new_meeting
current_zoom_meeting = ZoomMeeting.create(zoom_user: zoom_user, project: self)
end

View File

@@ -24,7 +24,7 @@ class QrCode
end
end
def to_base64_png(width = 100, height = 100)
def to_base64_png(width = 200, height = 200)
_qr_code.as_png.resize(width, height).to_data_url
end

View File

@@ -1,5 +1,5 @@
class ReleasableParam
TYPES = %w(talent appearance location material acquired_media music)
TYPES = %w(talent appearance location material acquired_media music medical)
def initialize(params)
@params = params

View File

@@ -1,5 +1,3 @@
require 'zoom_gateway'
class ZoomMeeting < ApplicationRecord
belongs_to :project, optional: true
belongs_to :zoom_user

View File

@@ -1,19 +1,27 @@
require 'zoom_gateway'
require 'securerandom'
class ZoomUser < ApplicationRecord
has_many :zoom_meetings, dependent: :nullify
enum tier: [:basic, :pro]
after_create :create_api_user, if: -> { api_id.nil? }
before_destroy :abort_destroy
scope :current_account, -> { where(account_number: ZoomGateway.ACCOUNT_NUMBER) }
scope :free, -> { where.not(id: ZoomMeeting.active.pluck(:zoom_user_id)) }
def api_email
self.class.api_email(self.id)
end
def create_api_user
self.api_id = gateway.create_host(api_email)
retries ||= 0
self.api_id = gateway.create_host(self.class.generate_api_email)
self.account_number = ZoomGateway.ACCOUNT_NUMBER
save
rescue ZoomGateway::UserAlreadyExists => e
retries += 1
if retries < 3
retry # new api email will be generated automatically
else
raise e
end
end
def delete_api_user
@@ -22,9 +30,22 @@ class ZoomUser < ApplicationRecord
save
end
def current_account?
account_number == ZoomGateway.ACCOUNT_NUMBER
end
# Static methods
class << self
def api_email(id)
"host#{id}@directme"
def generate_api_email
"host#{SecureRandom.random_number(10000)}@directme"
end
def for_new_meeting
user = public_send(ZoomGateway.USER_TYPE_NAME).current_account.free.first
if user.nil?
user = public_send(ZoomGateway.USER_TYPE_NAME).current_account.create
end
user
end
end

View File

@@ -0,0 +1,37 @@
class MedicalReleasePolicy < ReleasePolicy
def create?
true
end
def show?
true
end
def update?
!record.native?
end
def destroy?
true
end
def edit_photos?
true
end
def index?
true
end
def update_photos?
edit_photos?
end
def tag_multiple?
true
end
def download_multiple?
true
end
end

View File

@@ -4,7 +4,7 @@
<div class="form-row">
<%= form.text_field :name, required: true, wrapper_class: "col-12" %>
</div>
<%= form.form_group :categories, label: { text: "Categories" } do %>
<%= form.form_group :categories, label: { text: "Licensed property type" } do %>
<% AcquiredMediaRelease::CATEGORIES.each do |category| %>
<%= form.check_box :categories, { multiple: true, label: category }, category, false %>
<% end %>

View File

@@ -2,6 +2,10 @@
<div class="page">
<% has_logo = local_assigns[:logo] %>
<table class="heading-table">
<tr>
<td>&nbsp;</td>
<td class="do-not-copy-warning"><strong><%= t '.do_not_copy_warning' %></strong></td>
</tr>
<tr>
<td>
<% if has_logo %>
@@ -11,17 +15,9 @@
<% end %>
</td>
<td>
<img src="<%= qr_codes[copy_index] %>" />
<img class="qr-code" src="<%= qr_codes[copy_index] %>" />
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td class="serial-number"><%= serial_numbers[copy_index] %></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><strong><%= t '.do_not_copy_warning' %></strong></td>
</tr>
</table>
<hr>
<% if contract_template.body.present? %>

View File

@@ -1,4 +1,4 @@
<%= bootstrap_form_for model, layout: :inline, remote: true do |form| %>
<%= form.file_field :files, direct_upload: true, multiple: true, accept: "*", hide_label: true, wrapper_class: "w-65 mr-2" %>
<%= form.file_field :files, direct_upload: true, multiple: true, accept: "*", hide_label: true, wrapper_class: "w-65 mr-2", id: "broadcast_files_#{token}" %>
<%= form.button fa_icon("upload", text: "Add File"), class: "btn btn-primary", type: :submit, data: { disable_with: fa_icon("spinner", text: "Adding File") } %>
<% end %>

View File

@@ -0,0 +1,21 @@
<div class="text-center pb-2" id="broadcast_file_form_<%= broadcast.token %>">
<% if controller.class.module_parent.to_s == "Public" %>
<%= render partial: "public/broadcasts/file_form", locals: { model: [broadcast], token: broadcast.token } %>
<% else %>
<%= render partial: "broadcasts/file_form", locals: { model: [broadcast.project, broadcast], token: broadcast.token } %>
<% end %>
</div>
<div class="overflow-auto mh-30">
<ul class="list-unstyled d-flex flex-column align-items-center text-center" id="broadcast_file_list_<%= broadcast.token %>">
<% if files.present? %>
<%= render partial: "broadcasts/file", collection: files %>
<% else %>
<li class="my-3">
Files will appear here.
</li>
<% end %>
</ul>
<div class="d-flex mt-2 justify-content-center" id="broadcast_files_pagination_<%= broadcast.token %>">
<%= will_paginate(files, param_name: 'files_page', params: { active_files_tab: broadcast.token }) if files.present? %>
</div>
</div>

View File

@@ -3,7 +3,13 @@
<meta name="project-id" content="<%= @project.id %>">
<% end %>
<% if @broadcast %>
<meta name="broadcast-token" content="<%= @broadcast.token %>">
<meta name="broadcast-token" current="true" content="<%= @broadcast.token %>">
<% end %>
<% # Subscribe to Action Cable for every broadcast including those in multi-view %>
<% @multi_view_broadcasts.each do |multi_view_broadcast| %>
<% if multi_view_broadcast.token != @broadcast.token %>
<meta name="broadcast-token" current="false" content="<%= multi_view_broadcast.token %>">
<% end %>
<% end %>
<% end %>
@@ -24,9 +30,7 @@
<div class="d-flex justify-content-between align-items-center">
<h1 class="h3 m-0"><%= @broadcast.name %></h1>
<div class="dropdown">
<a class="btn btn-light border dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Switch View
</a>
<%= 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>
<span class="dropdown-item active"><%= fa_icon("check", text: @broadcast.name.titleize) %></span>
@@ -72,13 +76,10 @@
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<a class="<%= class_string("nav-link", "active" => !params[:active_tab].present?) %>" href="#home" data-toggle="tab">Home</a>
<%= link_to "Home", "#home", class: class_string("nav-link", "active" => !params[:active_tab].present?), data: { toggle: "tab" } %>
</li>
<li class="nav-item">
<a class="<%= class_string("nav-link", "active" => params[:active_tab] == "files") %>" href="#files" data-toggle="tab">Files</a>
</li>
<li class="nav-item">
<a class="<%= class_string("nav-link", "active" => params[:active_tab] == "recordings") %>" href="#recordings" data-toggle="tab">Previous Sessions</a>
<%= link_to "Previous Sessions", "#recordings", class: class_string("nav-link", "active" => params[:active_tab] == "recordings"), data: { toggle: "tab" } %>
</li>
</ul>
</div>
@@ -118,29 +119,6 @@
<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' %>
</div>
<div class="<%= class_string("tab-pane fade show", "active" => params[:active_tab] == 'files') %>" id="files">
<div class="text-center pb-2" id="broadcast_file_form">
<% if controller.class.module_parent.to_s == "Public" %>
<%= render partial: "public/broadcasts/file_form", locals: { model: [@broadcast], token: @broadcast.token } %>
<% else %>
<%= render partial: "broadcasts/file_form", locals: { model: [@project, @broadcast] } %>
<% end %>
</div>
<div class="overflow-auto mh-30">
<ul class="list-unstyled d-flex flex-column align-items-center text-center" id="broadcast_file_list">
<% if @files.present? %>
<%= render partial: "broadcasts/file", collection: @files %>
<% else %>
<li class="my-3">
Files will appear here.
</li>
<% end %>
</ul>
<div class="d-flex mt-2 justify-content-center" id="broadcast_files_pagination">
<%= will_paginate(@files, params: { active_tab: 'files' }) if @files.present? %>
</div>
</div>
</div>
<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 } %>
@@ -149,5 +127,36 @@
</div>
</div>
</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>
<% 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 %>
<% 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>
<div>

View File

@@ -1,9 +1,9 @@
$("#broadcast_file_form").html("<%= j render(partial: "broadcasts/file_form", locals: { model: [@project, @broadcast] }) %>");
$("#broadcast_file_form_<%= @broadcast.token %>").html("<%= j render(partial: "broadcasts/file_form", locals: { model: [@project, @broadcast], token: @broadcast.token }) %>");
var file_id = "#<%= dom_id(@files.first) %>"
if ($("#broadcast_file_list").has(file_id).length == 0) {
$("#broadcast_file_list").html("<%= j render(partial: "broadcasts/file", collection: @files) %>");
$("#broadcast_files_pagination").html("<%= j will_paginate(@files) %>");
if ($("#broadcast_file_list_<%= @broadcast.token %>").has(file_id).length == 0) {
$("#broadcast_file_list_<%= @broadcast.token %>").html("<%= j render(partial: "broadcasts/file", collection: @files) %>");
$("#broadcast_files_pagination_<%= @broadcast.token %>").html("<%= j will_paginate(@files, param_name: 'files_page', params: { active_files_tab: @broadcast.token }) %>");
}
bsCustomFileInput.init();
bsCustomFileInput.init();

View File

@@ -2,21 +2,20 @@
<%= 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: "#guardian_clause", show_values: %w(appearance talent) }, 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(appearance talent), "#fee_field": %w(appearance talent location material acquired_media), "#exploitable_rights_fields": %w(appearance talent location material acquired_media), "#custom_fields": %w(medical) } }, class: "form-control custom-select" %>
</div>
<div class="form-row">
<div class="form-row" id="fee_field">
<%= form.number_field :fee, min:"0", max:"99999999", step: "0.01", prepend: "$", help: "Leave at $0.00 for no-fee", wrapper_class: "col-sm-6" %>
</div>
<% end %>
<hr>
<%= field_set_tag content_tag(:span, t(".exploitable_rights.heading"), class: "h6 text-muted text-uppercase")do %>
<%= field_set_tag content_tag(:span, t(".exploitable_rights.heading"), class: "h6 text-muted text-uppercase"), id: "exploitable_rights_fields" do %>
<%= render "shared/exploitable_rights_fields", form: form %>
<hr>
<% end %>
<hr>
<%= field_set_tag content_tag(:span, t(".legal.heading"), class: "h6 text-muted text-uppercase") do %>
<%= form.form_group do %>
<%= form.rich_text_area :body %>
@@ -28,6 +27,12 @@
</div>
<% end %>
<%= field_set_tag content_tag(:span, t(".custom_fields.heading"), class: "h6 text-muted text-uppercase"), id: "custom_fields", style: "display: none;" do %>
<p class="alert alert-info"><%= fa_icon("info-circle", text: t(".custom_fields.instructions")) %></p>
<%= render "shared/custom_fields", form: form %>
<hr>
<% end %>
<div class="row align-items-center text-center mt-4">
<%= link_to t("shared.cancel"), [project, :contract_templates], class: "col-3 text-reset" %>
<div class="col-3">

View File

@@ -29,6 +29,7 @@
<% if releasable.model_name == "LocationRelease" %>
<%= description_list_pair "Filming Started On:", releasable&.filming_started_on&.strftime("%D") %>
<%= description_list_pair "Filming Ended On:", releasable&.filming_ended_on&.strftime("%D") %>
<%= description_list_pair "Filming Hours:", releasable&.filming_hours %>
<% end %>
<% if contract_template.fee? %>
<%= description_list_pair "Fee:", number_to_currency(contract_template.fee) %>
@@ -38,6 +39,15 @@
<% end %>
</dl>
<% if releasable.model_name == "MedicalRelease" %>
<% (1..10).each do |n| %>
<% if contract_template.public_send("question_#{n}_text").present? %>
<p><strong><%= contract_template.public_send("question_#{n}_text") %></strong></p>
<p><%= releasable.public_send("question_#{n}_answer") %></p>
<% end %>
<% end %>
<% end %>
<% if releasable.minor? %>
<br/>
<p class="text-left"><strong>Guardian Information</strong></p>
@@ -55,6 +65,7 @@
<%= description_list_pair_for releasable, :guardian_name, append: ":" %>
<%= description_list_pair_for releasable, :guardian_address, append: ":" %>
<%= description_list_pair_for releasable, :guardian_phone, append: ":" %>
<%= description_list_pair_for releasable, :guardian_email, append: ":" %>
<%= description_list_pair_for releasable, :signed_on, append: ":" %>
</dl>

View File

@@ -11,14 +11,10 @@
<%= field_set_tag content_tag(:span, t(".signer_details.heading"), class: "h6 text-muted text-uppercase") do %>
<div class="form-row">
<%= form.text_field :person_first_name, wrapper_class: "col-sm-3" %>
<%= form.text_field :person_last_name, wrapper_class: "col-sm-3" %>
<%= form.text_field :person_first_name, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_last_name, wrapper_class: "col-sm-6" %>
<%= form.phone_field :person_phone, wrapper_class: "col-sm-6" %>
</div>
<div class="form-row">
<%= form.email_field :person_email, wrapper_class: "col-sm-6" %>
</div>
<div class="form-row">
<%= form.text_field :person_company, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_title, wrapper_class: "col-sm-6" %>
</div>
@@ -38,6 +34,7 @@
<div class="form-row">
<%= form.text_field :filming_started_on, wrapper_class: "col-sm-6", class: "datepicker-control", readonly: true %>
<%= form.text_field :filming_ended_on, wrapper_class: "col-sm-6", class: "datepicker-control", readonly: true %>
<%= form.text_field :filming_hours, wrapper_class: "col-sm-12" %>
</div>
<% end %>

View File

@@ -13,14 +13,10 @@
<%= field_set_tag content_tag(:span, t(".signer_details.heading"), class: "h6 text-muted text-uppercase") do %>
<div class="form-row">
<%= form.text_field :person_first_name, wrapper_class: "col-sm-3" %>
<%= form.text_field :person_last_name, wrapper_class: "col-sm-3" %>
<%= form.text_field :person_first_name, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_last_name, wrapper_class: "col-sm-6" %>
<%= form.phone_field :person_phone, wrapper_class: "col-sm-6" %>
</div>
<div class="form-row">
<%= form.email_field :person_email, wrapper_class: "col-sm-6" %>
</div>
<div class="form-row">
<%= form.text_field :person_company, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_title, wrapper_class: "col-sm-6" %>
</div>

View File

@@ -0,0 +1,49 @@
<tr id="<%= dom_id(medical_release) %>">
<td data-behavior="select"><%= check_box_tag "medical_release_ids[]", medical_release.id, false %></td>
<td>
<% if medical_release.photo.attached? %>
<%= image_tag medium_variant(medical_release.photo), class: "img-fluid" %>
<% end %>
</td>
<td>
<%= medical_release.name %>
</td>
<td>
<%= contact_info(
address: medical_release.person_address,
phone: medical_release.person_phone,
email: medical_release.person_email
) %>
</td>
<td>
<%= notes_preview medical_release.notes.order_by_recent %>
</td>
<td id="<%= dom_id medical_release, "tags_preview" %>">
<%= tags_preview medical_release, medical_release.tags %>
</td>
<td>
<%= medical_release.signed_on %>
</td>
<td class="text-right">
<div class="btn-group">
<%= button_tag t(".actions.manage"), class: "btn btn-light btn-sm dropdown-toggle border", data: { toggle: "dropdown", boundary: "window" }, aria: { haspopup: true, expanded: false } %>
<div class="dropdown-menu dropdown-menu-right">
<% if policy(Note).new? %>
<%= link_to fa_icon("sticky-note fw", text: "Notes"), [:new, medical_release, :note], class: "dropdown-item", remote: true %>
<% end %>
<% if policy(medical_release.tags).new? %>
<%= link_to fa_icon("tags fw", text: "Tags"), [:new, medical_release, :acts_as_taggable_on_tag], class: "dropdown-item", remote: true %>
<% end %>
<% if policy(Contract).show? && (medical_release.contract.attached? || medical_release.contract_template.present?) %>
<%= link_to fa_icon("download fw", text: "Download"), [medical_release, :contracts, format: "pdf"], class: "dropdown-item", target: "_blank" %>
<% end %>
<% if policy(medical_release).destroy? %>
<%= link_to fa_icon("trash fw", text: "Delete"), medical_release, class: "dropdown-item", method: :delete, data: { confirm: "Are you sure?" } %>
<% end %>
</div>
</div>
</td>
</tr>

View File

@@ -0,0 +1,48 @@
<div class="row">
<div class="col-md-12">
<div class="d-md-flex d-sm-flex flex-sm-column flex-md-row flex-md-wrap mb-3">
<% if @medical_releases.any? && policy(MedicalRelease).tag_multiple? %>
<%= button_to_bulk_tagging(@project) %>
<% end %>
<% if @medical_releases.any? && policy(MedicalRelease).download_multiple? %>
<%= link_to "Download All", [@project, :contract_downloads, release_type: @medical_releases.name], method: :post, remote: true, class: "btn btn-light border ml-auto mr-2 mb-2", data: {
disable_with: "Please wait..." } %>
<% end %>
<%= bootstrap_form_with url: [@project, :medical_releases], 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 mb-2 rounded-pill-left") %>
<% end %>
</div>
</div>
</div>
<div class="border bg-white rounded shadow-sm pb-3 table-responsive">
<table class="table table-striped tr-px-4 align-all-middle">
<thead class="thead-light">
<tr>
<th data-behavior="all-selectable"><%= check_box_tag "medical_release_ids[]", false, false %></th>
<th></th>
<th><%= MedicalRelease.human_attribute_name(:person_name) %></th>
<th><%= MedicalRelease.human_attribute_name(:contact_info) %></th>
<th><%= t(".table_headers.notes") %></th>
<th><%= t(".table_headers.tags") %></th>
<th><%= t(".table_headers.signed_at") %></th>
<th></th>
</tr>
</thead>
<tbody id="medical_releases">
<% if @medical_releases.any? %>
<%= render @medical_releases %>
<% else %>
<tr>
<td colspan="12" class="py-4 text-center text-muted"><%= t(".empty") %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<div id="medical_releases_pagination" class="mt-3">
<%= will_paginate @medical_releases %>
</div>

View File

@@ -0,0 +1,3 @@
$("#medical_releases").html("<%= j render(@medical_releases) %>");
$("form input[type='search']").val("<%= params[:query] %>");
$("#medical_releases_pagination").html("<%= j will_paginate(@medical_releases) %>");

View File

@@ -1,6 +1,6 @@
<%= bootstrap_form_with model: project, local: true do |form| %>
<%= form.text_field :name %>
<%= form.select :predefined_client_name, options_for_select(options_for_predefined_client_name_select, selected_project_client_value(project)), {}, data: { toggle: "collapse-select", target: "#other_client", show_values: [:other] }, class: "form-control custom-select" %>
<%= form.select :predefined_client_name, options_for_select(options_for_predefined_client_name_select, selected_project_client_value(project)), {}, data: { toggle: "collapse-select", target_show_values_mapping: { "#other_client": [:other] } }, class: "form-control custom-select" %>
<div id="other_client" style="<%='display: none' if selected_project_client_value(project) != 'other'%>">
<%= form.text_field :client_name, placeholder: true %>
<%= form.form_group do %>

View File

@@ -15,12 +15,12 @@
<%= card_field_set_tag t(".acquired_media_info.heading") do %>
<div class="form-row">
<%= form.text_field :name, required: true, wrapper_class: "col-12", label: 'Licensor ("Owner")' %>
<%= form.text_field :name, required: true, wrapper_class: "col-12" %>
</div>
<div class="form-row">
<%= form.text_area :description, wrapper_class: "col-12" %>
</div>
<%= form.form_group :categories, label: { text: "Categories" } do %>
<%= form.form_group :categories, label: { text: "Licensed property type" } do %>
<% AcquiredMediaRelease::CATEGORIES.each do |category| %>
<%= form.check_box :categories, { multiple: true, label: category }, category, false %>
<% end %>
@@ -31,8 +31,11 @@
<%= card_field_set_tag t(".personal_info.heading") do %>
<div class="form-row">
<%= form.text_field :person_first_name, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_last_name, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_title, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_phone, wrapper_class: "col-sm-6", label: 'Phone' %>
<%= form.phone_field :person_phone, wrapper_class: "col-sm-6", label: 'Phone' %>
<%= form.email_field :person_email, wrapper_class: "col-sm-6", label: 'Email' %>
<%= form.text_field :person_fax, wrapper_class: "col-sm-6", label: 'Fax' %>
</div>
<%= render "shared/address_fields", form: form, subject: "person" %>

View File

@@ -76,6 +76,9 @@
<%= form.text_field :guardian_last_name, required: @appearance_release.minor?, wrapper_class: "col-sm-3" %>
<%= form.phone_field :guardian_phone, wrapper_class: "col-sm-6" %>
</div>
<div class="form-row">
<%= form.text_field :guardian_email, wrapper_class: "col-sm-6" %>
</div>
<div class="form-row">
<%= form.text_field :guardian_address, wrapper_class: "col-sm-6" %>
</div>

View File

@@ -1,4 +1,4 @@
<%= bootstrap_form_for model, url: broadcast_url(token: token), layout: :inline, remote: true do |form| %>
<%= form.file_field :files, direct_upload: true, multiple: true, accept: "*", hide_label: true, wrapper_class: "w-65 mr-2" %>
<%= form.file_field :files, direct_upload: true, multiple: true, accept: "*", hide_label: true, wrapper_class: "w-65 mr-2", id: "broadcast_files_#{token}" %>
<%= form.button fa_icon("upload", text: "Add File"), class: "btn btn-primary", type: :submit, data: { disable_with: fa_icon("spinner", text: "Adding File") } %>
<% end %>

View File

@@ -1,9 +1,9 @@
$("#broadcast_file_form").html("<%= j render(partial: "public/broadcasts/file_form", locals: { model: [@project, @broadcast], token: @broadcast.token }) %>");
$("#broadcast_file_form_<%= @broadcast.token %>").html("<%= j render(partial: "public/broadcasts/file_form", locals: { model: [@project, @broadcast], token: @broadcast.token }) %>");
var file_id = "#<%= dom_id(@files.first) %>"
if ($("#broadcast_file_list").has(file_id).length == 0) {
$("#broadcast_file_list").html("<%= j render(partial: "broadcasts/file", collection: @files) %>");
$("#broadcast_files_pagination").html("<%= j will_paginate(@files) %>");
if ($("#broadcast_file_list_<%= @broadcast.token %>").has(file_id).length == 0) {
$("#broadcast_file_list_<%= @broadcast.token %>").html("<%= j render(partial: "broadcasts/file", collection: @files) %>");
$("#broadcast_files_pagination_<%= @broadcast.token %>").html("<%= j will_paginate(@files) %>");
}
bsCustomFileInput.init();

View File

@@ -27,11 +27,7 @@
<%= form.text_field :person_first_name, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_last_name, wrapper_class: "col-sm-6" %>
<%= form.phone_field :person_phone, wrapper_class: "col-sm-6" %>
</div>
<div class="form-row">
<%= form.email_field :person_email, wrapper_class: "col-sm-6" %>
</div>
<div class="form-row">
<%= form.text_field :person_company, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_title, wrapper_class: "col-sm-6" %>
</div>
@@ -45,9 +41,14 @@
<div class="form-row">
<%= form.text_field :filming_started_on, wrapper_class: "col-sm-6", class: "datepicker-control", readonly: true %>
<%= form.text_field :filming_ended_on, wrapper_class: "col-sm-6", class: "datepicker-control", readonly: true %>
<%= form.text_field :filming_hours, wrapper_class: "col-sm-12" %>
</div>
<% end %>
<%= card_field_set_tag t(".photos.heading") do %>
<%= render "shared/photos_dropzone_fields", form: form, release: @location_release %>
<% end %>
<%= card_field_set_tag t(".signature.heading") do %>
<%= render "shared/signature_fields", form: form, instruction: 'An Authorized Signatory' %>
<% end %>

View File

@@ -27,17 +27,17 @@
<%= form.text_field :person_first_name, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_last_name, wrapper_class: "col-sm-6" %>
<%= form.phone_field :person_phone, wrapper_class: "col-sm-6" %>
</div>
<div class="form-row">
<%= form.email_field :person_email, wrapper_class: "col-sm-6" %>
</div>
<div class="form-row">
<%= form.text_field :person_company, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_title, wrapper_class: "col-sm-6" %>
</div>
<%= render "shared/address_fields", form: form, subject: "person" %>
<% end %>
<%= card_field_set_tag t(".photo.heading") do %>
<%= render "shared/photos_dropzone_fields", form: form, release: @material_release %>
<% end %>
<hr>
<%= card_field_set_tag t(".signature.heading") do %>

View File

@@ -0,0 +1 @@
<p class="alert alert-success p-3 lead text-center">Your release was successfully submitted. Thank you.</p>

View File

@@ -0,0 +1,56 @@
<div class="card shadow-sm">
<div class="card-body">
<%= errors_summary_for @medical_release %>
<%= 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>
<% if @contract_template.fee? %>
<p>
Fee <span class="font-weight-bold text-success"><%= number_to_currency @contract_template.fee %></span>
</p>
<% end %>
<% end %>
<hr>
<% if (1..10).map {|n| @contract_template.public_send("question_#{n}_text").presence }.compact.any? %>
<%= card_field_set_tag t(".questionnaire.heading") do %>
<% (1..10).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">
<%= form.text_field :person_first_name, required: true, wrapper_class: "col-sm-6" %>
<%= form.text_field :person_last_name, required: true, wrapper_class: "col-sm-6" %>
<%= form.phone_field :person_phone, wrapper_class: "col-sm-6" %>
<%= form.email_field :person_email, wrapper_class: "col-sm-6" %>
</div>
<%= render "shared/address_fields", form: form, subject: "person" %>
<% end %>
<hr>
<%= card_field_set_tag t(".photo.heading") do %>
<%= render "shared/photos_dropzone_fields", form: form, release: @medical_release %>
<% end %>
<%= card_field_set_tag t(".signature.heading") do %>
<%= render "shared/signature_fields", form: form %>
<% end %>
<div class="mt-5">
<%= form.button t("shared.submit_release_short"), class: "btn btn-block btn-lg btn-success", data: { disable_with: t("shared.disable_with") } %>
</div>
<% end %>
</div>
</div>

View File

@@ -11,6 +11,6 @@
</div>
<%= form.form_group "#{field_name_prefix}address_country" do %>
<%= form.label "#{field_name_prefix}address_country" %>
<%= form.country_select "#{field_name_prefix}address_country", { priority: %w(US CA), prompt: true }, class: "form-control custom-select" %>
<%= form.country_select "#{field_name_prefix}address_country", { selected: 'US', priority: %w(US CA), prompt: true }, class: "form-control custom-select" %>
<% end %>

View File

@@ -0,0 +1,5 @@
<% (1..10).each do |n| %>
<div class="form-row">
<%= form.text_area "question_#{n}_text", wrapper_class: "col-sm-12" %>
</div>
<% end%>

View File

@@ -1,4 +1,6 @@
require 'zoom'
require 'zoom_gateway'
unless Rails.env.test?
Zoom.configure do |c|
c.api_key = ENV['ZOOM_API_KEY']

View File

@@ -112,6 +112,7 @@ en:
no_photos: Needs Photo
create:
failed_import: Failed to create appearance release for files listed below
matching_started: Matching started
no_attachments: Failed to import - no attachments
edit:
heading: Edit Appearance Release
@@ -141,6 +142,8 @@ en:
shared:
imported_appearance_release_contract_name: Imported Contract
imported_appearance_release_headshot_name: Imported Headshot
incomplete_match: Incomplete Match
matched_import: Complete Match
type_filter_actions:
all_releases: All Releases
complete_releases: Complete Releases
@@ -212,12 +215,15 @@ en:
archived_failure: Failed to archive the release template
archived_notice: The release template has been archived
form:
custom_fields:
heading: Medical Questionnaire
instructions: Please list the questions you wish the signer to answer. Any unused question fields will be hidden.
exploitable_rights:
heading: 2 of 3 Exploitable Rights
heading: Exploitable Rights
legal:
heading: 3 of 3 Legal
heading: Legal
release_info:
heading: 1 of 3 Release Info
heading: Release Info
index:
actions:
import: Import Release Template
@@ -305,6 +311,8 @@ en:
graphics_only_edl_file: If you do not upload a Graphics Only EDL, the software will not generate a Graphics Cue List.
label:
acquired_media_release:
description: Description of licensed property
name: Name of licensed property
person_address: Address
person_address_city: City
person_address_country: Country
@@ -314,29 +322,41 @@ en:
person_address_zip: Zip code
person_company: Company
person_email: Email address
person_first_name: Licensor/Owner first name
person_last_name: Licensor/Owner last name
person_name: Name
person_phone: Phone number
person_title: Title
appearance_release:
minor: Is the person a minor?
person_address: Address
person_date_of_birth: Date of birth
person_email: Email address
person_first_name: First name
person_last_name: Last name
person_name: Name
person_phone: Phone number
location_release:
address_city: City
address_country: Country
address_state: State
address_street1: Address
address_street1: Address of Property
address_street2: Address (Line 2)
address_zip: Zip code
filming_ended_on: Filming end date
filming_started_on: Filming start date
name: Name of property
person_address_city: City
person_address_country: Country
person_address_state: State
person_address_street1: Address
person_address_street2: Address (Line 2)
person_address_zip: Zip code
person_first_name: Owner first name
person_last_name: Owner last name
material_release:
description: Description of licensed material
name: Name of licensed material
person_address: Address
person_address_city: City
person_address_country: Country
@@ -346,6 +366,8 @@ en:
person_address_zip: Zip code
person_company: Company
person_email: Email address
person_first_name: Licensor/Owner first name
person_last_name: Licensor/Owner last name
person_name: Name
person_phone: Phone number
person_title: Title
@@ -542,9 +564,10 @@ en:
location_details:
heading: 1 of 4 Location Details
photos:
dropzone_label: Tap to take a photo of the Property (optional)
heading: 4 of 4 Photos
signer_details:
heading: 2 of 4 Signer Details
heading: 2 of 4 Owner Details
index:
actions:
new: Import Release
@@ -576,9 +599,10 @@ en:
material_details:
heading: 1 of 3 Material Details
photos:
dropzone_label: Tap to take a photo of Licensed Material (optional)
heading: 4 of 4 Photos
signer_details:
heading: 2 of 4 Signer Details
heading: 2 of 4 Licensor/Owner Details
index:
actions:
new: Import Release
@@ -596,6 +620,20 @@ en:
heading: Import Material Release (Products / Logos)
update:
notice: The material release has been updated
medical_releases:
destroy:
alert: The medical release has been deleted
index:
actions:
search: Search
empty: Medical releases will appear here
table_headers:
notes: Notes
signed_at: Date Signed
tags: Tags
medical_release:
actions:
manage: Manage
music_releases:
create:
notice: The music release has been created
@@ -701,6 +739,7 @@ en:
label: Which release categories do you require for this project?
location_release: Location Releases
material_release: Material Releases (Products / Logos)
medical_release: Medical Releases
music_release: Music Releases (Original Music)
talent_release: Talent Releases
index:
@@ -727,6 +766,7 @@ en:
downloads: Downloads
location_release: Location Releases (%{count})
material_release: Material Releases (%{count})
medical_release: Medical Releases (%{count})
music_release: Music Releases (%{count})
report: Reports
talent_release: Talent Releases (%{count})
@@ -741,9 +781,7 @@ en:
legal:
heading: Legal
personal_info:
heading: Person Details
personal_info:
heading: Signer's Contact Information
heading: Licensor/Owner Contact Information
signature:
heading: Signature
appearance_releases:
@@ -760,17 +798,17 @@ en:
instructions: >
Lastly, it's time for guardian to take a selfie photo! Please remove your hat and sunglasses (regular eyewear is ok), make sure that you are the only person in the photo, look straight into the camera, and say Cheese!
instructions_html: >
Congrats in appearing on <em>%{name}</em>. Below is the appearance release form. After scrolling down and reading the appearance release form, please enter your personal information, take a selfie photo, and press the "Submit Release" button.
Congrats on appearing in <em>%{name}</em>. After scrolling down and reading the appearance release form, please enter your personal information, take a selfie photo and press the "Submit Release" button. If the person appearing on camera is a minor, you will enter their personal information first, select the button noting this person is a minor, and then enter your own personal info under "Guardian Information" at the bottom.
legal:
heading: Legal
personal_info:
heading: Personal Information
instructions: Now, enter your personal information.
instructions: Enter the personal information for the person appearing on camera.
photo:
camera_instructions_html: Click <em>Take Photo</em> to Turn ON Camera
heading: Take a Photo
heading: Photo of Person Appearing on Camera
instructions: >
Lastly, it's time for you to take a selfie photo! Please remove your hat and sunglasses (regular eyewear is ok), make sure that you are the only person in the photo, look straight into the camera, and say Cheese!
'Before you take your selfie photo, remove your hat and sunglasses (regular eyewear is okay). Make sure you are the only person in the frame, look straight at the camera and snap the photo. NOTE: If you are a Guardian, you must take the photo of the minor. Minor photo is required only for record keeping purposes to maintain proof of parental consent and to identify minor within the Program.'
no_photo: No photo yet
take_photo: Take Photo
warning: If your photo appears sideways, it will be autocorrected when you submit your release.
@@ -782,13 +820,15 @@ en:
new:
cancel: Cancel
contact_info:
heading: Signer Information
heading: Owner Contact Information
filming_info:
heading: Filming Information
legal:
heading: Legal
location_info:
heading: Location Information
photos:
heading: Photos
signature:
heading: Sign Below
material_releases:
@@ -797,13 +837,33 @@ en:
new:
cancel: Cancel
contact_info:
heading: Signer's Contact Information
heading: Licensor/Owner Contact Information
legal:
heading: Legal
photo:
heading: Photos
release_info:
heading: Release Information
signature:
heading: Sign Below
medical_releases:
create:
notice: Your release has been signed. Thank you!
new:
cancel: Cancel
instructions_html: >
Below is the medical release form. After scrolling down and reading the medical release form, please enter your personal information, take a photo, and press the "Submit Release" button.
legal:
heading: Legal
personal_info:
heading: Personal Information
instructions: Now, enter your personal information.
photo:
heading: Photos
questionnaire:
heading: Questionnaire
signature:
heading: Signature
talent_releases:
create:
notice: Your release has been signed. Thank you!
@@ -891,6 +951,7 @@ en:
search: Search
submit_release: Submit Release
submit_release_long: I have read and agree to the above
submit_release_short: Submit
suite: Suite
tag_multiple_releases: Add Tag
tag_multiple_releases_form:

View File

@@ -25,6 +25,7 @@ es:
appearance_releases:
create:
failed_import: Failed to create appearance release for files listed below (ES)
matching_started: Matching started (ES)
no_attachments: Failed to import - no attachments (ES)
form:
photos:
@@ -37,6 +38,8 @@ es:
shared:
imported_appearance_release_contract_name: Contrato Importado
imported_appearance_release_headshot_name: Retrato Importado
incomplete_match: Incomplete Match (ES)
matched_import: Complete Match (ES)
type_filter_actions:
all_releases: All Releases (ES)
complete_releases: Complete Releases (ES)
@@ -52,6 +55,15 @@ es:
blank_contracts:
create:
number_of_copies_invalid_notice: Please enter valid number greater than 0 (ES)
form:
custom_fields:
heading: Medical Questionnaire (ES)
exploitable_rights:
heading: Exploitable Rights (ES)
legal:
heading: Legal (ES)
release_info:
heading: Release Info (ES)
contracts:
photos:
guardian_photo_heading: Guardian photo (ES)
@@ -132,6 +144,14 @@ es:
update: Save Changes (ES)
create: 'Crear %{model}'
update: 'Actualizar %{model}'
location_releases:
form:
photos:
dropzone_label: Tap to take a photo of the Property (optional) (ES)
material_releases:
form:
photos:
dropzone_label: Tap to take a photo of Licensed Material (optional) (ES)
public:
appearance_releases:
create:
@@ -160,6 +180,14 @@ es:
clear: Despejar
heading: Firma
instructions: 'Firma Abajo:'
location_releases:
new:
photos:
heading: Photos (ES)
material_releases:
new:
photo:
heading: Photos (ES)
talent_releases:
new:
guardian_clause:

View File

@@ -53,6 +53,7 @@ Rails.application.routes.draw do
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]
resources :contract_templates, only: [:index, :new, :create, :destroy] do
resource :qr_codes, only: [:show], controller: "contract_templates/qr_codes"
resource :blank_contracts, only: [:show, :new, :create], controller: "contract_templates/blank_contracts"
@@ -117,6 +118,7 @@ Rails.application.routes.draw do
resources :acquired_media_releases, only: [:new, :create]
resources :location_releases, only: [:new, :create]
resources :material_releases, only: [:new, :create]
resources :medical_releases, only: [:new, :create]
end
end
end
@@ -126,7 +128,7 @@ Rails.application.routes.draw do
end
RELEASES = [:acquired_media_releases, :appearance_releases, :talent_releases, :material_releases, :location_releases]
ALL_RELEASES = RELEASES + [:music_releases]
ALL_RELEASES = RELEASES + [:music_releases, :medical_releases]
ALL_RELEASES.each do |release|
resources release, only: [], concerns: :taggable
@@ -161,6 +163,7 @@ Rails.application.routes.draw do
resources :appearance_releases, only: [:show] do
resources :notes, controller: "notes", only: [:create, :index]
end
resources :direct_uploads, only: [:create]
end
end

View File

@@ -0,0 +1,7 @@
class AssignAccountNumberAndTypeToZoomUsers < ActiveRecord::DataMigration
def up
ZoomUser.find_each do |zu|
zu.update account_number: ENV.fetch('ZOOM_ACCOUNT_NUMBER'), tier: (ENV['ZOOM_USER_TYPE'] == 'pro' ? 1 : 0)
end
end
end

View File

@@ -0,0 +1,9 @@
class CreateMatchingRequests < ActiveRecord::Migration[6.0]
def change
create_table :matching_requests do |t|
t.belongs_to :project, foreign_key: true
t.timestamps
end
end
end

View File

@@ -0,0 +1,5 @@
class AddIdentifierToAppearanceReleases < ActiveRecord::Migration[6.0]
def change
add_column :appearance_releases, :identifier, :string, default: nil
end
end

View File

@@ -0,0 +1,6 @@
class AddAccountNumberAndTypeToZoomUsers < ActiveRecord::Migration[6.0]
def change
add_column :zoom_users, :account_number, :string
add_column :zoom_users, :tier, :integer, default: 0
end
end

View File

@@ -0,0 +1,5 @@
class AddFilmingHoursToLocationReleases < ActiveRecord::Migration[6.0]
def change
add_column :location_releases, :filming_hours, :text
end
end

View File

@@ -0,0 +1,23 @@
class CreateMedicalReleases < ActiveRecord::Migration[6.0]
def change
create_table :medical_releases do |t|
t.belongs_to :project, foreign_key: true
t.belongs_to :contract_template, foreign_key: true
t.string :person_first_name
t.string :person_last_name
t.string :person_address_street1
t.string :person_address_street2
t.string :person_address_city
t.string :person_address_state
t.string :person_address_zip
t.string :person_address_country
t.string :person_phone
t.string :person_email
t.string :locale
t.text :notes
t.datetime :signed_at
t.timestamps
end
end
end

View File

@@ -0,0 +1,14 @@
class AddQuestionFieldsToContractTemplates < ActiveRecord::Migration[6.0]
def change
add_column :contract_templates, :question_1_text, :text
add_column :contract_templates, :question_2_text, :text
add_column :contract_templates, :question_3_text, :text
add_column :contract_templates, :question_4_text, :text
add_column :contract_templates, :question_5_text, :text
add_column :contract_templates, :question_6_text, :text
add_column :contract_templates, :question_7_text, :text
add_column :contract_templates, :question_8_text, :text
add_column :contract_templates, :question_9_text, :text
add_column :contract_templates, :question_10_text, :text
end
end

View File

@@ -0,0 +1,14 @@
class AddAnswerFieldsToMedicalReleases < ActiveRecord::Migration[6.0]
def change
add_column :medical_releases, :question_1_answer, :text
add_column :medical_releases, :question_2_answer, :text
add_column :medical_releases, :question_3_answer, :text
add_column :medical_releases, :question_4_answer, :text
add_column :medical_releases, :question_5_answer, :text
add_column :medical_releases, :question_6_answer, :text
add_column :medical_releases, :question_7_answer, :text
add_column :medical_releases, :question_8_answer, :text
add_column :medical_releases, :question_9_answer, :text
add_column :medical_releases, :question_10_answer, :text
end
end

View File

@@ -0,0 +1,5 @@
class AddGuardianEmailToAppearanceReleases < ActiveRecord::Migration[6.0]
def change
add_column :appearance_releases, :guardian_email, :string
end
end

View File

@@ -0,0 +1,5 @@
class RemoveBroadcastIdFromZoomMeetings < ActiveRecord::Migration[6.0]
def change
remove_column :zoom_meetings, :broadcast_id
end
end

View File

@@ -50,7 +50,7 @@ $_$;
SET default_tablespace = '';
SET default_table_access_method = heap;
SET default_with_oids = false;
--
-- Name: account_auths; Type: TABLE; Schema: public; Owner: -
@@ -316,7 +316,9 @@ CREATE TABLE public.appearance_releases (
person_first_name character varying,
person_last_name character varying,
guardian_first_name character varying,
guardian_last_name character varying
guardian_last_name character varying,
identifier character varying,
guardian_email character varying
);
@@ -592,7 +594,17 @@ CREATE TABLE public.contract_templates (
term_text character varying,
restriction_id bigint,
restriction_text character varying,
archived_at timestamp without time zone
archived_at timestamp without time zone,
question_1_text text,
question_2_text text,
question_3_text text,
question_4_text text,
question_5_text text,
question_6_text text,
question_7_text text,
question_8_text text,
question_9_text text,
question_10_text text
);
@@ -615,15 +627,6 @@ CREATE SEQUENCE public.contract_templates_id_seq
ALTER SEQUENCE public.contract_templates_id_seq OWNED BY public.contract_templates.id;
--
-- Name: data_migrations; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.data_migrations (
version character varying NOT NULL
);
--
-- Name: directories; Type: TABLE; Schema: public; Owner: -
--
@@ -846,7 +849,8 @@ CREATE TABLE public.location_releases (
filming_started_on date,
filming_ended_on date,
person_first_name character varying,
person_last_name character varying
person_last_name character varying,
filming_hours text
);
@@ -869,6 +873,37 @@ CREATE SEQUENCE public.location_releases_id_seq
ALTER SEQUENCE public.location_releases_id_seq OWNED BY public.location_releases.id;
--
-- Name: matching_requests; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.matching_requests (
id bigint NOT NULL,
project_id bigint,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL
);
--
-- Name: matching_requests_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.matching_requests_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: matching_requests_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public.matching_requests_id_seq OWNED BY public.matching_requests.id;
--
-- Name: material_releases; Type: TABLE; Schema: public; Owner: -
--
@@ -927,6 +962,61 @@ CREATE SEQUENCE public.material_releases_id_seq
ALTER SEQUENCE public.material_releases_id_seq OWNED BY public.material_releases.id;
--
-- Name: medical_releases; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.medical_releases (
id bigint NOT NULL,
project_id bigint,
contract_template_id bigint,
person_first_name character varying,
person_last_name character varying,
person_address_street1 character varying,
person_address_street2 character varying,
person_address_city character varying,
person_address_state character varying,
person_address_zip character varying,
person_address_country character varying,
person_phone character varying,
person_email character varying,
locale character varying,
notes text,
signed_at timestamp without time zone,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL,
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
);
--
-- Name: medical_releases_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.medical_releases_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: medical_releases_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public.medical_releases_id_seq OWNED BY public.medical_releases.id;
--
-- Name: music_releases; Type: TABLE; Schema: public; Owner: -
--
@@ -1181,6 +1271,7 @@ CREATE TABLE public.settings (
--
CREATE SEQUENCE public.settings_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
@@ -1216,6 +1307,7 @@ CREATE TABLE public.taggings (
--
CREATE SEQUENCE public.taggings_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
@@ -1246,6 +1338,7 @@ CREATE TABLE public.tags (
--
CREATE SEQUENCE public.tags_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
@@ -1558,7 +1651,6 @@ CREATE TABLE public.zoom_meetings (
api_meeting_id character varying,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL,
broadcast_id bigint,
zoom_user_id bigint,
project_id bigint,
status integer DEFAULT 0
@@ -1592,7 +1684,9 @@ CREATE TABLE public.zoom_users (
id bigint NOT NULL,
api_id character varying,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL
updated_at timestamp(6) without time zone NOT NULL,
account_number character varying,
tier integer DEFAULT 0
);
@@ -1755,6 +1849,13 @@ ALTER TABLE ONLY public.imports ALTER COLUMN id SET DEFAULT nextval('public.impo
ALTER TABLE ONLY public.location_releases ALTER COLUMN id SET DEFAULT nextval('public.location_releases_id_seq'::regclass);
--
-- Name: matching_requests id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.matching_requests ALTER COLUMN id SET DEFAULT nextval('public.matching_requests_id_seq'::regclass);
--
-- Name: material_releases id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -1762,6 +1863,13 @@ ALTER TABLE ONLY public.location_releases ALTER COLUMN id SET DEFAULT nextval('p
ALTER TABLE ONLY public.material_releases ALTER COLUMN id SET DEFAULT nextval('public.material_releases_id_seq'::regclass);
--
-- Name: medical_releases id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.medical_releases ALTER COLUMN id SET DEFAULT nextval('public.medical_releases_id_seq'::regclass);
--
-- Name: music_releases id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -2008,14 +2116,6 @@ ALTER TABLE ONLY public.contract_templates
ADD CONSTRAINT contract_templates_pkey PRIMARY KEY (id);
--
-- Name: data_migrations data_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.data_migrations
ADD CONSTRAINT data_migrations_pkey PRIMARY KEY (version);
--
-- Name: directories directories_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -2064,6 +2164,14 @@ ALTER TABLE ONLY public.location_releases
ADD CONSTRAINT location_releases_pkey PRIMARY KEY (id);
--
-- Name: matching_requests matching_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.matching_requests
ADD CONSTRAINT matching_requests_pkey PRIMARY KEY (id);
--
-- Name: material_releases material_releases_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -2072,6 +2180,14 @@ ALTER TABLE ONLY public.material_releases
ADD CONSTRAINT material_releases_pkey PRIMARY KEY (id);
--
-- Name: medical_releases medical_releases_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.medical_releases
ADD CONSTRAINT medical_releases_pkey PRIMARY KEY (id);
--
-- Name: music_releases music_releases_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -2525,6 +2641,13 @@ CREATE INDEX index_location_releases_on_term_id ON public.location_releases USIN
CREATE INDEX index_location_releases_on_territory_id ON public.location_releases USING btree (territory_id);
--
-- Name: index_matching_requests_on_project_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_matching_requests_on_project_id ON public.matching_requests USING btree (project_id);
--
-- Name: index_material_releases_on_applicable_medium_id; Type: INDEX; Schema: public; Owner: -
--
@@ -2567,6 +2690,20 @@ CREATE INDEX index_material_releases_on_term_id ON public.material_releases USIN
CREATE INDEX index_material_releases_on_territory_id ON public.material_releases USING btree (territory_id);
--
-- Name: index_medical_releases_on_contract_template_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_medical_releases_on_contract_template_id ON public.medical_releases USING btree (contract_template_id);
--
-- Name: index_medical_releases_on_project_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_medical_releases_on_project_id ON public.medical_releases USING btree (project_id);
--
-- Name: index_music_releases_on_applicable_medium_id; Type: INDEX; Schema: public; Owner: -
--
@@ -2840,13 +2977,6 @@ CREATE INDEX index_videos_on_audio_analysis_uid ON public.videos USING btree (au
CREATE INDEX index_videos_on_project_id ON public.videos USING btree (project_id);
--
-- Name: index_zoom_meetings_on_broadcast_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_zoom_meetings_on_broadcast_id ON public.zoom_meetings USING btree (broadcast_id);
--
-- Name: index_zoom_meetings_on_project_id; Type: INDEX; Schema: public; Owner: -
--
@@ -3003,6 +3133,14 @@ ALTER TABLE ONLY public.video_release_confirmations
ADD CONSTRAINT fk_rails_2787252ceb FOREIGN KEY (file_info_id) REFERENCES public.file_infos(id);
--
-- Name: medical_releases fk_rails_325442c794; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.medical_releases
ADD CONSTRAINT fk_rails_325442c794 FOREIGN KEY (contract_template_id) REFERENCES public.contract_templates(id);
--
-- Name: music_releases fk_rails_3a2b4033ad; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -3147,6 +3285,14 @@ ALTER TABLE ONLY public.material_releases
ADD CONSTRAINT fk_rails_6b945b36b9 FOREIGN KEY (contract_template_id) REFERENCES public.contract_templates(id);
--
-- Name: matching_requests fk_rails_71d16e64c8; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.matching_requests
ADD CONSTRAINT fk_rails_71d16e64c8 FOREIGN KEY (project_id) REFERENCES public.projects(id);
--
-- Name: appearance_releases fk_rails_7a58302526; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -3180,11 +3326,11 @@ ALTER TABLE ONLY public.music_releases
--
-- Name: zoom_meetings fk_rails_8d814ea729; Type: FK CONSTRAINT; Schema: public; Owner: -
-- Name: medical_releases fk_rails_98aa92daa9; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.zoom_meetings
ADD CONSTRAINT fk_rails_8d814ea729 FOREIGN KEY (broadcast_id) REFERENCES public.broadcasts(id);
ALTER TABLE ONLY public.medical_releases
ADD CONSTRAINT fk_rails_98aa92daa9 FOREIGN KEY (project_id) REFERENCES public.projects(id);
--
@@ -3498,7 +3644,16 @@ INSERT INTO "schema_migrations" (version) VALUES
('20200424161117'),
('20200427073429'),
('20200428091105'),
('20200430151828'),
('20200430190412'),
('20200507110804'),
('20200512161738');
('20200512161738'),
('20200526113516'),
('20200603090419'),
('20200606044747'),
('20200610085411'),
('20200610140459'),
('20200612121539'),
('20200615131722');

View File

@@ -10,5 +10,6 @@ require_relative "./brayniac_ai/edl_parse"
require_relative "./brayniac_ai/edl_parse_result"
require_relative "./brayniac_ai/facial_recognition"
require_relative "./brayniac_ai/facial_recognition_result"
require_relative "./brayniac_ai/qr_matching"
require_relative "./brayniac_ai/tag"
require_relative "./brayniac_ai/validation"

View File

@@ -0,0 +1,4 @@
module BrayniacAI
class QrMatching < Base
end
end

View File

@@ -1,4 +1,3 @@
require 'zoom_gateway'
namespace :zoom do
desc "Setup necessary zoom roles and users"
task :setup => :environment do
@@ -17,4 +16,30 @@ namespace :zoom do
Rails.logger.info "Created role #{ZoomGateway.HOST_ROLE}."
end
end
desc "Synchronize ActiveRecord users with current account state"
task :sync => :environment do
zoom = Zoom.new
roles = zoom.roles_list["roles"]
ActiveRecord::Base.transaction do
ZoomUser.tiers.keys.each do |tier|
full_role_name = ZoomGateway.host_role_name(tier)
role_id = roles.select { |r| r["name"] == full_role_name }.first["id"]
user_ids = zoom.roles_members(role_id: role_id).dig("members").pluck("id")
# Invalid db users (not existing on the given Zoom account, but existing in the app db)
ZoomUser.current_account.public_send(tier).where.not(api_id: user_ids).each do |zu|
zu.api_id = nil
zu.destroy
end
# Missing zoom users (existing in given Zoom account, but not existing in the app db)
(user_ids - ZoomUser.current_account.public_send(tier).pluck(:api_id)).each do |api_user_id|
ZoomUser.current_account.public_send(tier).create(api_id: api_user_id)
end
end
end
end
end

View File

@@ -3,6 +3,7 @@ class ZoomGateway
class MeetingExpired < StandardError; end
class UserNotFound < StandardError; end
class TooManyHosts < StandardError; end
class UserAlreadyExists < StandardError; end
class << self
def USER_TYPE_NAME
@@ -20,16 +21,24 @@ class ZoomGateway
end
def HOST_ROLE
"#{self.USER_TYPE_NAME}-directme-host"
self.host_role_name(self.USER_TYPE_NAME)
end
def ACCOUNT_NUMBER
ENV['ZOOM_ACCOUNT_NUMBER']
end
def enable_recordings?
ENV['ZOOM_ENABLE_RECORDINGS'] == '1'
ENV['ZOOM_ENABLE_RECORDINGS'] == 'true'
end
def apply_limits?
self.USER_TYPE_NAME == 'pro'
end
def host_role_name(user_type_name)
"#{user_type_name}-directme-host"
end
end
def initialize
@@ -97,6 +106,8 @@ class ZoomGateway
raise UserNotFound, error.message
elsif error.status_code == 3001
raise MeetingExpired, error.message
elsif error.status_code == 1005
raise UserAlreadyExists, error.message
else
raise error
end

View File

@@ -50,4 +50,21 @@ RSpec.describe BroadcastsChannel, type: :channel do
})
end
end
describe ".broadcast_file_upload_updates" do
it 'broadcasts to the channel with the right data' do
broadcast = create(:broadcast, :with_stream, :with_files, skip_create_callback: true)
files_content = ApplicationController.render partial: "broadcasts/file", collection: broadcast.files
expect {
BroadcastsChannel.broadcast_file_upload_updates(broadcast, broadcast.files, "pagination_content")
}.to have_broadcasted_to(broadcast).with({
event: 'file_upload_update',
broadcast_token: broadcast.token,
files_content: files_content,
pagination_content: "pagination_content"
})
end
end
end

View File

@@ -0,0 +1,36 @@
require 'rails_helper'
RSpec.describe Api::DirectUploadsController, type: :controller do
let(:current_user) { create(:user) }
let(:project) { create(:project, account: current_user.primary_account) }
before do
sign_in_to_api(current_user)
end
describe '#create' do
it 'returns data for a direct upload' do
post :create, params: { direct_upload: direct_upload_params }
expect(response).to be_successful
expect(response_body_data_attributes).to include('id', 'key', 'signed_id', 'url', 'headers')
end
end
private
def direct_upload_params
file = file_fixture("video_file.mp4")
checksum = Digest::MD5.base64digest(StringIO.new(file.read).to_s)
{ filename: file.basename.to_s, content_type: "video/mp4", byte_size: file.size, checksum: checksum }
end
def response_body_data
JSON.parse(response.body).dig('data')
end
def response_body_data_attributes
response_body_data.dig('attributes')
end
end

View File

@@ -263,7 +263,9 @@ RSpec.describe AppearanceReleasesController, tye: :controller do
end
def minor_appearance_release_params
attributes_for(:appearance_release, :non_native, :minor_with_guardian_photo).except(:contract).merge(contract_param, exploitable_rights_params)
attributes_for(:appearance_release, :non_native, :minor_with_guardian_photo)
.except(:contract)
.merge(contract_param, exploitable_rights_params)
end
def exploitable_rights_params

View File

@@ -111,17 +111,6 @@ RSpec.describe BroadcastsController, type: :controller do
end
end
describe "#update" do
let!(:broadcast) { create(:broadcast, :with_stream, skip_create_callback: true, project: project ) }
it "uploads files to broadcast" do
patch :update, params: { project_id: project.id, id: broadcast.id, broadcast: file_params }, xhr: true
expect(broadcast.files.count).to eq(1)
expect(broadcast.files.first.filename).to eq("contract.pdf")
end
end
describe "#show" do
let(:broadcast) { create(:broadcast, project: project, name: "Another Broadcast") }
@@ -198,6 +187,25 @@ RSpec.describe BroadcastsController, type: :controller do
end
end
describe "#update" do
let!(:broadcast) { create(:broadcast, :with_stream, skip_create_callback: true, project: project ) }
it "uploads files to broadcast" do
patch :update, params: { project_id: project.id, id: broadcast.id, broadcast: file_params }, xhr: true
expect(broadcast.files.count).to eq(1)
expect(broadcast.files.first.filename).to eq("contract.pdf")
end
it "sends an update to the broadcasts channel" do
allow(BroadcastsChannel).to receive(:broadcast_file_upload_updates)
patch :update, params: { project_id: project.id, id: broadcast.id, broadcast: file_params }, xhr: true
expect(BroadcastsChannel).to have_received(:broadcast_file_upload_updates)
end
end
describe "#destroy" do
let!(:broadcast_2) { create(:broadcast, :with_stream, skip_create_callback: true, project: project, name: "Another Broadcast") }

View File

@@ -0,0 +1,91 @@
require "rails_helper"
RSpec.describe MedicalReleasesController, 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
end
describe "#index" do
it "responds successfully" do
get :index, params: { project_id: project }
expect(response).to be_successful
end
it "renders content" do
release = create(:medical_release, project: project,
person_first_name: "My",
person_last_name: "Release",
person_phone: "5551234567",
person_email: "jane.doe@test.com")
create(:note, notable: release, content: "Some notes here")
get :index, params: { project_id: project }
expect(response.body).to have_content "My Release"
expect(response.body).to have_content "Some notes here"
expect(response.body).to have_content "Manage"
end
context "when there are no medical releases" do
it "renders an empty message" do
get :index, params: { project_id: project }
expect(response.body).to have_content("Medical releases will appear here")
end
end
context "when there are many records" do
it "paginates the table" do
create_list(:medical_release, 20, project: project)
get :index, params: { project_id: project }
expect(response.body).to have_link("2", href: project_medical_releases_path(project, page: 2))
end
end
context "for xhr request" do
it "filters the releases by a query param" do
medical_releases = [
create(:medical_release, person_name: "Adam Sandler", project: project),
create(:medical_release, person_name: "Zoe Perry", project: project),
]
get :index, params: { project_id: project, query: "Zoe" }, xhr: true
expect(response.body).not_to have_content("Adam Sandler")
expect(response.body).to have_content("Zoe Perry")
end
end
end
describe "#destroy" do
let!(:medical_release) { create(:medical_release, project: project) }
it "responds with redirect" do
delete :destroy, params: { project_id: project, id: medical_release }
expect(response).to be_redirect
expect(response).to redirect_to [project, :medical_releases]
end
it "sets the flash" do
delete :destroy, params: { project_id: project, id: medical_release }
expect(flash.alert).not_to be_nil
end
it "destroys the record" do
expect {
delete :destroy, params: { project_id: project, id: medical_release }
}.to change(MedicalRelease, :count).by(-1)
end
end
end

View File

@@ -92,6 +92,14 @@ RSpec.describe Public::BroadcastsController, type: :controller do
expect(broadcast.files.count).to eq(1)
expect(broadcast.files.first.filename).to eq("contract.pdf")
end
it "sends an update to the broadcasts channel" do
allow(BroadcastsChannel).to receive(:broadcast_file_upload_updates)
patch :update, params: { token: broadcast.token, broadcast: file_params }, xhr: true
expect(BroadcastsChannel).to have_received(:broadcast_file_upload_updates)
end
end
after do

View File

@@ -7,6 +7,15 @@ describe Public::LocationReleasesController do
render_views
describe "#create" 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, location_release: location_release_params_with_photos }
expect(response).to be_successful
expect(LocationRelease.last.photos.attached?).to eq true
end
it "logs analytics" do
contract_template = create(:contract_template, project: project)
@@ -67,6 +76,10 @@ describe Public::LocationReleasesController do
attributes_for(:location_release, :native).except(:signature).merge(signature_param).merge(person_address_params)
end
def location_release_params_with_photos
attributes_for(:location_release, :native, :with_photo).except(:signature).merge(signature_param)
end
def person_address_params
{
person_address_street1: "123 Broadway",

View File

@@ -7,6 +7,15 @@ describe Public::MaterialReleasesController do
render_views
describe "#create" 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_photos }
expect(response).to be_successful
expect(MaterialRelease.last.photos.attached?).to eq true
end
it "logs analytics" do
contract_template = create(:contract_template, project: project)
@@ -65,6 +74,11 @@ describe Public::MaterialReleasesController do
attributes_for(:material_release, :native).except(:signature).merge(signature_param)
end
def material_release_params_with_photos
attributes_for(:material_release, :native, :with_photo).except(:signature).merge(signature_param)
end
def signature_param
file = file_fixture("signature.png")
data_uri = Base64Image.from_image(file).data_uri

View File

@@ -0,0 +1,71 @@
require "rails_helper"
RSpec.describe Public::MedicalReleasesController, type: :controller do
let(:user) { create(:user) }
let(:project) { create(:project, account: user.primary_account) }
render_views
describe "#create" do
it "logs analytics" do
contract_template = create(:contract_template, project: project)
expect {
post :create, params: { account_id: project.account.to_param, project_id: project, contract_template_id: contract_template, medical_release: medical_release_params }
}.to(
have_enqueued_job(TrackAnalyticsJob)
.with(nil, nil, :track_create_native_release, release_type: "MedicalRelease", account: project.account, user_agent: "Rails Testing", user_ip: "0.0.0.0")
)
end
it "displays validation errors" do
contract_template = create(:contract_template, project: project)
sign_in(user)
post :create, params: { account_id: user.primary_account.to_param, project_id: project, contract_template_id: contract_template, medical_release: { person_address_city: "Albuquerque" } }
body = CGI.unescape_html(response.body)
expect(body).to match /Person first name can't be blank/
expect(body).to match /Person last name can't be blank/
expect(body).to match />can't be blank</
end
it "responds with success " 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, medical_release: medical_release_params }
expect(response).to be_successful
end
it "runs attach contract to releasable job" do
contract_template = create(:contract_template, project: project)
expect {
post :create, params: { account_id: project.account.to_param, project_id: project, contract_template_id: contract_template, medical_release: medical_release_params }
}.to(
have_enqueued_job(AttachContractToReleasableJob)
.with(MedicalRelease.last)
)
end
end
private
def medical_release_params
attributes_for(:medical_release, :native).except(:signature).merge(signature_param, photos_param)
end
def photos_param
path = Rails.root.join("spec", "fixtures", "files", "person_photo.png")
photo = Rack::Test::UploadedFile.new(path, "image/png")
{ photos: [photo] }
end
def signature_param
file = file_fixture("signature.png")
data_uri = Base64Image.from_image(file).data_uri
{ signature_base64: data_uri }
end
end

View File

@@ -1,5 +1,4 @@
require 'rails_helper'
require 'zoom_gateway'
RSpec.describe Public::ZoomMeetingsController, type: :controller do
let(:user) { create(:user) }

View File

@@ -1,5 +1,4 @@
require 'rails_helper'
require 'zoom_gateway'
RSpec.describe ZoomMeetingsController, type: :controller do
let(:user) { create(:user) }

View File

@@ -1,82 +0,0 @@
require "rails_helper"
RSpec.describe ZoomNotificationsController, type: :controller do
render_views
let!(:zoom_meeting) { create(:zoom_meeting, api_meeting_id: 'meeting_id') }
let(:started_status) { {event: 'meeting.started', payload: {object: {id: 'meeting_id' }}} }
let(:ended_status) { {event: 'meeting.ended', payload: {object: {id: 'meeting_id' }}} }
let(:wrong_meeting_id) { {payload: {object: {id: 'wrong_id' }}} }
let(:authorization_header) { {'Authorization' => 'xxx-xxx-xxx'} }
let(:wrong_authorization_header) { {'Authorization' => 'yyy-yyy-yyy'} }
before do
allow(ENV).to receive(:[]).with('ZOOM_VERIFICATION_TOKEN').and_return('xxx-xxx-xxx')
end
describe '#create' do
context 'with no authorization key' do
it 'raises 403 response' do
post :create, params: started_status
expect(response).to have_http_status(403)
end
end
context 'with wrong authorization key' do
it 'raises 403 response' do
request.headers.merge!(wrong_authorization_header)
post :create, params: started_status
expect(response).to have_http_status(403)
end
end
context 'authorized' do
before do
request.headers.merge!(authorization_header)
end
context 'with wrong meeting id' do
it 'raises RecordNotFound' do
expect {
post :create, params: wrong_meeting_id
}.to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'with right meeting id' do
it 'responds with 200' do
post :create, params: started_status
expect(response).to have_http_status(200)
end
it 'assigns the zoom meeting' do
post :create, params: started_status
expect(assigns(:zoom_meeting)).to eq(zoom_meeting)
end
it 'updates the zoom_meeting when started status is received in notification' do
post :create, params: started_status
expect(zoom_meeting.reload).to be_started
end
it 'updates the zoom_meeting when ended status is received in notification' do
post :create, params: ended_status
expect(zoom_meeting.reload).to be_ended
end
it 'updates the recording when recording complete notification is received' do
expect {
post :create, params: recording_complete
}.to change { zoom_meeting.recording }
end
end
end
end
end

View File

@@ -0,0 +1,101 @@
require "rails_helper"
RSpec.describe ZoomNotificationsController, type: :controller do
render_views
let!(:zoom_meeting) { create(:zoom_meeting, api_meeting_id: 'meeting_id') }
let(:started_status) { {event: 'meeting.started', payload: {object: {id: 'meeting_id' }}} }
let(:ended_status) { {event: 'meeting.ended', payload: {object: {id: 'meeting_id' }}} }
let(:wrong_meeting_id) { {event: 'meeting.started', payload: {object: {id: 'wrong_id' }}} }
let(:recording_complete) { {event: 'recording.completed', payload: {object: {id: 'meeting_id', recording_files: [Object.new]}}} }
let(:authorization_header) { {'Authorization' => 'xxx-xxx-xxx'} }
let(:wrong_authorization_header) { {'Authorization' => 'yyy-yyy-yyy'} }
before do
allow(ENV).to receive(:[]).with('ZOOM_VERIFICATION_TOKEN').and_return('xxx-xxx-xxx')
end
describe '#create' do
context 'with no authorization key' do
it 'raises 403 response' do
post :create, params: started_status
expect(response).to have_http_status(403)
end
end
context 'with wrong authorization key' do
it 'raises 403 response' do
request.headers.merge!(wrong_authorization_header)
post :create, params: started_status
expect(response).to have_http_status(403)
end
end
context 'authorized' do
before do
request.headers.merge!(authorization_header)
end
context 'user hooks' do
before(:each) { ZoomUser.create api_id: 'zoom_user_id' }
it 'deletes the user from db if user.deleted is passed with existing user id' do
expect {
post :create, params: {event: 'user.deleted', payload: {object: {id: 'zoom_user_id'}}}
}.to change { ZoomUser.count }.by(-1)
end
it 'does not do anything if user.deleted is passed with non-existing user' do
expect {
post :create, params: {event: 'user.deleted', payload: {object: {id: 'wrong-user-id'}}}
}.not_to change { ZoomUser.count }
end
end
context 'meeting hooks' do
context 'with wrong meeting id' do
it 'raises RecordNotFound' do
expect {
post :create, params: wrong_meeting_id
}.to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'with right meeting id' do
it 'responds with 200' do
post :create, params: started_status
expect(response).to have_http_status(200)
end
it 'assigns the zoom meeting' do
post :create, params: started_status
expect(assigns(:zoom_meeting)).to eq(zoom_meeting)
end
it 'updates the zoom_meeting when started status is received in notification' do
post :create, params: started_status
expect(zoom_meeting.reload).to be_started
end
it 'updates the zoom_meeting when ended status is received in notification' do
post :create, params: ended_status
expect(zoom_meeting.reload).to be_ended
end
it 'updates the recording when recording complete notification is received' do
expect(AttachRecordingToZoomMeetingJob).to receive(:perform_later)
post :create, params: recording_complete
end
end
end
end
end
end

View File

@@ -33,6 +33,7 @@ FactoryBot.define do
guardian_last_name "Doe"
guardian_address "100 Test Lane, New York, 10001"
guardian_phone "123-555-1234"
guardian_email "guardian@galaxy.all"
end
trait :minor_with_guardian_photo do
@@ -41,6 +42,7 @@ FactoryBot.define do
guardian_last_name "Doe"
guardian_address "100 Test Lane, New York, 10001"
guardian_phone "123-555-1234"
guardian_email "guardian@galaxy.all"
guardian_photo do
path = Rails.root.join("spec", "fixtures", "files", "pratt.jpg")
Rack::Test::UploadedFile.new(path, "image/jpeg")
@@ -65,5 +67,24 @@ FactoryBot.define do
appearance_release.contract_template = build(:appearance_release_contract_template)
end
end
factory :appearance_release_import do
person_photo nil
trait :with_headshot do
person_photo do
path = Rails.root.join("spec", "fixtures", "files", "person_photo.png")
Rack::Test::UploadedFile.new(path, "image/png")
end
end
trait :with_contract do
contract do
path = Rails.root.join("spec", "fixtures", "files", "AppearanceRelease.pdf")
Rack::Test::UploadedFile.new(path, "application/pdf")
end
end
end
end
end

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