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

Valkyrize batch editing #6148

Merged
merged 10 commits into from
Aug 22, 2023
35 changes: 32 additions & 3 deletions app/controllers/hyrax/batch_edits_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,30 @@ def update_document(obj)
obj.save
end

def valkyrie_update_document(obj)
form = form_class.new(obj, current_ability, nil)
return unless form.validate(params[form_class.model_class.model_name.param_key])

cleanup_form_fields form

result = transactions['change_set.update_work']
.with_step_args('work_resource.save_acl' => { permissions_params: form.input_params["permissions"] })
.call(form)
obj = result.value!

InheritPermissionsJob.perform_now(obj)
tpendragon marked this conversation as resolved.
Show resolved Hide resolved
VisibilityCopyJob.perform_now(obj)
tpendragon marked this conversation as resolved.
Show resolved Hide resolved
end

def update
case params["update_type"]
when "update"
batch.each do |doc_id|
update_document(Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: doc_id, use_valkyrie: false))
if Hyrax.config.use_valkyrie?
valkyrie_update_document(Hyrax.query_service.find_by(id: doc_id))
else
update_document(Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: doc_id, use_valkyrie: false))
end
end
flash[:notice] = "Batch update complete"
after_update
Expand Down Expand Up @@ -97,15 +116,15 @@ def destroy_batch
end

def form_class
Forms::BatchEditForm
Hyrax.config.use_valkyrie? ? Forms::ResourceBatchEditForm : Forms::BatchEditForm
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: How's Hyrax feel about ternaries over explicit branches? We use an if/else above, should we use one here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it's not being flagged by rubocop, i think we don't block on style.

on the other hand, i'm wondering if we could just drop all the old handling? if the valkyrie code path is working, do we need the opt-in?

end

def terms
form_class.terms
end

def work_params(extra_params = {})
work_params = params[form_class.model_name.param_key] || ActionController::Parameters.new
work_params = params[form_class.model_class.model_name.param_key] || ActionController::Parameters.new
form_class.model_attributes(work_params.merge(extra_params))
end

Expand Down Expand Up @@ -135,5 +154,15 @@ def redirect_to_return_controller
redirect_to hyrax.dashboard_path
end
end

# Clean up form fields
# @param form Hyrax::Froms::ResourceBatchEditForm
def cleanup_form_fields(form)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Can we get a comment about why this is necessary? Why doesn't the form handle this somewhere? Does this mean the form's not useful outside of this controller?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm also curious about this.
it does seem like the form should handle these transformations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what is the best way to handle it. The another way that we can try is to merge the submitted params to the resource attributes in form.validate(params). Please advice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action item: Fix this, I can take a look at it

form.lease = nil if form.lease && form.lease.fields['lease_expiration_date'].nil?
form.embargo = nil if form.embargo && form.embargo.fields['embargo_release_date'].nil?
form.fields.keys.each do |k|
form.fields[k] = nil if form.fields[k].is_a?(Array) && form.fields[k].blank?
end
end
end
end
90 changes: 90 additions & 0 deletions app/forms/hyrax/forms/resource_batch_edit_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# frozen_string_literal: true
module Hyrax
module Forms
class ResourceBatchEditForm < Hyrax::Forms::ResourceForm
include Hyrax::FormFields(:basic_metadata)

self.required_fields = []
self.model_class = Hyrax.primary_work_type
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this form only support the primary work type?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. It'll work for any models, even with a batch that mixes with different objects types. The model_class here is only served for retrieving a consistent param_key for different object types.


# Contains a list of titles of all the works in the batch
attr_accessor :names

# @param [Hyrax::Work] model the model backing the form
# @param [Ability] current_ability the user authorization model
# @param [Array<String>] batch_document_ids a list of document ids in the batch
def initialize(model, _current_ability, batch_document_ids)
@names = []
@batch_document_ids = batch_document_ids
if @batch_document_ids.present?
super(model.class.new(initialize_combined_fields))
else
super(model)
end
end

def terms
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is it okay to hard code these values? shouldn't they be derived from configuration?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action item: See if we can use the model configurable fields. Pull @no-reply in if we get stuck with this.

[:creator, :contributor, :description,
:keyword, :resource_type, :license, :publisher, :date_created,
:subject, :language, :identifier, :based_near,
:related_url]
end

attr_reader :batch_document_ids

# Returns a list of parameters we accept from the form
# rubocop:disable Metrics/MethodLength
def self.build_permitted_params
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: I just don't know much about hyrax forms anymore - if there's form objects with a restricted subset of fields, why do they also need to define their permitted params? Where's this get used - do all the form objects have these or is it something special about bulk edit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tpendragon you probably know more about this form setup than most of us. these are valkyrie ChangeSets.

i have the same question about why this is needed, and where it's used other than the tests below.

[{ creator: [] },
{ contributor: [] },
{ description: [] },
{ keyword: [] },
{ resource_type: [] },
{ license: [] },
{ publisher: [] },
{ date_created: [] },
{ subject: [] },
{ language: [] },
{ identifier: [] },
{ based_near: [] },
{ related_url: [] },
{ permissions_attributes: [:type, :name, :access, :id, :_destroy] },
:on_behalf_of,
:version,
:add_works_to_collection,
:visibility_during_embargo,
:embargo_release_date,
:visibility_after_embargo,
:visibility_during_lease,
:lease_expiration_date,
:visibility_after_lease,
:visibility,
{ based_near_attributes: [:id, :_destroy] }]
end
# rubocop:enable Metrics/MethodLength

# @param name [Symbol]
# @return [Symbol]
# @note Added for ActiveModel compatibility.
def column_for_attribute(name)
name
end

private

# override this method if you need to initialize more complex RDF assertions (b-nodes)
# @return [Hash<String, Array>] the list of unique values per field
def initialize_combined_fields
# For each of the files in the batch, set the attributes to be the concatenation of all the attributes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a little comment about why I'd want it to do this?

batch_document_ids.each_with_object({}) do |doc_id, combined_attributes|
work = Hyrax.query_service.find_by(id: doc_id)
terms.each do |field|
combined_attributes[field] ||= []
combined_attributes[field] = (combined_attributes[field] + work[field].to_a).uniq
end
names << work.to_s
end
end
end
end
end
2 changes: 1 addition & 1 deletion app/services/hyrax/visibility_propagator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class VisibilityPropagator
# @return [#propagate]
def self.for(source:)
case source
when Hyrax::WorkBehavior # ActiveFedora
when ActiveFedora::Base # ActiveFedora
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: I missed it, where did this change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is that the monograph https://github.com/samvera/hyrax/blob/main/.dassie/app/models/monograph.rb model won't include Hyrax::WorkBehavior, which will cause tests related to visibility changes failed for fileset visibility propagating.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, the generated model doesn't include WorkBehavior. it seems like the weird thing here is that the VisibilityCopyJob is casting the valkyrie resource back to an AF::Base, even when use_valkyrie is true(?)

i think in the ideal case, both of the following would be true:

  • VisibilityCopyJob should support works generated by Wings; being generous about the type check as proposed here seems good.
  • the use_valkyrie code path here would leverage valkyrie objects throughout.

i think it would be worth checking up about the second point as part of this work.

FileSetVisibilityPropagator.new(source: source)
when Hyrax::Resource # Valkyrie
ResourceVisibilityPropagator.new(source: source)
Expand Down
4 changes: 4 additions & 0 deletions lib/wings/active_fedora_converter/default_work.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ def enforce_future_date_for_lease?
false
end

def file_sets
members.select(&:file_set?)
tpendragon marked this conversation as resolved.
Show resolved Hide resolved
end

def indexing_service
Hyrax::ValkyrieIndexer.for(resource: valkyrie_resource)
end
Expand Down
Loading