From b3ed4ccee3a69d5e4e3b64c64ddca7c7871f02ff Mon Sep 17 00:00:00 2001 From: Trey Pendragon Date: Tue, 8 Aug 2017 12:55:17 -0700 Subject: [PATCH] Adds Append Scanned Resource Functionality (#82) * Enable appending child SRs. * Add support for IIIF Collections --- .../plum_change_set_persister.rb | 10 +++++++++- app/change_sets/scanned_resource_change_set.rb | 5 +++-- app/decorators/scanned_resource_decorator.rb | 4 ++++ app/decorators/valkyrie/resource_decorator.rb | 4 ++++ app/services/manifest_builder.rb | 4 +++- app/views/catalog/_admin_controls.html.erb | 14 ++++++++++++++ app/views/records/edit_fields/_append_id.html.erb | 1 + config/routes.rb | 1 + .../plum_change_set_persister_spec.rb | 14 ++++++++++++++ spec/controllers/catalog_controller_spec.rb | 2 ++ .../scanned_resources_controller_spec.rb | 14 +++++++++++++- spec/routing/scanned_resource_routes_spec.rb | 9 +++++++++ spec/services/manifest_builder_spec.rb | 11 +++++++++++ .../concerns/valhalla/resource_controller.rb | 2 +- valhalla/app/services/valhalla/contextual_path.rb | 4 ++-- 15 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 app/views/records/edit_fields/_append_id.html.erb create mode 100644 spec/routing/scanned_resource_routes_spec.rb diff --git a/app/change_set_persisters/plum_change_set_persister.rb b/app/change_set_persisters/plum_change_set_persister.rb index 86eb280cb8..f6737613ca 100644 --- a/app/change_set_persisters/plum_change_set_persister.rb +++ b/app/change_set_persisters/plum_change_set_persister.rb @@ -54,12 +54,20 @@ def before_save(change_set:) create_files(change_set: change_set) end - def after_save(change_set:, updated_resource:); end + def after_save(change_set:, updated_resource:) + append(append_id: change_set.append_id, updated_resource: updated_resource) if change_set.append_id.present? + end def before_delete(change_set:) clean_up_collection_associations(change_set: change_set) if change_set.resource.is_a?(Collection) end + def append(append_id:, updated_resource:) + parent_obj = query_service.find_by(id: append_id) + parent_obj.member_ids = parent_obj.member_ids + [updated_resource.id] + persister.save(resource: parent_obj) + end + def apply_remote_metadata(change_set:) return unless change_set.respond_to?(:source_metadata_identifier) return unless change_set.apply_remote_metadata? diff --git a/app/change_sets/scanned_resource_change_set.rb b/app/change_sets/scanned_resource_change_set.rb index 1acd18fee4..ff233af4fb 100644 --- a/app/change_sets/scanned_resource_change_set.rb +++ b/app/change_sets/scanned_resource_change_set.rb @@ -1,3 +1,4 @@ + # frozen_string_literal: true class ScannedResourceChangeSet < Valkyrie::ChangeSet delegate :human_readable_type, to: :model @@ -20,7 +21,6 @@ class ScannedResourceChangeSet < Valkyrie::ChangeSet property :logical_structure, multiple: true, required: false, type: Types::Strict::Array.member(Structure), default: [Structure.new(label: "Logical", nodes: [])] property :state, multiple: false, required: true, default: BookWorkflow.aasm.initial_state.to_s property :read_groups, multiple: true, required: false - # Virtual Attributes property :refresh_remote_metadata, virtual: true, multiple: false property :files, virtual: true, multiple: true, required: false @@ -45,7 +45,8 @@ def primary_terms :portion_note, :nav_date, :state, - :member_of_collection_ids + :member_of_collection_ids, + :append_id ] end diff --git a/app/decorators/scanned_resource_decorator.rb b/app/decorators/scanned_resource_decorator.rb index c48ae9a142..98476f2063 100644 --- a/app/decorators/scanned_resource_decorator.rb +++ b/app/decorators/scanned_resource_decorator.rb @@ -48,4 +48,8 @@ def rendered_holding_location def manageable_structure? true end + + def attachable_objects + [ScannedResource] + end end diff --git a/app/decorators/valkyrie/resource_decorator.rb b/app/decorators/valkyrie/resource_decorator.rb index 1fe62d72e0..a19cfec142 100644 --- a/app/decorators/valkyrie/resource_decorator.rb +++ b/app/decorators/valkyrie/resource_decorator.rb @@ -25,4 +25,8 @@ def manageable_files? def manageable_structure? false end + + def attachable_objects + [] + end end diff --git a/app/services/manifest_builder.rb b/app/services/manifest_builder.rb index e6b9dd26f3..2d961f9944 100644 --- a/app/services/manifest_builder.rb +++ b/app/services/manifest_builder.rb @@ -23,7 +23,9 @@ def to_s end def work_presenters - [] + @work_presenters ||= (members - leaf_nodes).map do |node| + RootNode.new(node) + end end def file_set_presenters diff --git a/app/views/catalog/_admin_controls.html.erb b/app/views/catalog/_admin_controls.html.erb index b53ff3347b..e780f34a59 100644 --- a/app/views/catalog/_admin_controls.html.erb +++ b/app/views/catalog/_admin_controls.html.erb @@ -6,6 +6,20 @@ <% if decorated_resource.manageable_structure? %> <%= link_to "Edit Structure", main_app.polymorphic_path([:structure, resource]), class: 'btn btn-default' %> <% end %> + <% if decorated_resource.attachable_objects.length > 0 %> +
+ + +
+ <% end %> <%= link_to "Delete This #{resource.human_readable_type}", main_app.polymorphic_path([resource]), class: 'btn btn-danger pull-right', data: { confirm: "Delete this #{resource.human_readable_type}?" }, method: :delete %> diff --git a/app/views/records/edit_fields/_append_id.html.erb b/app/views/records/edit_fields/_append_id.html.erb new file mode 100644 index 0000000000..8ace2e47a8 --- /dev/null +++ b/app/views/records/edit_fields/_append_id.html.erb @@ -0,0 +1 @@ + <%= f.input :append_id, as: :hidden %> diff --git a/config/routes.rb b/config/routes.rb index a1c75fa471..96d07b1743 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -67,6 +67,7 @@ post :browse_everything_files end end + get '/scanned_resources/:parent_id/new', to: 'scanned_resources#new', as: :parent_new_scanned_resource end resources :collections diff --git a/spec/change_set_persisters/plum_change_set_persister_spec.rb b/spec/change_set_persisters/plum_change_set_persister_spec.rb index e8385e2b9d..735d904c69 100644 --- a/spec/change_set_persisters/plum_change_set_persister_spec.rb +++ b/spec/change_set_persisters/plum_change_set_persister_spec.rb @@ -183,4 +183,18 @@ end end end + + describe "appending" do + it "appends a child via #append_id" do + parent = FactoryGirl.create_for_repository(:scanned_resource) + resource = FactoryGirl.build(:scanned_resource) + change_set = change_set_class.new(resource) + change_set.validate(append_id: parent.id.to_s) + change_set.sync + + output = change_set_persister.save(change_set: change_set) + reloaded = query_service.find_by(id: parent.id) + expect(reloaded.member_ids).to eq [output.id] + end + end end diff --git a/spec/controllers/catalog_controller_spec.rb b/spec/controllers/catalog_controller_spec.rb index 848b6d78ac..97aa5bb503 100644 --- a/spec/controllers/catalog_controller_spec.rb +++ b/spec/controllers/catalog_controller_spec.rb @@ -92,6 +92,8 @@ expect(response.body).to have_link "Delete This Scanned Resource", href: scanned_resource_path(resource) expect(response.body).to have_link "File Manager", href: file_manager_scanned_resource_path(resource) expect(response.body).to have_link "Edit Structure", href: structure_scanned_resource_path(resource) + expect(response.body).to have_button "Attach Child" + expect(response.body).to have_link "Attach Scanned Resource", href: parent_new_scanned_resource_path(resource) end it "renders for a FileSet" do diff --git a/spec/controllers/scanned_resources_controller_spec.rb b/spec/controllers/scanned_resources_controller_spec.rb index 1f30a8b8c8..186eee3600 100644 --- a/spec/controllers/scanned_resources_controller_spec.rb +++ b/spec/controllers/scanned_resources_controller_spec.rb @@ -23,8 +23,9 @@ render_views it "has a form for creating scanned resources" do collection = FactoryGirl.create_for_repository(:collection) + parent = FactoryGirl.create_for_repository(:scanned_resource) - get :new + get :new, params: { parent_id: parent.id.to_s } expect(response.body).to have_field "Title" expect(response.body).to have_field "Source Metadata ID" expect(response.body).to have_field "scanned_resource[refresh_remote_metadata]" @@ -34,6 +35,7 @@ expect(response.body).to have_field "Holding Location" expect(response.body).to have_field "Portion Note" expect(response.body).to have_field "Navigation Date" + expect(response.body).to have_selector "#scanned_resource_append_id[value='#{parent.id}']", visible: false expect(response.body).to have_select "Collections", name: "scanned_resource[member_of_collection_ids][]", options: ["", collection.title.first] expect(response.body).to have_select "Rights Statement", name: "scanned_resource[rights_statement]", options: [""] + ControlledVocabulary.for(:rights_statement).all.map(&:label) expect(response.body).to have_select "PDF Type", name: "scanned_resource[pdf_type]", options: ["Color PDF", "Grayscale PDF", "Bitonal PDF", "No PDF"] @@ -74,6 +76,16 @@ id = response.location.gsub("http://test.host/catalog/", "").gsub("%2F", "/").gsub(/^id-/, "") expect(find_resource(id).title).to contain_exactly "Title 1", "Title 2" end + it "can create a nested scanned resource" do + parent = FactoryGirl.create_for_repository(:scanned_resource) + post :create, params: { scanned_resource: valid_params.merge(append_id: parent.id.to_s) } + + expect(response).to be_redirect + expect(response.location).to start_with "http://test.host/catalog/parent/#{parent.id}/" + id = response.location.gsub("http://test.host/catalog/parent/#{parent.id}/", "").gsub(/^id-/, "") + expect(find_resource(id).title).to contain_exactly "Title 1", "Title 2" + expect(find_resource(parent.id).member_ids).to eq [Valkyrie::ID.new(id)] + end context "when joining a collection" do let(:valid_params) do { diff --git a/spec/routing/scanned_resource_routes_spec.rb b/spec/routing/scanned_resource_routes_spec.rb new file mode 100644 index 0000000000..9cf2e398d7 --- /dev/null +++ b/spec/routing/scanned_resource_routes_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true +require 'rails_helper' + +RSpec.describe "Scanned Resource Routes" do + it "routes nested record creates appropriately" do + expect(get("/concern/scanned_resources/1/new")).to route_to("scanned_resources#new", parent_id: "1") + expect(get(parent_new_scanned_resource_path(parent_id: "1"))).to route_to("scanned_resources#new", parent_id: "1") + end +end diff --git a/spec/services/manifest_builder_spec.rb b/spec/services/manifest_builder_spec.rb index 42715d987d..d6eb7dfa1c 100644 --- a/spec/services/manifest_builder_spec.rb +++ b/spec/services/manifest_builder_spec.rb @@ -56,4 +56,15 @@ def logical_structure(file_set_id) expect(canvas_id).to eq structure_canvas_id end end + context "when given a nested child" do + let(:scanned_resource) { FactoryGirl.create_for_repository(:scanned_resource, member_ids: child.id) } + let(:child) { FactoryGirl.create_for_repository(:scanned_resource, files: [file]) } + it "builds a IIIF collection" do + output = manifest_builder.build + expect(output).to be_kind_of Hash + expect(output["@type"]).to eq "sc:Collection" + expect(output["manifests"].length).to eq 1 + expect(output["manifests"][0]["@id"]).to eq "http://www.example.com/concern/scanned_resources/#{child.id}/manifest" + end + end end diff --git a/valhalla/app/controllers/concerns/valhalla/resource_controller.rb b/valhalla/app/controllers/concerns/valhalla/resource_controller.rb index 7f3d92f8d5..9a0925ace3 100644 --- a/valhalla/app/controllers/concerns/valhalla/resource_controller.rb +++ b/valhalla/app/controllers/concerns/valhalla/resource_controller.rb @@ -10,7 +10,7 @@ module ResourceController end def new - @change_set = change_set_class.new(resource_class.new).prepopulate! + @change_set = change_set_class.new(resource_class.new, append_id: params[:parent_id]).prepopulate! authorize! :create, resource_class end diff --git a/valhalla/app/services/valhalla/contextual_path.rb b/valhalla/app/services/valhalla/contextual_path.rb index 7d07bde536..07c4ef1c89 100644 --- a/valhalla/app/services/valhalla/contextual_path.rb +++ b/valhalla/app/services/valhalla/contextual_path.rb @@ -10,7 +10,7 @@ def initialize(child:, parent_id: nil) end def show - if parent_id + if parent_id.present? polymorphic_path([:parent, :solr_document], parent_id: parent_id, id: "id-#{child.id}") else polymorphic_path([:solr_document], id: "id-#{child.id}") @@ -18,7 +18,7 @@ def show end def file_manager - if parent_id + if parent_id.present? polymorphic_path([:file_manager, child], parent_id: parent_id) else polymorphic_path([:file_manager, child])