Skip to content

Commit

Permalink
Merge pull request #1376 from samvera-labs/1372_iiif_rendering
Browse files Browse the repository at this point in the history
Implementation of #1372: support downloads (eg. a PDF of the whole paged item) via Universal Viewer
  • Loading branch information
mjgiarlo committed Sep 21, 2017
2 parents 62a8325 + 668deb5 commit 9bdc698
Show file tree
Hide file tree
Showing 17 changed files with 202 additions and 14 deletions.
6 changes: 5 additions & 1 deletion app/forms/hyrax/generic_work_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ module Hyrax
class GenericWorkForm < Hyrax::Forms::WorkForm
self.model_class = ::GenericWork
include HydraEditor::Form::Permissions
self.terms += [:resource_type]
self.terms += [:resource_type, :rendering_ids]

def secondary_terms
super - [:rendering_ids]
end
end
end
6 changes: 5 additions & 1 deletion app/forms/hyrax/image_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
module Hyrax
class ImageForm < Hyrax::Forms::WorkForm
self.model_class = ::Image
self.terms += [:resource_type, :extent]
self.terms += [:resource_type, :extent, :rendering_ids]

def secondary_terms
super - [:rendering_ids]
end
end
end
5 changes: 5 additions & 0 deletions app/indexers/file_set_indexer.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
class FileSetIndexer < Hyrax::FileSetIndexer
self.thumbnail_path_service = IIIFWorkThumbnailPathService
def generate_solr_document
super.tap do |solr_doc|
solr_doc['hasFormat_ssim'] = object.rendering_ids
end
end
end
12 changes: 12 additions & 0 deletions app/models/concerns/has_rendering.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module HasRendering
extend ActiveSupport::Concern

included do
# rubocop:disable Rails/HasAndBelongsToMany
# dc:hasFormat defined in Presentation API context: http://iiif.io/api/presentation/2/context.json
has_and_belongs_to_many :renderings,
predicate: ::RDF::Vocab::DC.hasFormat,
class_name: 'ActiveFedora::Base'
# rubocop:enable Rails/HasAndBelongsToMany
end
end
21 changes: 21 additions & 0 deletions app/models/file_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,28 @@
class FileSet < ActiveFedora::Base
include ::Hyrax::FileSetBehavior

before_destroy :remove_rendering_relationship

# Hyku has its own FileSetIndexer: app/indexers/file_set_indexer.rb
# It overrides Hyrax to inject IIIF behavior.
self.indexer = FileSetIndexer

def rendering_ids
to_param
end

private

# If any parent objects are pointing at this object as their
# rendering, remove that pointer.
def remove_rendering_relationship
parent_objects = parents
return if parent_objects.empty?
parent_objects.each do |work|
if work.rendering_ids.include(id)
new_rendering_ids = work.rendering_ids.delete(id)
work.update(rendering_ids: new_rendering_ids)
end
end
end
end
1 change: 1 addition & 0 deletions app/models/generic_work.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class GenericWork < ActiveFedora::Base
include ::Hyrax::WorkBehavior
include ::Hyrax::BasicMetadata
include HasRendering
validates :title, presence: { message: 'Your work must have a title.' }

# This indexer uses IIIF thumbnails:
Expand Down
1 change: 1 addition & 0 deletions app/models/image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# `rails generate hyrax:work Image`
class Image < ActiveFedora::Base
include ::Hyrax::WorkBehavior
include HasRendering

property :extent, predicate: ::RDF::Vocab::DC.extent, multiple: true do |index|
index.as :stored_searchable
Expand Down
1 change: 1 addition & 0 deletions app/models/solr_document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ class SolrDocument
use_extension(Hydra::ContentNegotiation)

attribute :extent, Solr::Array, solr_name('extent')
attribute :rendering_ids, Solr::Array, solr_name('hasFormat', :symbol)
end
2 changes: 2 additions & 0 deletions app/presenters/hyku/file_set_presenter.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module Hyku
class FileSetPresenter < Hyrax::FileSetPresenter
include DisplaysImage
# CurationConcern methods
delegate :rendering_ids, to: :solr_document
end
end
47 changes: 46 additions & 1 deletion app/presenters/hyku/manifest_enabled_work_show_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,61 @@ module Hyku
class ManifestEnabledWorkShowPresenter < Hyrax::WorkShowPresenter
Hyrax::MemberPresenterFactory.file_presenter_class = Hyku::FileSetPresenter

delegate :extent, to: :solr_document
delegate :extent, :rendering_ids, to: :solr_document

def manifest_url
manifest_helper.polymorphic_url([:manifest, self])
end

# Hash to populate IIIF manifest with extras. Currently only sequence rendering is implemented.
#
# @return [Hash] hash including sequence_rendering
def manifest_extras
{
sequence_rendering: sequence_rendering
}
end

private

def manifest_helper
@manifest_helper ||= ManifestHelper.new(request.base_url)
end

# IIIF rendering linking property for inclusion in the manifest
#
# @return [Array] array of rendering hashes
def sequence_rendering
renderings = []
if solr_document.rendering_ids.present?
solr_document.rendering_ids.each do |file_set_id|
renderings << build_rendering(file_set_id)
end
end
renderings.flatten
end

# Build a rendering hash
#
# @return [Hash] rendering
def build_rendering(file_set_id)
query_for_rendering(file_set_id).map do |x|
label = x['label_ssi'] ? ": #{x.fetch('label_ssi')}" : ''
{
'@id' => Hyrax::Engine.routes.url_helpers.download_url(x.fetch(ActiveFedora.id_field), host: request.host),
'format' => x.fetch('mime_type_ssi') ? x.fetch('mime_type_ssi') : 'unknown mime type',
'label' => 'Download whole resource' + label
}
end
end

# Query for the properties to create a rendering
#
# @return [SolrResult] query result
def query_for_rendering(file_set_id)
ActiveFedora::SolrService.query("id:#{file_set_id}",
fl: [ActiveFedora.id_field, 'label_ssi', 'mime_type_ssi'],
rows: 1)
end
end
end
8 changes: 8 additions & 0 deletions app/views/hyrax/base/_form_media.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<% if f.object.persisted? && f.object.member_ids.present? %>
<%= render 'form_representative', f: f %>
<%= render 'form_thumbnail', f: f %>
<%# display the rendering dropdown only if the representitive responds to image? (ie. Universal Viewer is used) %>
<% if curation_concern.representative.image? %>
<%= render 'form_rendering', f: f %>
<% end %>
<% end %>
15 changes: 15 additions & 0 deletions app/views/hyrax/base/_form_rendering.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="row">
<div class="col-md-12">
<fieldset id="representative-media">
<legend>
Rendering
</legend>
<p>Select file(s) to be offered as as downloads for every image in Universal Viewer, for example a PDF of the whole <%= f.object.human_readable_type %>.</p>
<%= f.select :rendering_ids,
f.object.select_files,
{ include_blank: true },
{ class: 'form-control', multiple: true } %>
</fieldset>
</div>
</div>

4 changes: 3 additions & 1 deletion spec/factories/generic_works.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

factory :work_with_one_file do
before(:create) do |work, evaluator|
work.ordered_members << FactoryGirl.create(:file_set, user: evaluator.user, title: ['A Contained Generic File'])
work.ordered_members << FactoryGirl.create(:file_set,
user: evaluator.user,
title: ['A Contained Generic File'])
end
end

Expand Down
20 changes: 18 additions & 2 deletions spec/forms/hyrax/generic_work_form_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
# Generated via
# `rails generate curation_concerns:work GenericWork`
RSpec.describe Hyrax::GenericWorkForm do
it "has tests" do
skip "Add your tests here"
let(:work) { GenericWork.new }
let(:form) { described_class.new(work, nil, nil) }
let(:file_set) { FactoryGirl.create(:file_set) }

describe ".model_attributes" do
subject { described_class.model_attributes(params) }

let(:params) { ActionController::Parameters.new(attributes) }
let(:attributes) do
{
title: ['foo'],
rendering_ids: [file_set.id]
}
end

it 'permits parameters' do
expect(subject['rendering_ids']).to eq [file_set.id]
end
end
end
24 changes: 21 additions & 3 deletions spec/forms/hyrax/image_form_spec.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
# Generated via
# `rails generate hyrax:work Image`
# `rails generate hyrax:work Image`

RSpec.describe Hyrax::ImageForm do
it "has tests" do
skip "Add your tests here"
let(:work) { Image.new }
let(:form) { described_class.new(work, nil, nil) }
let(:file_set) { FactoryGirl.create(:file_set) }

describe ".model_attributes" do
subject { described_class.model_attributes(params) }

let(:params) { ActionController::Parameters.new(attributes) }
let(:attributes) do
{
title: ['foo'],
rendering_ids: [file_set.id],
extent: ['extent']
}
end

it 'permits parameters' do
expect(subject['rendering_ids']).to eq [file_set.id]
expect(subject['extent']).to eq ['extent']
end
end
end
25 changes: 20 additions & 5 deletions spec/presenters/hyku/manifest_enabled_work_show_presenter_spec.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
RSpec.describe Hyku::ManifestEnabledWorkShowPresenter do
let(:document) { { "has_model_ssim" => ['GenericWork'], 'id' => '99' } }
let(:work) { FactoryGirl.create(:work_with_one_file) }
let(:document) { work.to_solr }
let(:solr_document) { SolrDocument.new(document) }
let(:request) { double(base_url: 'http://test.host') }
let(:request) { double(base_url: 'http://test.host', host: 'http://test.host') }
let(:ability) { nil }
let(:presenter) { described_class.new(solr_document, ability, request) }

describe "#manifest_url" do
subject { presenter.manifest_url }
let(:document) { { "has_model_ssim" => ['GenericWork'], 'id' => '99' } }
it { is_expected.to eq 'http://test.host/concern/generic_works/99/manifest' }
end

Expand All @@ -15,14 +17,27 @@
presenter.representative_presenter
end

let(:work) { FactoryGirl.create(:work_with_one_file) }
let(:document) { work.to_solr }

before do
work.representative_id = work.file_sets.first.id
end
it "returns a presenter" do
expect(subject).to be_kind_of Hyku::FileSetPresenter
end
end

describe "manifest_extras" do
subject do
presenter.manifest_extras
end

before do
Hydra::Works::AddFileToFileSet.call(work.file_sets.first,
fixture_file('images/world.png'), :original_file)
end

it "returns a hash containing the rendering information" do
work.rendering_ids = [work.file_sets.first.id]
expect(subject).to include(:sequence_rendering)
end
end
end
18 changes: 18 additions & 0 deletions spec/views/hyrax/base/_form_rendering.html.erb_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
RSpec.describe 'hyrax/base/_form_rendering.html.erb', type: :view do
let(:ability) { double }
let(:work) { FactoryGirl.create(:work_with_one_file) }
let(:form) do
Hyrax::GenericWorkForm.new(work, ability, controller)
end

let(:page) do
view.simple_form_for form do |f|
render 'hyrax/base/form_rendering', f: f
end
Capybara::Node::Simple.new(rendered)
end

it 'has a rendering_ids field' do
expect(page).to have_selector("select#generic_work_rendering_ids", count: 1)
end
end

0 comments on commit 9bdc698

Please sign in to comment.