Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add image selection for item embed viewers #2752

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions app/assets/javascripts/request_viewer_state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/* eslint-disable camelcase */
/* global Blacklight */

(function (global) {
var RequestViewerState;

RequestViewerState = {
init: function() {
this.setupIframeMessageListener();
this.setupRequestButton();
$('[data-embed-url]').oEmbed();
},

setupRequestButton: function() {
document.querySelector('#request-state').addEventListener('click', (event) => {
this.formId = event.target.dataset.formId;
this.itemId = event.target.dataset.itemId;
this.requestState();
});
},

setupIframeMessageListener: function() {
window.addEventListener('message', (event) => {
if (event && event.data) {
// avoid development issues
if (event.data == "recaptcha-setup" || event.data.source == "react-devtools-content-script") { return; }
let parsedData;
try {
parsedData = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
} catch (error) {
console.error('Failed to parse event data:', error);
return; // Exit if parsing fails
}

if (parsedData.type === "stateResponse" && parsedData.source === "sul-embed-m3") {

console.log('Exhibits: received state:', parsedData.data);
let { viewers, windows, iiif_images, canvas_index } = parsedData.data;
if (document.querySelector('#page-2-placeholder')) document.querySelector('#page-2-placeholder').remove();
const viewer = Object.keys(viewers)[0];
const canvas_id = windows[viewer]['canvasId'];
const iiif_initial_viewer_config = JSON.stringify(viewers[viewer]);
const canvasField = document.querySelector(`#${this.formId} > input[name="item[${this.itemId}][iiif_canvas_id]"]`)
const configInput = document.querySelector(`#${this.formId} > input[name="item[${this.itemId}][iiif_initial_viewer_config]"]`)
canvasField.value = canvas_id;
configInput.value = iiif_initial_viewer_config;
document.querySelector(`#${this.formId} > input[name="item[${this.itemId}][full_image_url]"]`).value = iiif_images[0];
const thumbnail_size = iiif_images.length > 1 ? '!33,100' : '!100,100';
const thumbnail = iiif_images.map(image => image.replace('full', thumbnail_size))
document.querySelector(`#${this.formId} > input[name="item[${this.itemId}][thumbnail_image_url]"]`).value = thumbnail[0];
document.querySelector(`#${this.formId} img`).src = `${thumbnail[0]}?${new Date().getTime()}`;
if (thumbnail.length > 1){
if (!document.querySelector('#page-2')) {
document.querySelector(`#${this.formId} img`).insertAdjacentHTML('afterend', `<img id="page-2" src="${thumbnail[1]}?${new Date().getTime()}"/>`);
} else {
document.querySelector('#page-2').src = `${thumbnail[1]}?${new Date().getTime()}`;
}
} else if (document.querySelector('#page-2')) {
document.querySelector('#page-2').src = ''
}
let modal_link_element = document.querySelector(`#${this.formId} #select-image-area`);
let modal_link = this.updateQueryParameters(modal_link_element.href, canvas_id, iiif_initial_viewer_config);
const multi_page = document.querySelector('[data-current-image]');
if (multi_page) multi_page.innerText = canvas_index;
modal_link_element.href = modal_link;
}
}
});
},

updateQueryParameters: function(url, canvas_id, iiif_initial_viewer_config) {
const urlObj = new URL(url);
urlObj.searchParams.set('canvas_id', canvas_id);
urlObj.searchParams.set('iiif_initial_viewer_config', iiif_initial_viewer_config);
return urlObj.href;
},

requestState: function() {
const iframe = document.querySelector('.oembed-widget iframe, iframe.mirador-embed-wrapper');
iframe.contentWindow.postMessage(JSON.stringify({ type: 'requestState' }), '*'); // Change '*' to a specific origin for security?
}
};

global.RequestViewerState = RequestViewerState;
}(this));


Blacklight.onLoad(function () {
'use strict';
document.addEventListener('show.blacklight.blacklight-modal', function(event) {
RequestViewerState.init()
})
});
63 changes: 63 additions & 0 deletions app/assets/javascripts/select_image_area.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* eslint-disable camelcase */
/* global Blacklight */

(function (global) {
var SelectImageArea;

SelectImageArea = {
init: function(el) {
this.panel = $(el);
this.addSelectImageAreaLink();
},

addSelectImageAreaLink: function() {
const target = $('[data-panel-image-pagination]', this.panel);

const resourceId = this.panel.data('resource-id');
const itemId = this.panel.data('id');
const exhibit_path = this.panel.closest('form')[0].dataset.exhibitPath;
const iiif_initial_viewer_config = $(`input[name="item[${itemId}][iiif_initial_viewer_config]"]`, this.panel)[0].value;
const canvas_id = $(`input[name="item[${itemId}][iiif_canvas_id]"]`, this.panel)[0].value;
let href = `${exhibit_path}/select_image_area/${resourceId}?form_id=${this.panel[0].id}&item_id=${itemId}`
if (canvas_id != "undefined") href += `&canvas_id=${canvas_id}`
if (iiif_initial_viewer_config && iiif_initial_viewer_config != "undefined") href += `&iiif_initial_viewer_config=${encodeURIComponent(iiif_initial_viewer_config)}`
const selectImageAreaHtml = $(`<a id="select-image-area" data-blacklight-modal="trigger" href="${href}">Select image area</a>`);
const image_url = this.panel[0].querySelector('img');
if (image_url.src.includes('!33')) {
image_url.insertAdjacentHTML('afterend', '<span id="page-2-placeholder">This section spans two pages, we can not display the thumbnail for page 2.</span>');
}

target.before(selectImageAreaHtml);
}
};

global.SelectImageArea = SelectImageArea;
}(this));

Blacklight.onLoad(function () {
'use strict';

$('[data-type="solr_documents_embed"] .panels li').each(function (i, element) {
SelectImageArea.init(element);
});

// for if another embed widget is added after page load
const callback = function(mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1 && node.tagName === 'LI') {
const isEmbed = node.closest('[data-type]').dataset.type == 'solr_documents_embed';
if (isEmbed) SelectImageArea.init(node);
}
});
}
}
};

document.querySelectorAll('#page-content').forEach(function(element, i) {
const observer = new MutationObserver(callback);
observer.observe(element, {childList: true, subtree: true});
})
});

13 changes: 12 additions & 1 deletion app/assets/javascripts/sir_trevor_block_overrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,18 @@ SirTrevor.Blocks.SolrDocumentsEmbed = (function(){
`<label for="${formId}">Maximum height of viewer (in pixels)</label>`,
`<input id="${formId}" type="number" class="form-control" placeholder="600" name="maxheight" />`
].join(' ');
}
},
_itemPanelIiifFields: function(index, autocomplete_data) {
return [
'<input type="hidden" name="item[' + index + '][thumbnail_image_url]" value="' + (autocomplete_data.thumbnail_image_url || autocomplete_data.thumbnail || "") + '"/>',
'<input type="hidden" name="item[' + index + '][full_image_url]" value="' + (autocomplete_data.full_image_url || autocomplete_data.thumbnail_image_url || autocomplete_data.thumbnail || "") + '"/>',
'<input type="hidden" name="item[' + index + '][iiif_tilesource]" value="' + (autocomplete_data.iiif_tilesource) + '"/>',
'<input type="hidden" name="item[' + index + '][iiif_manifest_url]" value="' + (autocomplete_data.iiif_manifest_url) + '"/>',
'<input type="hidden" name="item[' + index + '][iiif_canvas_id]" value="' + (autocomplete_data.iiif_canvas_id) + '"/>',
"<input type='hidden' name='item[" + index + "][iiif_initial_viewer_config]' value='" + (autocomplete_data.iiif_initial_viewer_config) + "'/>",
'<input type="hidden" name="item[' + index + '][iiif_image_id]" value="' + (autocomplete_data.iiif_image_id) + '"/>',
].join("\n");
},
});
})();

Expand Down
2 changes: 2 additions & 0 deletions app/assets/javascripts/spotlight.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
//= require spotlight/application
//= require sir_trevor_block_overrides
//= require select_image_area
//= require request_viewer_state
2 changes: 1 addition & 1 deletion app/components/custom_viewer_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<% if document.external_iiif? %>
<%= render partial: "embedded_mirador3", locals: {document: document} %>
<%= render partial: "embedded_mirador3", locals: {document: document, block: block_context} %>
<% elsif document.uploaded_resource? %>
<%= render Blacklight::Gallery::OpenseadragonEmbedComponent.new(document: document, presenter: presenter, view_config: view_config) %>
<% else %>
Expand Down
6 changes: 5 additions & 1 deletion app/components/custom_viewer_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ def initialize(document:, presenter:, view_config: nil, block_context: nil, **kw
@document = document
@presenter = presenter
@view_config = view_config
@block_context = block_context
@block_context = correct_block(block_context)
end

def correct_block(block_context)
block_context&.item&.select { |_key, value| value['id'] == @document.id }
end
end
19 changes: 19 additions & 0 deletions app/controllers/catalog_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,25 @@ def search_tips
end
end

def select_image_area
begin
result = search_service.fetch params[:id]
@document = if result.is_a?(Array)
result.last
else
result
end
rescue StandardError
@document = nil
end
respond_to do |format|
format.html do
return render layout: false if request.xhr?
# Otherwise draw the full page
end
end
end

# Action for parker to fetch bibliography references by ID
def documents_list
search_service = Blacklight::SearchService.new(config: blacklight_config)
Expand Down
9 changes: 7 additions & 2 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ def custom_render_oembed_tag_async(document, canvas_id, block)
content_tag :div, '', data: {
embed_url: blacklight_oembed_engine.embed_url(
url: url,
canvas_id: canvas_id,
iiif_initial_viewer_config: choose_initial_viewer_config(block) || params[:iiif_initial_viewer_config],
canvas_id: canvas_id || params[:canvas_id],
search: params[:search],
maxheight: block&.maxheight.presence || '600',
suggested_search: (current_search_session&.query_params || {})[:q]
Expand All @@ -67,7 +68,11 @@ def custom_render_oembed_tag_async(document, canvas_id, block)
# @param [SirTrevorRails::Blocks::SolrDocumentsEmbedBlock] block
# @return [String] Selected canvas URI
def choose_canvas_id(sir_trevor_block)
sir_trevor_block&.items&.dig(0, 'iiif_canvas_id') if sir_trevor_block.respond_to? :items
sir_trevor_block&.values&.dig(0, 'iiif_canvas_id')
end

def choose_initial_viewer_config(sir_trevor_block)
sir_trevor_block&.values&.dig(0, 'iiif_initial_viewer_config')
end

def context_specific_oembed_url(document)
Expand Down
2 changes: 1 addition & 1 deletion app/views/catalog/_embedded_mirador3.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<% if document.manifest_url.present? %>
<% manifest_url = document.manifest_url.starts_with?('/') ? root_url + document.manifest_url.slice(1..-1) : document.manifest_url %>
<%= content_tag :iframe, '',
src: "#{Settings.iiif_embed.url}?#{{ url: manifest_url }.to_query}",
src: "#{Settings.iiif_embed.url}?#{{ url: manifest_url, canvas_id: choose_canvas_id(block) || params[:canvas_id], iiif_initial_viewer_config: choose_initial_viewer_config(block) || params[:iiif_initial_viewer_config]}.to_query}",
allowfullscreen: true,
class: 'mirador-embed-wrapper',
frameborder: 0,
Expand Down
35 changes: 35 additions & 0 deletions app/views/catalog/select_image_area.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<%= render Blacklight::System::ModalComponent.new do |component| %>
<% component.with_title do %>
<div><h2 class="modal-title">Select image area</h2></div>
<% end %>
<% component.with_body do %>
<div class="p-4">
<% if @document %>
<p>
Adjust the image so that the viewer contains the area you want to display to exhibit visitors.
<ul>
<li>Click-and-drag to adjust position</li>
<li>Adjust zoom-level</li>
<li>Change page (for multi-page items)</li>
</ul>
Select Save changes to save the image area.
</p>
<%= render CustomViewerComponent.new(document: @document, presenter: document_presenter(@document), view_config: blacklight_config) %>
<% else %>
<p>
<%= link_to params[:id], spotlight.exhibit_solr_document_path(current_exhibit, params[:id]) %> does on exist in this exhibit.
</p>
<% end %>
</div>
<% end %>
<% component.with_footer do %>
<button type="button" class="btn btn-outline-primary me-2" data-bl-dismiss="modal">
Cancel
</button>
<% if @document %>
<button id="request-state" data-bl-dismiss="modal" data-form-id="<%= params[:form_id] %>" data-item-id="<%= params[:item_id] %>" class="btn btn-primary">
Save changes
</button>
<% end %>
<% end %>
<% end %>
2 changes: 1 addition & 1 deletion app/views/viewers/_mirador3.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<% if exhibit_specific_manifest.present? %>
<%= content_tag :iframe, '',
src: "#{Settings.iiif_embed.url}?#{{ url: exhibit_specific_manifest, image_tools: true, canvas_id: document.canvas.iiif_id }.to_query}",
src: "#{Settings.iiif_embed.url}?#{{ url: exhibit_specific_manifest, image_tools: true, canvas_id: choose_canvas_id(block) || params[:canvas_id], iiif_initial_viewer_config: choose_initial_viewer_config(block) || params[:iiif_initial_viewer_config] }.to_query}",
allowfullscreen: true,
class: 'mirador-embed-wrapper',
frameborder: 0,
Expand Down
2 changes: 1 addition & 1 deletion config/initializers/blacklight_oembed.rb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Blacklight::Oembed::Engine.config.additional_params = [:canvas_id, :search, :suggested_search, :maxheight]
Blacklight::Oembed::Engine.config.additional_params = [:canvas_id, :search, :suggested_search, :maxheight, :iiif_initial_viewer_config]
4 changes: 3 additions & 1 deletion config/initializers/oembed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

OEmbed::Providers.register_all

purl_provider = OEmbed::Provider.new('http://purl.stanford.edu/embed.{format}?&hide_title=true')
#purl_provider = OEmbed::Provider.new('http://purl.stanford.edu/embed.{format}?&hide_title=true')
purl_provider = OEmbed::Provider.new('https://sul-purl-stage.stanford.edu/embed.{format}?&hide_title=true')
purl_provider << 'http://purl.stanford.edu/*'
purl_provider << 'https://purl.stanford.edu/*'
purl_provider << 'https://sul-purl-stage.stanford.edu/*'
purl_provider << 'http://searchworks.stanford.edu/*'

purl_uat_provider = OEmbed::Provider.new('https://sul-purl-uat.stanford.edu/embed.{format}?&hide_title=true')
Expand Down
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
end
end

get '/:exhibit_id/select_image_area/:id' => 'catalog#select_image_area'

resources :solr_documents, only: [:show], path: '/catalog', controller: 'catalog'
resource :catalog, only: [:index], as: 'catalog', path: '/catalog', controller: 'catalog' do
concerns :searchable
Expand Down
2 changes: 1 addition & 1 deletion config/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ feature_flags:
traject:
processing_thread_pool: 1
iiif_embed:
url: https://embed.stanford.edu/iiif
url: https://embed-stage.stanford.edu/iiif
iiif_dnd_base_url: https://library.stanford.edu/iiif?%{query}
action_mailer:
default_options:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddInitialViewerConfigToSpotlightFeaturedImages < ActiveRecord::Migration[7.2]
def change
add_column :spotlight_featured_images, :iiif_initial_viewer_config, :string
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.2].define(version: 2024_09_16_120405) do
ActiveRecord::Schema[7.2].define(version: 2024_12_12_200851) do
create_table "bibliography_services", force: :cascade do |t|
t.string "header"
t.string "api_id"
Expand Down Expand Up @@ -217,6 +217,7 @@
t.string "iiif_canvas_id"
t.string "iiif_image_id"
t.string "iiif_tilesource"
t.string "iiif_initial_viewer_config"
end

create_table "spotlight_filters", force: :cascade do |t|
Expand Down
13 changes: 10 additions & 3 deletions spec/views/catalog/_embedded_mirador3.html.erb_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@
before do
without_partial_double_verification do
allow(view).to receive_messages(
document: document
document: document,
block: nil
)
end
end

it 'renders an iframe' do
render

expect(rendered).to have_css "iframe[src='https://embed.stanford.edu/iiif?#{{ url: manifest_url }.to_query}']"
expect(rendered).to have_css "iframe[src='#{Settings.iiif_embed.url}?#{{
url: manifest_url, canvas_id: '',
iiif_initial_viewer_config: ''
}.to_query}']"
end

context 'with a local IIIF manifest' do
Expand All @@ -28,7 +32,10 @@

render

expect(rendered).to have_css "iframe[src='https://embed.stanford.edu/iiif?#{{ url: expected_url }.to_query}']"
expect(rendered).to have_css "iframe[src='#{Settings.iiif_embed.url}?#{{
url: expected_url, canvas_id: '',
iiif_initial_viewer_config: ''
}.to_query}']"
end
end
end
Loading