Skip to content

Commit

Permalink
Merge pull request #5970 from avalonmediasystem/develop
Browse files Browse the repository at this point in the history
v7.8 RC3
  • Loading branch information
Jon Cameron authored Aug 2, 2024
2 parents 1c48a24 + 783f49f commit 9abd3b6
Show file tree
Hide file tree
Showing 20 changed files with 186 additions and 40 deletions.
13 changes: 9 additions & 4 deletions app/assets/javascripts/player_listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ let addToPlaylistListenersAdded = false;
let firstLoad = true;
let streamId = '';
let isMobile = false;
let isPlaying = false;

/**
* Bind action buttons on the page with player events and re-populate details
Expand Down Expand Up @@ -69,17 +70,21 @@ function addActionButtonListeners(player, mediaObjectId, sectionIds) {
}

/* Add player event listeners to update UI components on the page */
// Listen to 'timeupdate' event to udate add to playlist form when using while media is playing or manually seeking
player.player.on('timeupdate', () => {
// Listen to 'seeked' event to udate add to playlist form
player.player.on('seeked', () => {
if (getActiveItem() != undefined) {
activeTrack = getActiveItem(false);
if (activeTrack != undefined) {
streamId = activeTrack.streamId;
}
disableEnableCurrentTrack(activeTrack, player.player.currentTime(), true, currentSectionLabel);
disableEnableCurrentTrack(activeTrack, player.player.currentTime(), isPlaying, currentSectionLabel);
}
});

player.player.on('play', () => { isPlaying = true; });

player.player.on('pause', () => { isPlaying = false; });

/*
Disable action buttons tied to player related information on player's 'loadstart' event which functions
parallel to the player's src changes. So, that the user doesn't interact with them get corrupted data
Expand Down Expand Up @@ -273,7 +278,7 @@ function addToPlaylistListeners(sectionIds, mediaObjectId) {
disableEnableCurrentTrack(
activeTrack,
currentTime,
false,
isPlaying,
$('#playlist_item_title').val() || currentSectionLabel // Preserve user edits for the title when available
);
$('#current-section-name').text(currentSectionLabel);
Expand Down
19 changes: 12 additions & 7 deletions app/assets/javascripts/ramp_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ function getTimelineScopes() {
if (parent.length === 0) {
let begin = 0;
let end = activeItem.times.end;
scopes[0].times = { begin: 0, end: end }
scopes[0].times = { begin: 0, end: end };
}
while (parent.length > 0) {
let next = parent.closest('ul').closest('li');
Expand Down Expand Up @@ -209,10 +209,10 @@ function collapseMoreDetails() {
* disable the option otherwise enable it.
* @param {Object} activeTrack JSON object for the active timespans
* @param {Number} currentTime player's playhead position
* @param {Boolean} isSeeked flag to indicate player 'seeked' event happened/not
* @param {Boolean} isPlaying flag to inidicate media is playing or not
* @param {String} sectionTitle name of the current section
*/
function disableEnableCurrentTrack(activeTrack, currentTime, isSeeked, sectionTitle) {
function disableEnableCurrentTrack(activeTrack, currentTime, isPlaying, sectionTitle) {
// Return when add to playlist form is not visible
let playlistForm = $('#add_to_playlist')[0];
if (!playlistForm) {
Expand All @@ -222,7 +222,8 @@ function disableEnableCurrentTrack(activeTrack, currentTime, isSeeked, sectionTi
if (activeTrack != undefined) {
streamId = activeTrack.streamId;
let { label, times, sectionLabel } = activeTrack;
let starttime = currentTime || times.begin;
// Update starttime when media is not playing
let starttime = isPlaying ? times.begin : currentTime || times.begin;
$('#playlist_item_start').val(createTimestamp(starttime, true));
$('#playlist_item_end').val(createTimestamp(times.end, true));
title = `${sectionLabel} - ${label}`;
Expand All @@ -245,9 +246,7 @@ function disableEnableCurrentTrack(activeTrack, currentTime, isSeeked, sectionTi
$('#playlistitem_scope_track').prop('checked', false);
}
}
// Only change the title when user actively seeked to a different timestamp,
// persisting the user changes to the field unless the active track is changed
if (isSeeked && sectionTitle != undefined) {
if (sectionTitle != undefined) {
$('#playlist_item_title').val(title);
}
}
Expand Down Expand Up @@ -345,4 +344,10 @@ function resetAddToPlaylistForm() {
function closeAlert() {
$('#add_to_playlist_alert').slideUp();
$('#add_to_playlist_form_group').slideDown();
// Set default selection in options list when alert is closed
if ($('#playlistitem_scope_track')[0].disabled) {
$('#playlistitem_scope_section').prop('checked', true);
} else {
$('#playlistitem_scope_track').prop('checked', true);
}
}
2 changes: 1 addition & 1 deletion app/assets/stylesheets/avalon/_footer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
width: 100%;
min-height: 2.5rem;
color: #999999;
z-index: -1000;
z-index: 0;
}

footer {
Expand Down
1 change: 1 addition & 0 deletions app/assets/stylesheets/avalon/_playlists.scss
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@

#Playlists_paginate {
float: right;
z-index: 10;
}

/* Playlist copy modal */
Expand Down
16 changes: 8 additions & 8 deletions app/controllers/catalog_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,18 @@ class CatalogController < ApplicationController
config.add_facet_field 'unit_ssim', label: 'Unit', limit: 5
config.add_facet_field 'language_ssim', label: 'Language', limit: 5
# Hide these facets if not a Collection Manager
config.add_facet_field 'workflow_published_sim', label: 'Published', limit: 5, if: Proc.new {|context, config, opts| Ability.new(context.current_user, context.user_session).can? :create, MediaObject}, group: "workflow"
config.add_facet_field 'avalon_uploader_ssi', label: 'Created by', limit: 5, if: Proc.new {|context, config, opts| Ability.new(context.current_user, context.user_session).can? :create, MediaObject}, group: "workflow"
config.add_facet_field 'read_access_group_ssim', label: 'Item access', if: Proc.new {|context, config, opts| Ability.new(context.current_user, context.user_session).can? :create, MediaObject}, group: "workflow", query: {
config.add_facet_field 'workflow_published_sim', label: 'Published', limit: 5, if: Proc.new {|context, config, opts| context.current_ability.can? :create, MediaObject}, group: "workflow"
config.add_facet_field 'avalon_uploader_ssi', label: 'Created by', limit: 5, if: Proc.new {|context, config, opts| context.current_ability.can? :create, MediaObject}, group: "workflow"
config.add_facet_field 'read_access_group_ssim', label: 'Item access', if: Proc.new {|context, config, opts| context.current_ability.can? :create, MediaObject}, group: "workflow", query: {
public: { label: "Public", fq: "has_model_ssim:MediaObject AND read_access_group_ssim:#{Hydra::AccessControls::AccessRight::PERMISSION_TEXT_VALUE_PUBLIC}" },
restricted: { label: "Authenticated", fq: "has_model_ssim:MediaObject AND read_access_group_ssim:#{Hydra::AccessControls::AccessRight::PERMISSION_TEXT_VALUE_AUTHENTICATED}" },
private: { label: "Private", fq: "has_model_ssim:MediaObject AND NOT read_access_group_ssim:#{Hydra::AccessControls::AccessRight::PERMISSION_TEXT_VALUE_PUBLIC} AND NOT read_access_group_ssim:#{Hydra::AccessControls::AccessRight::PERMISSION_TEXT_VALUE_AUTHENTICATED}" }
}
config.add_facet_field 'read_access_virtual_group_ssim', label: 'External Group', limit: 5, if: Proc.new {|context, config, opts| Ability.new(context.current_user, context.user_session).can? :create, MediaObject}, group: "workflow", helper_method: :vgroup_display
config.add_facet_field 'date_digitized_ssim', label: 'Date Digitized', limit: 5, if: Proc.new {|context, config, opts| Ability.new(context.current_user, context.user_session).can? :create, MediaObject}, group: "workflow"#, partial: 'blacklight/hierarchy/facet_hierarchy'
config.add_facet_field 'date_ingested_ssim', label: 'Date Ingested', limit: 5, if: Proc.new {|context, config, opts| Ability.new(context.current_user, context.user_session).can? :create, MediaObject}, group: "workflow"
config.add_facet_field 'has_captions_bsi', label: 'Has Captions', if: Proc.new {|context, config, opts| Ability.new(context.current_user, context.user_session).can? :create, MediaObject}, group: "workflow", helper_method: :display_has_caption_or_transcript
config.add_facet_field 'has_transcripts_bsi', label: 'Has Transcripts', if: Proc.new {|context, config, opts| Ability.new(context.current_user, context.user_session).can? :create, MediaObject}, group: "workflow", helper_method: :display_has_caption_or_transcript
config.add_facet_field 'read_access_virtual_group_ssim', label: 'External Group', limit: 5, if: Proc.new {|context, config, opts| context.current_ability.can? :create, MediaObject}, group: "workflow", helper_method: :vgroup_display
config.add_facet_field 'date_digitized_ssim', label: 'Date Digitized', limit: 5, if: Proc.new {|context, config, opts| context.current_ability.can? :create, MediaObject}, group: "workflow"#, partial: 'blacklight/hierarchy/facet_hierarchy'
config.add_facet_field 'date_ingested_ssim', label: 'Date Ingested', limit: 5, if: Proc.new {|context, config, opts| context.current_ability.can? :create, MediaObject}, group: "workflow"
config.add_facet_field 'has_captions_bsi', label: 'Has Captions', if: Proc.new {|context, config, opts| context.current_ability.can? :create, MediaObject}, group: "workflow", helper_method: :display_has_caption_or_transcript
config.add_facet_field 'has_transcripts_bsi', label: 'Has Transcripts', if: Proc.new {|context, config, opts| context.current_ability.can? :create, MediaObject}, group: "workflow", helper_method: :display_has_caption_or_transcript

# Have BL send all facet field names to Solr, which has been the default
# previously. Simply remove these lines if you'd rather use Solr request
Expand Down
4 changes: 2 additions & 2 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ def lti_share_url_for(obj, _opts = {})
def image_for(document)
master_file_id = document["section_id_ssim"].try :first

video_count = document["avalon_resource_type_ssim"].count{|m| m.start_with?('moving image') } rescue 0
audio_count = document["avalon_resource_type_ssim"].count{|m| m.start_with?('sound recording') } rescue 0
video_count = document["avalon_resource_type_ssim"].count{|m| m.downcase.start_with?('moving image') } rescue 0
audio_count = document["avalon_resource_type_ssim"].count{|m| m.downcase.start_with?('sound recording') } rescue 0

if master_file_id
if video_count > 0
Expand Down
2 changes: 1 addition & 1 deletion app/helpers/blacklight/local_blacklight_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def facet_group_names
end

def url_for_document doc, options = {}
SpeedyAF::Base.find(doc[:id])
SpeedyAF::Base.for(doc.to_h.with_indifferent_access)
end

def contributor_index_display args
Expand Down
4 changes: 2 additions & 2 deletions app/javascript/components/PlaylistRamp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ const Ramp = ({
</div>
</Col>
</Row>
<Row className="ramp--playlist-desc-tags">
<Row className="ramp--playlist-desc-tags mx-1 mx-sm-0">
{comment && (
<div style={{ position: 'relative' }}>
<h4>{comment_label}</h4>
Expand Down Expand Up @@ -251,7 +251,7 @@ const Ramp = ({
</Row>
{playlist_item_ids?.length > 0 && (
<React.Fragment>
<h4 className="mt-3">Playlist Items</h4>
<h4 className="mt-3 mx-1 mx-sm-0">Playlist Items</h4>
<StructuredNavigation />
</React.Fragment>
)}
Expand Down
2 changes: 1 addition & 1 deletion app/models/master_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ def create_frame_source_hls_temp_file(offset)
# Fixes https://github.com/avalonmediasystem/avalon/issues/3474
target_location = File.basename(details[:location]).split('?')[0]
target = File.join(Dir.tmpdir, target_location)
File.open(target,'wb') { |f| URI.open(details[:location]) { |io| f.write(io.read) } }
File.open(target,'wb') { |f| URI.open(details[:location], "Referer" => Rails.application.routes.url_helpers.root_url) { |io| f.write(io.read) } }
return target, details[:offset]
end

Expand Down
2 changes: 1 addition & 1 deletion app/models/media_object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def section_ids
return @section_ids if @section_ids

# Do migration
self.section_ids = self.ordered_master_file_ids if self.section_list.nil?
self.section_ids = self.ordered_master_file_ids.compact if self.section_list.nil?

return [] if self.section_list.nil?
@section_ids = JSON.parse(self.section_list)
Expand Down
2 changes: 1 addition & 1 deletion app/models/mods_behaviors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def to_solr(solr_doc = Hash.new, opts = {})
solr_doc['other_identifier_sim'] = gather_terms(self.find_by_terms(:other_identifier))
solr_doc['bibliographic_id_ssi'] = self.bibliographic_id.first
solr_doc['bibliographic_id_source_ssi'] = self.bibliographic_id.source.first
solr_doc['statement_of_responsibility_ssi'] = gather_terms(self.find_by_terms(:statement_of_responsibility))
solr_doc['statement_of_responsibility_ssi'] = gather_terms(self.find_by_terms(:statement_of_responsibility)).first
solr_doc['record_identifier_ssim'] = gather_terms(self.find_by_terms(:record_identifier))

# Extract 4-digit year for creation date facet in Hydra and pub_date facet in Blacklight
Expand Down
1 change: 1 addition & 0 deletions app/models/search_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def search_section_transcripts(solr_parameters)
return unless solr_parameters[:q].present? && SupplementalFile.with_tag('transcript').any? && !(blacklight_params[:controller] == 'bookmarks')

terms = solr_parameters[:q].split
return if terms.any? { |term| term.match?(/[\{\}]/) }
term_subquery = terms.map { |term| "transcript_tsim:#{RSolr.solr_escape(term)}" }.join(" OR ")
solr_parameters[:defType] = "lucene"
solr_parameters[:q] = "({!edismax v=\"#{RSolr.solr_escape(solr_parameters[:q])}\"}) {!join to=id from=isPartOf_ssim}{!join to=id from=isPartOf_ssim}#{term_subquery}"
Expand Down
3 changes: 3 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require_relative 'boot'
require_relative '../lib/tempfile_factory'

require 'rails/all'
require 'resolv-replace'
Expand Down Expand Up @@ -56,6 +57,8 @@ class Application < Rails::Application
end
end

config.middleware.insert_before 0, TempfileFactory

config.active_storage.service = (Settings&.active_storage&.service.presence || "local").to_sym
end
end
5 changes: 5 additions & 0 deletions config/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,8 @@ derivative:
allow_download: true
# Maximum size for uploaded files in bytes (default is disabled)
#max_upload_size: 2147483648 # Use :none or comment out to disable limit
# Rack Multipart creates temporary files when processing multipart form data with a large payload.
# If the default system /tmp directory is storage constrained, you can define an alternative here.
# Leave commented out to use the system default.
# tempfile:
# location: '/tmp'
4 changes: 2 additions & 2 deletions lib/tasks/avalon_migrations.rake
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ namespace :avalon do
task media_object_section_list: :environment do
error_ids = []
mo_count = MediaObject.count
ids_to_migrate = ActiveFedora::SolrService.query("has_model_ssim:MediaObject AND NOT section_list_ssim:[* TO *]", rows: mo_count).pluck("id")
ids_to_migrate = ActiveFedora::SolrService.query("has_model_ssim:MediaObject AND NOT section_list_ssim:[* TO *]", fl: [:id], rows: mo_count).pluck("id")
puts "Migrating #{ids_to_migrate.size} out of #{mo_count} Media Objects."
ids_to_migrate.each do |id|
MediaObject.find(id).save!(validate: false)
rescue StandardError => error
error_ids += [id]
puts "Error migrating #{id}: #{error.message}"
end
remaining_ids_count = ActiveFedora::SolrService.query("has_model_ssim:MediaObject AND NOT section_list_ssim:[* TO *]", rows: mo_count).size
remaining_ids_count = ActiveFedora::SolrService.query("has_model_ssim:MediaObject AND NOT section_list_ssim:[* TO *]", fl: [:id], rows: mo_count).size
if error_ids.size > 0
puts "Migration finished running but #{error_ids.size} Media Objects failed to migrate. Try running the migration again."
elsif remaining_ids_count > 0
Expand Down
30 changes: 30 additions & 0 deletions lib/tempfile_factory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Middleware to override the default Rack::Multipart tempfile factory:
# https://www.rubydoc.info/gems/rack/Rack/Multipart/Parser#TEMPFILE_FACTORY-constant
class TempfileFactory
def initialize(app)
@app = app
return unless Settings.tempfile.present?
if Settings.tempfile&.location.present? && File.directory?(Settings.tempfile&.location) && File.writable?(Settings.tempfile&.location)
@tempfile_location = Settings.tempfile.location
else
logger.warn("[Rack::Multipart] [Tempfile] #{Settings.tempfile.location} is not a diretory or not writable. Falling back to #{Dir.tmpdir}.")
end
end

def call(env)
if @tempfile_location
env["rack.multipart.tempfile_factory"] = lambda { |filename, content_type|
extension = ::File.extname(filename.gsub("\0", '%00'))[0, 129]
Tempfile.new(["RackMultipart", extension], @tempfile_location)
}
end

@app.call(env)
end

private

def logger
@logger ||= Logger.new(File.join(Rails.root, 'log', "#{Rails.env}.log"))
end
end
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"@babel/plugin-proposal-object-rest-spread": "^7.20.7",
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "7",
"@samvera/ramp": "https://github.com/samvera-labs/ramp.git#e7d98e06ceceebe7e8ba59af29d1350a3a43acee",
"@samvera/ramp": "^3.2.0",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"buffer": "^6.0.3",
Expand Down
16 changes: 16 additions & 0 deletions spec/controllers/catalog_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,22 @@
expect(assigns(:response).documents.count).to eq 2
expect(assigns(:response).documents.map(&:id)).to match_array [private_media_object.id, public_media_object.id]
end
context "when there are transcripts present" do
let!(:transcript) { FactoryBot.create(:supplemental_file, :with_transcript_file, :with_transcript_tag, parent_id: private_media_object.id) }

before do
private_media_object.supplemental_files = [transcript]
private_media_object.save
end

it "should not error when search includes curly brackets" do
request.headers['Avalon-Api-Key'] = 'secret_token'
get 'index', params: { q: '{8}', format: 'atom' }
expect(response).to be_successful
expect(response).to render_template('catalog/index')
expect(response.content_type).to eq "application/atom+xml; charset=utf-8"
end
end
end
context "without api key" do
it "should not show results" do
Expand Down
Loading

0 comments on commit 9abd3b6

Please sign in to comment.