curation_concerns 1.7.0.beta1 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/curation_concerns/application_controller_behavior.rb +2 -8
  3. data/app/controllers/concerns/curation_concerns/collections_controller_behavior.rb +13 -13
  4. data/app/controllers/concerns/curation_concerns/curation_concern_controller.rb +40 -3
  5. data/app/controllers/concerns/curation_concerns/selects_collections.rb +2 -0
  6. data/app/controllers/concerns/curation_concerns/single_use_links_controller_behavior.rb +9 -7
  7. data/app/models/concerns/curation_concerns/solr_document_behavior.rb +8 -0
  8. data/app/models/concerns/curation_concerns/suppressible.rb +31 -0
  9. data/app/models/concerns/curation_concerns/work_behavior.rb +1 -1
  10. data/app/presenters/curation_concerns/inspect_work_presenter.rb +52 -0
  11. data/app/presenters/curation_concerns/work_show_presenter.rb +4 -0
  12. data/app/search_builders/curation_concerns/README.md +69 -0
  13. data/app/search_builders/curation_concerns/collection_search_builder.rb +35 -17
  14. data/app/search_builders/curation_concerns/filter_suppressed.rb +15 -0
  15. data/app/search_builders/curation_concerns/filter_suppressed_with_roles.rb +30 -0
  16. data/app/search_builders/curation_concerns/search_filters.rb +1 -0
  17. data/app/search_builders/curation_concerns/single_collection_search_builder.rb +5 -0
  18. data/app/search_builders/curation_concerns/work_search_builder.rb +1 -0
  19. data/app/services/curation_concerns/actors/actor_factory.rb +0 -1
  20. data/app/services/curation_concerns/embargo_service.rb +0 -13
  21. data/app/services/curation_concerns/workflow/action_taken_service.rb +25 -15
  22. data/app/services/curation_concerns/workflow/activate_object.rb +9 -2
  23. data/app/services/curation_concerns/workflow/deactivate_object.rb +9 -4
  24. data/app/services/curation_concerns/workflow/{remove_depositor_permissions.rb → grant_edit_to_depositor.rb} +4 -4
  25. data/app/services/curation_concerns/workflow/workflow_action_service.rb +1 -1
  26. data/app/services/curation_concerns/working_directory.rb +1 -1
  27. data/app/views/curation_concerns/base/inspect_work.html.erb +37 -0
  28. data/app/views/curation_concerns/base/unavailable.html.erb +10 -0
  29. data/app/views/curation_concerns/operations/index.html.erb +1 -1
  30. data/app/views/curation_concerns/operations/show.html.erb +1 -1
  31. data/config/locales/curation_concerns.en.yml +3 -0
  32. data/curation_concerns.gemspec +3 -3
  33. data/lib/curation_concerns/rails/routes.rb +1 -0
  34. data/lib/curation_concerns/version.rb +1 -1
  35. data/lib/curation_concerns/workflow_authorization_exception.rb +4 -0
  36. data/lib/generators/curation_concerns/templates/workflow.json.erb +1 -0
  37. data/spec/controllers/curation_concerns/generic_works_controller_spec.rb +78 -16
  38. data/spec/controllers/selects_collections_controller_spec.rb +2 -4
  39. data/spec/factories/generic_works.rb +6 -0
  40. data/spec/factories/workflows.rb +1 -1
  41. data/spec/features/add_file_spec.rb +3 -0
  42. data/spec/features/create_child_work_spec.rb +13 -7
  43. data/spec/features/update_file_spec.rb +3 -2
  44. data/spec/forms/curation_concerns/forms/workflow_action_form_spec.rb +2 -4
  45. data/spec/indexers/work_indexer_spec.rb +11 -7
  46. data/spec/jobs/characterize_job_spec.rb +2 -2
  47. data/spec/models/curation_concerns/work_behavior_spec.rb +1 -1
  48. data/spec/models/generic_work_spec.rb +16 -7
  49. data/spec/models/solr_document_spec.rb +13 -0
  50. data/spec/presenters/curation_concerns/inspect_work_presenter_spec.rb +58 -0
  51. data/spec/presenters/curation_concerns/work_show_presenter_spec.rb +9 -0
  52. data/spec/presenters/curation_concerns/workflow_presenter_spec.rb +29 -15
  53. data/spec/routing/route_spec.rb +4 -0
  54. data/spec/services/curation_concerns/workflow/action_taken_service_spec.rb +5 -6
  55. data/spec/services/curation_concerns/workflow/activate_object_spec.rb +13 -12
  56. data/spec/services/curation_concerns/workflow/deactivate_object_spec.rb +14 -13
  57. data/spec/services/curation_concerns/workflow/grant_edit_to_depositor_spec.rb +20 -0
  58. data/spec/services/curation_concerns/workflow/permission_query_spec.rb +3 -3
  59. data/spec/services/curation_concerns/workflow/status_list_service_spec.rb +4 -1
  60. data/spec/services/curation_concerns/workflow/workflow_importer_spec.rb +2 -3
  61. data/spec/services/curation_concerns/working_directory_spec.rb +12 -0
  62. data/spec/views/curation_concerns/base/unavailable.html.erb_spec.rb +40 -0
  63. metadata +34 -20
  64. data/app/actors/curation_concerns/actors/grant_edit_to_depositor_actor.rb +0 -19
  65. data/app/models/concerns/curation_concerns/publishable.rb +0 -25
  66. data/app/models/curation_concerns/state_workflow.rb +0 -13
  67. data/app/views/curation_concerns/file_sets/_rights_modal.html.erb +0 -41
  68. data/app/views/records/_rights_modal.html.erb +0 -1
  69. data/spec/actors/curation_concerns/grant_edit_to_depositor_actor_spec.rb +0 -32
  70. data/spec/services/curation_concerns/workflow/remove_depositor_permissions_spec.rb +0 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 976d4594c0d89e8f8fc0fb3eb2e3fda2bc0de0e7
4
- data.tar.gz: 0bba18a25f0eb32c5998c071544accdd09bcebd0
3
+ metadata.gz: 5fce83d5072e8e270e130b70cbbcd172c4a67f8b
4
+ data.tar.gz: 810425e450dc9b3c74b34f8e2e5b5d792caad21c
5
5
  SHA512:
6
- metadata.gz: 2edb7a435f0b294fa1018bca41904327219d0b75ddc8ed736b20851b5d57bce7142085241e9d73e2d4df6c2784fad14fbe8851ff4f8302040af7e1cd2fb4472b
7
- data.tar.gz: 1b31bc0aefd9067b0874e1dd0ab3e383a2a4c5f8b6190a2d977892d17b78edc9acdea5f7a8b3795d087f75cbc23d6ec1b0810da1851ab6bee974c28463bbd5eb
6
+ metadata.gz: 464f2c154c361745c3fcba8eaac523c4ebee3d7a625163980c5754ac5c9f1f476fd8ccb813d3c7d39f42ab26fe96c5dfc72be5113ee0233f7397e73faaea1191
7
+ data.tar.gz: f477c89852669354e20fd1243788c7dec5263e12c0636e9869478317b82422bb4ecdcd7138c5ab74cfeb8ccf5d6978ff7cb91bc206cd964dfbaf28b359fbf5c0
@@ -6,14 +6,8 @@ module CurationConcerns
6
6
 
7
7
  included do
8
8
  helper CurationConcerns::MainAppHelpers
9
-
10
- rescue_from ActiveFedora::ObjectNotFoundError do |exception|
11
- not_found_response(exception)
12
- end
13
-
14
- rescue_from Blacklight::Exceptions::InvalidSolrID do |exception|
15
- not_found_response(exception)
16
- end
9
+ rescue_from ActiveFedora::ObjectNotFoundError, with: :not_found_response
10
+ rescue_from Blacklight::Exceptions::InvalidSolrID, with: :not_found_response
17
11
  end
18
12
 
19
13
  def render_404
@@ -15,16 +15,7 @@ module CurationConcerns
15
15
  copy_blacklight_config_from(::CatalogController)
16
16
 
17
17
  # Catch permission errors
18
- rescue_from Hydra::AccessDenied, CanCan::AccessDenied do |exception|
19
- if exception.action == :edit
20
- redirect_to(url_for(action: 'show'), alert: 'You do not have sufficient privileges to edit this document')
21
- elsif current_user && current_user.persisted?
22
- redirect_to root_url, alert: exception.message
23
- else
24
- session['user_return_to'] = request.url
25
- redirect_to new_user_session_url, alert: exception.message
26
- end
27
- end
18
+ rescue_from Hydra::AccessDenied, CanCan::AccessDenied, with: :deny_collection_access
28
19
 
29
20
  # actions: audit, index, create, new, edit, show, update, destroy, permissions, citation
30
21
  before_action :authenticate_user!, except: [:show, :index]
@@ -51,11 +42,22 @@ module CurationConcerns
51
42
  # To build a query to find a list of collections
52
43
  self.list_search_builder_class = CurationConcerns::CollectionSearchBuilder
53
44
  # The search builder to find the collection
54
- self.single_item_search_builder_class = CurationConcerns::WorkSearchBuilder
45
+ self.single_item_search_builder_class = CurationConcerns::SingleCollectionSearchBuilder
55
46
  # The search builder to find the collections' members
56
47
  self.member_search_builder_class = CurationConcerns::CollectionMemberSearchBuilder
57
48
  end
58
49
 
50
+ def deny_collection_access(exception)
51
+ if exception.action == :edit
52
+ redirect_to(url_for(action: 'show'), alert: 'You do not have sufficient privileges to edit this document')
53
+ elsif current_user && current_user.persisted?
54
+ redirect_to root_url, alert: exception.message
55
+ else
56
+ session['user_return_to'] = request.url
57
+ redirect_to new_user_session_url, alert: exception.message
58
+ end
59
+ end
60
+
59
61
  def index
60
62
  # run the solr query to find the collections
61
63
  query = list_search_builder.with(params).query
@@ -90,7 +92,6 @@ module CurationConcerns
90
92
 
91
93
  def after_create_error
92
94
  form
93
-
94
95
  respond_to do |format|
95
96
  format.html { render action: 'new' }
96
97
  format.json { render json: @collection.errors, status: :unprocessable_entity }
@@ -120,7 +121,6 @@ module CurationConcerns
120
121
  def after_update_error
121
122
  form
122
123
  query_collection_members
123
-
124
124
  respond_to do |format|
125
125
  format.html { render action: 'edit' }
126
126
  format.json { render json: @collection.errors, status: :unprocessable_entity }
@@ -15,11 +15,13 @@ module CurationConcerns
15
15
  self.work_form_service = WorkFormService
16
16
  attr_accessor :curation_concern
17
17
  helper_method :curation_concern, :contextual_path
18
+
19
+ rescue_from WorkflowAuthorizationException, with: :render_unavailable
18
20
  end
19
21
 
20
22
  module ClassMethods
21
23
  def curation_concern_type=(curation_concern_type)
22
- load_and_authorize_resource class: curation_concern_type, instance_name: :curation_concern, except: [:show, :file_manager]
24
+ load_and_authorize_resource class: curation_concern_type, instance_name: :curation_concern, except: [:show, :file_manager, :inspect_work]
23
25
  self._curation_concern_type = curation_concern_type
24
26
  end
25
27
 
@@ -105,6 +107,11 @@ module CurationConcerns
105
107
  presenter
106
108
  end
107
109
 
110
+ def inspect_work
111
+ raise Hydra::AccessDenied unless current_ability.admin?
112
+ presenter
113
+ end
114
+
108
115
  attr_writer :actor
109
116
 
110
117
  protected
@@ -193,10 +200,40 @@ module CurationConcerns
193
200
  search_result_document(params)
194
201
  end
195
202
 
203
+ # Only returns unsuppressed documents the user has read access to
196
204
  def search_result_document(search_params)
197
205
  _, document_list = search_results(search_params)
198
- raise CanCan::AccessDenied.new(nil, :show) if document_list.empty?
199
- document_list.first
206
+ return document_list.first unless document_list.empty?
207
+ document_not_found!
208
+ end
209
+
210
+ def document_not_found!
211
+ doc = SolrDocument.find(params[:id])
212
+ raise WorkflowAuthorizationException if doc.suppressed?
213
+ raise CanCan::AccessDenied.new(nil, :show)
214
+ end
215
+
216
+ def render_unavailable
217
+ message = I18n.t("curation_concerns.workflow.unauthorized")
218
+ respond_to do |wants|
219
+ wants.html do
220
+ flash[:notice] = message
221
+ render 'unavailable', status: :unauthorized
222
+ end
223
+ wants.json do
224
+ render plain: message, status: :unauthorized
225
+ end
226
+ additional_response_formats(wants)
227
+ wants.ttl do
228
+ render plain: message, status: :unauthorized
229
+ end
230
+ wants.jsonld do
231
+ render plain: message, status: :unauthorized
232
+ end
233
+ wants.nt do
234
+ render plain: message, status: :unauthorized
235
+ end
236
+ end
200
237
  end
201
238
  end
202
239
  end
@@ -9,6 +9,8 @@ module CurationConcerns::SelectsCollections
9
9
  end
10
10
  end
11
11
 
12
+ # @return [Hash{Symbol => Array[Symbol]}] bottom-up map of "what you need" to "what qualifies"
13
+ # @note i.e., requiring :read access is satisfied by either :read or :edit access
12
14
  def access_levels
13
15
  { read: [:read, :edit], edit: [:edit] }
14
16
  end
@@ -8,13 +8,15 @@ module CurationConcerns
8
8
  before_action :authenticate_user!
9
9
  before_action :authorize_user!
10
10
  # Catch permission errors
11
- rescue_from Hydra::AccessDenied, CanCan::AccessDenied do |exception|
12
- if current_user && current_user.persisted?
13
- redirect_to main_app.root_url, alert: "You do not have sufficient privileges to create links to this document"
14
- else
15
- session["user_return_to"] = request.url
16
- redirect_to new_user_session_url, alert: exception.message
17
- end
11
+ rescue_from Hydra::AccessDenied, CanCan::AccessDenied, with: :deny_link_access
12
+ end
13
+
14
+ def deny_link_access(exception)
15
+ if current_user && current_user.persisted?
16
+ redirect_to main_app.root_url, alert: "You do not have sufficient privileges to create links to this document"
17
+ else
18
+ session["user_return_to"] = request.url
19
+ redirect_to new_user_session_url, alert: exception.message
18
20
  end
19
21
  end
20
22
 
@@ -56,6 +56,10 @@ module CurationConcerns
56
56
  hydra_model == ::Collection
57
57
  end
58
58
 
59
+ def suppressed?
60
+ first(Solrizer.solr_name('suppressed', stored_boolean_field))
61
+ end
62
+
59
63
  # Method to return the ActiveFedora model
60
64
  def hydra_model
61
65
  first(Solrizer.solr_name('has_model', :symbol)).constantize
@@ -181,5 +185,9 @@ module CurationConcerns
181
185
  Rails.logger.info "Unable to parse date: #{field.first.inspect} for #{self['id']}"
182
186
  end
183
187
  end
188
+
189
+ def stored_boolean_field
190
+ Solrizer::Descriptor.new(:boolean, :stored, :indexed)
191
+ end
184
192
  end
185
193
  end
@@ -0,0 +1,31 @@
1
+ module CurationConcerns
2
+ # A work should be able to be filtered out of search results if it's inactive
3
+ module Suppressible
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # This holds the workflow state
8
+ property :state, predicate: Vocab::FedoraResourceStatus.objState, multiple: false
9
+ end
10
+
11
+ ##
12
+ # Used to restrict visibility on search results for a work that is inactive. If the state is not set, the
13
+ # default behavior is to consider the work not to be suppressed.
14
+ #
15
+ # Override this method if you have some criteria by which records should not display in the search results.
16
+ def suppressed?
17
+ return false if state.nil?
18
+ # This is a clumsy check only needed under RDF < 2.0 where there
19
+ # is a bug with `AT::Resource#==`
20
+ if RDF::VERSION.to_s < '2.0'
21
+ return state.rdf_subject == Vocab::FedoraResourceStatus.inactive
22
+ end
23
+ state == Vocab::FedoraResourceStatus.inactive
24
+ end
25
+
26
+ def to_sipity_entity
27
+ raise "Can't create an entity until the model has been persisted" unless persisted?
28
+ @sipity_entity ||= Sipity::Entity.find_by(proxy_for_global_id: to_global_id.to_s)
29
+ end
30
+ end
31
+ end
@@ -16,7 +16,7 @@ module CurationConcerns::WorkBehavior
16
16
  include Hydra::AccessControls::Embargoable
17
17
  include GlobalID::Identification
18
18
  include CurationConcerns::NestedWorks
19
- include CurationConcerns::Publishable
19
+ include CurationConcerns::Suppressible
20
20
 
21
21
  included do
22
22
  property :owner, predicate: RDF::URI.new('http://opaquenamespace.org/ns/hydra/owner'), multiple: false
@@ -0,0 +1,52 @@
1
+ module CurationConcerns
2
+ class InspectWorkPresenter
3
+ def initialize(solr_document, current_ability)
4
+ @solr_document = solr_document
5
+ @current_ability = current_ability
6
+ end
7
+ attr_reader :solr_document, :current_ability
8
+
9
+ def workflow
10
+ return { comments: [], roles: [] } unless sipity_entity
11
+ {
12
+ entity_id: sipity_entity.id,
13
+ proxy_for: sipity_entity.proxy_for_global_id,
14
+ workflow_id: sipity_entity.workflow_id,
15
+ workflow_name: sipity_entity.workflow_name,
16
+ state_id: sipity_entity.workflow_state_id,
17
+ state_name: sipity_entity.workflow_state_name,
18
+ comments: workflow_comments,
19
+ roles: sipity_entity_roles
20
+ }
21
+ end
22
+
23
+ def solr
24
+ @solr_document.inspect
25
+ end
26
+
27
+ private
28
+
29
+ def sipity_entity
30
+ @sipity_entity ||= PowerConverter.convert(solr_document, to: :sipity_entity)
31
+ end
32
+
33
+ def sipity_entity_roles
34
+ roles = CurationConcerns::Workflow::PermissionQuery.scope_roles_associated_with_the_given_entity(entity: solr_document)
35
+ roles.map do |role|
36
+ { id: role.id, name: role.name, description: role.description, users: sipity_entity_role_users(role) }
37
+ end
38
+ end
39
+
40
+ def sipity_entity_role_users(role)
41
+ users = CurationConcerns::Workflow::PermissionQuery.scope_users_for_entity_and_roles(entity: sipity_entity, roles: role)
42
+ users.map(&:user_key)
43
+ end
44
+
45
+ def workflow_comments
46
+ return [] unless sipity_entity && sipity_entity.comments.count > 0
47
+ sipity_entity.comments.map do |comment|
48
+ { comment: comment.comment, created_at: comment.created_at }
49
+ end
50
+ end
51
+ end
52
+ end
@@ -50,6 +50,10 @@ module CurationConcerns
50
50
  @workflow ||= WorkflowPresenter.new(solr_document, current_ability)
51
51
  end
52
52
 
53
+ def inspect_work
54
+ @inspect_workflow ||= InspectWorkPresenter.new(solr_document, current_ability)
55
+ end
56
+
53
57
  # @return FileSetPresenter presenter for the representative FileSets
54
58
  def representative_presenter
55
59
  return nil if representative_id.blank?
@@ -0,0 +1,69 @@
1
+ # Search Builders
2
+
3
+ Building searches is core to any Blacklight app, and CurationConcerns is no exception.
4
+ This directory contains our Search Builders, so named because the design followed a builder pattern, meaning
5
+ that when invoked to set values, methods return the object itself, so that invocations can be chained, like:
6
+
7
+ ```ruby
8
+ builder = Blacklight::SearchBuilder.new(processor_chain, scope)
9
+ .rows(20)
10
+ .page(3)
11
+ .with(q: 'Abraham Lincoln')
12
+ ```
13
+
14
+ However, at this level, many if not most of the additional methods do not follow this pattern.
15
+ Refer to the `Blacklight::SearchBuilder` class if you want to be certain. That leads to the next topic...
16
+
17
+ ## Ancestry
18
+
19
+ Most of the SearchBuilders have `::SearchBuilder` as a parent or ancestor. `::SearchBuilder` does not exist in any repo: it is generated
20
+ by Blacklight and modified by CurationConcerns. Others descend from `Blacklight::SearchBuilder`, or various other relatives.
21
+
22
+ ### ::SearchBuilder
23
+
24
+ The generated parent class `SearchBuilder` descends from `Blacklight::SearchBuilder`.
25
+ As modified by CurationConcerns' installer, it includes additional modules and overrides. So if your SearchBuilder has `::SearchBuilder` as a parent class, you are getting:
26
+ - [Blacklight::SearchBuilder](https://github.com/projectblacklight/blacklight/blob/master/lib/blacklight/search_builder.rb) grandparent class
27
+ - [Blacklight::Solr::SearchBuilderBehavior](https://github.com/projectblacklight/blacklight/blob/master/lib/blacklight/solr/search_builder_behavior.rb) associated methods
28
+ - [Hydra::AccessControlsEnforcement](https://github.com/projecthydra/hydra-head/blob/master/hydra-access-controls/lib/hydra/access_controls_enforcement.rb) module
29
+ - [Blacklight::AccessControls::Enforcement](https://github.com/projectblacklight/blacklight-access_controls/blob/master/lib/blacklight/access_controls/enforcement.rb) ancestor of `Hydra::AccessControlsEnforcement`
30
+ - [CurationConcerns::SearchFilters](https://github.com/projecthydra/curation_concerns/blob/master/app/search_builders/curation_concerns/search_filters.rb) module that itself includes:
31
+ - [BlacklightAdvancedSearch::AdvancedSearchBuilder](https://github.com/projectblacklight/blacklight_advanced_search/blob/master/lib/blacklight_advanced_search/advanced_search_builder.rb) more magic for compound Boolean queries
32
+ - [CurationConcerns::FilterByType](https://github.com/projecthydra/curation_concerns/blob/master/app/search_builders/curation_concerns/filter_by_type.rb) Collection vs. Work filtering, specifically the `filter_models` method
33
+
34
+ This is not a comprehensive list, but it is sufficient to trace some of the complexity of interaction between various layers.
35
+
36
+ ## Development: AKA Doing Something Useful
37
+
38
+ Note, the `default_processor_chain` defined by `Blacklight::Solr::SearchBuilderBehavior` provides a way to extend functionality, but also many possible points of override (namely method names). When you need to do something novel and additional, adding to the chain is completely reasonable. For example:
39
+
40
+ ```ruby
41
+ module MySearchBuilder
42
+ extend ActiveSupport::Concern
43
+
44
+ included do
45
+ self.default_processor_chain += [:special_filter]
46
+ end
47
+
48
+ def special_filter(solr_parameters)
49
+ solr_parameters[:fq] << "{!field f=some_field_ssim}#{...}"
50
+ end
51
+ end
52
+ ```
53
+
54
+ But to the extent that you are overriding (or undoing) something already done, `CurationConcerns::FileSetSearchBuilder` is a better example:
55
+
56
+ ```ruby
57
+ module CurationConcerns
58
+ class FileSetSearchBuilder < ::SearchBuilder
59
+ include CurationConcerns::SingleResult
60
+
61
+ # This overrides the models in FilterByType
62
+ def models
63
+ [::FileSet]
64
+ end
65
+ end
66
+ end
67
+ ```
68
+
69
+ There is no point having the other `filter_models` methods apply `:fq`s that we then try to undo or overwrite. In general, directly overwriting the whole `default_processor_chain` or solr parameters like `:fq` is less flexible than appending constraints sufficient for your use case. In particular, you might find that you have overwritten components that implement access controls, thereby making your SearchBuilder less useful and less secure. When in doubt, examine the actual solr queries produced.
@@ -1,35 +1,53 @@
1
1
  module CurationConcerns
2
+ # Our parent class is the generated SearchBuilder descending from Blacklight::SearchBuilder
3
+ # It includes Blacklight::Solr::SearchBuilderBehavior, Hydra::AccessControlsEnforcement, CurationConcerns::SearchFilters
4
+ # @see https://github.com/projectblacklight/blacklight/blob/master/lib/blacklight/search_builder.rb Blacklight::SearchBuilder parent
5
+ # @see https://github.com/projectblacklight/blacklight/blob/master/lib/blacklight/solr/search_builder_behavior.rb Blacklight::Solr::SearchBuilderBehavior
6
+ # @see https://github.com/projecthydra/curation_concerns/blob/master/app/search_builders/curation_concerns/README.md SearchBuilders README
7
+ # @note the default_processor_chain defined by Blacklight::Solr::SearchBuilderBehavior provides many possible points of override
8
+ #
2
9
  class CollectionSearchBuilder < ::SearchBuilder
3
10
  include FilterByType
4
11
  # Defines which search_params_logic should be used when searching for Collections
5
- self.default_processor_chain = [:default_solr_parameters, :add_query_to_solr,
6
- :add_access_controls_to_solr_params, :filter_models,
7
- :some_rows, :sort_by_title]
8
-
9
- def some_rows(solr_parameters)
10
- solr_parameters[:rows] = '100'
12
+ def initialize(context)
13
+ @rows = 100
14
+ super(context)
11
15
  end
12
16
 
13
- # This overrides FilterByType and ensures we only match on collections.
14
- def only_collections?
15
- true
17
+ # @return [String] Solr field name indicating default sort order
18
+ def sort_field
19
+ Solrizer.solr_name('title', :sortable)
16
20
  end
17
21
 
18
- # Sort results by title if no query was supplied.
19
- # This overrides the default 'relevance' sort.
20
- def sort_by_title(solr_parameters)
21
- return if solr_parameters[:q]
22
- solr_parameters[:sort] ||= "#{sort_field} asc"
22
+ # @return [Hash{Symbol => Array[Symbol]}] bottom-up map of "what you need" to "what qualifies"
23
+ # @note i.e., requiring :read access is satisfied by either :read or :edit access
24
+ def access_levels
25
+ { read: [:read, :edit], edit: [:edit] }
23
26
  end
24
27
 
25
- attr_writer :discovery_perms
28
+ attr_writer :discovery_perms # TODO: remove this line
29
+ ## Overrides
26
30
 
31
+ # unprotect lib/blacklight/access_controls/enforcement.rb methods
32
+ # Remove these when https://github.com/projectblacklight/blacklight-access_controls/pull/23 is merged/released/required
27
33
  def discovery_permissions
28
34
  @discovery_perms || super
29
35
  end
30
36
 
31
- def sort_field
32
- Solrizer.solr_name('title', :sortable)
37
+ def discovery_permissions=(*args)
38
+ super
39
+ end
40
+
41
+ # This overrides the models in FilterByType
42
+ def models
43
+ collection_classes
44
+ end
45
+
46
+ # Sort results by title if no query was supplied.
47
+ # This overrides the default 'relevance' sort.
48
+ def add_sorting_to_solr(solr_parameters)
49
+ return if solr_parameters[:q]
50
+ solr_parameters[:sort] ||= "#{sort_field} asc"
33
51
  end
34
52
  end
35
53
  end
@@ -0,0 +1,15 @@
1
+ module CurationConcerns
2
+ # Injects a search builder filter to hide documents marked as suppressed
3
+ module FilterSuppressed
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ self.default_processor_chain += [:only_active_works]
8
+ end
9
+
10
+ def only_active_works(solr_parameters)
11
+ solr_parameters[:fq] ||= []
12
+ solr_parameters[:fq] << '-suppressed_bsi:true'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ module CurationConcerns
2
+ # Overrides FilterSuppressed filter to hide documents marked as
3
+ # suppressed when the current user is permitted to take no workflow
4
+ # actions for the work's current state
5
+ #
6
+ # Assumes presence of `blacklight_params[:id]` and a SolrDocument
7
+ # corresponding to that `:id` value
8
+ module FilterSuppressedWithRoles
9
+ extend ActiveSupport::Concern
10
+ include CurationConcerns::FilterSuppressed
11
+
12
+ # Skip the filter if the current user is permitted to take
13
+ # workflow actions on the work corresponding to the SolrDocument
14
+ # with id = `blacklight_params[:id]`
15
+ def only_active_works(solr_parameters)
16
+ return if user_has_active_workflow_role?
17
+ super
18
+ end
19
+
20
+ private
21
+
22
+ def current_work
23
+ SolrDocument.find(blacklight_params[:id])
24
+ end
25
+
26
+ def user_has_active_workflow_role?
27
+ CurationConcerns::Workflow::PermissionQuery.scope_permitted_workflow_actions_available_for_current_state(user: current_ability.current_user, entity: current_work).any?
28
+ end
29
+ end
30
+ end
@@ -1,6 +1,7 @@
1
1
  module CurationConcerns::SearchFilters
2
2
  extend ActiveSupport::Concern
3
3
  include CurationConcerns::FilterByType
4
+ include CurationConcerns::FilterSuppressed
4
5
 
5
6
  # Override Hydra::AccessControlsEnforcement (or Hydra::PolicyAwareAccessControlsEnforcement)
6
7
  # Allows admin users to see everything (don't apply any gated_discovery_filters for those users)
@@ -0,0 +1,5 @@
1
+ module CurationConcerns
2
+ class SingleCollectionSearchBuilder < ::SearchBuilder
3
+ include CurationConcerns::SingleResult
4
+ end
5
+ end
@@ -1,5 +1,6 @@
1
1
  module CurationConcerns
2
2
  class WorkSearchBuilder < ::SearchBuilder
3
3
  include CurationConcerns::SingleResult
4
+ include CurationConcerns::FilterSuppressedWithRoles
4
5
  end
5
6
  end
@@ -14,7 +14,6 @@ module CurationConcerns
14
14
  AttachFilesActor,
15
15
  ApplyOrderActor,
16
16
  InterpretVisibilityActor,
17
- GrantEditToDepositorActor,
18
17
  model_actor(curation_concern),
19
18
  InitializeWorkflowActor]
20
19
  end
@@ -30,19 +30,6 @@ module CurationConcerns
30
30
  def presenter_class
31
31
  CurationConcerns::EmbargoPresenter
32
32
  end
33
-
34
- def presenters(builder)
35
- response = repository.search(builder)
36
- response.documents.map { |d| presenter_class.new(d) }
37
- end
38
-
39
- def repository
40
- config.repository
41
- end
42
-
43
- def config
44
- @config ||= ::CatalogController.new
45
- end
46
33
  end
47
34
  end
48
35
  end
@@ -2,46 +2,56 @@ module CurationConcerns
2
2
  module Workflow
3
3
  # Responsible for performing additional functions when the given criteria is met.
4
4
  class ActionTakenService
5
- # For the given :entity and :action
5
+ # For the given target and :action
6
6
  # - Find the appropriate "function" to call
7
- # - Then call that function. If the function returns a truthy value, then save the entity
8
- def self.handle_action_taken(entity:, action:, comment:, user:)
9
- new(entity: entity,
7
+ # - Then call that function. If the function returns a truthy value, then save the target
8
+ def self.handle_action_taken(target:, action:, comment:, user:)
9
+ new(target: target,
10
10
  action: action,
11
11
  comment: comment,
12
12
  user: user).call
13
13
  end
14
14
 
15
- def initialize(entity:, action:, comment:, user:)
16
- @entity = entity
15
+ def initialize(target:, action:, comment:, user:)
16
+ @target = target
17
17
  @action = action
18
18
  @comment = comment
19
19
  @user = user
20
20
  end
21
21
 
22
- attr_reader :action, :entity, :comment, :user
22
+ attr_reader :action, :target, :comment, :user
23
23
 
24
+ # Calls all the workflow methods for this action. Stops calling methods if any return falsy
25
+ # @return [Boolean] true if all methods returned a truthy result
24
26
  def call
25
27
  return unless action.triggered_methods.any?
26
28
  success = action.triggered_methods.order(:weight).all? do |method|
27
- process_action(method.service_name)
29
+ status = process_action(method.service_name)
30
+ Rails.logger.debug("Result of #{method.service_name} is #{status}")
31
+ status
28
32
  end
29
33
 
30
- return entity.proxy_for.save if success
31
- Rails.logger.error "Not all workflow methods were successful, so not saving (#{entity.id})"
34
+ return target.save if success
35
+ Rails.logger.error "Not all workflow methods were successful, so not saving (#{target.id})"
32
36
  false
33
37
  end
34
38
 
39
+ # @param service_name [String] the fully qualified class name to run the `call` method on
40
+ # @yieldparam status the result of calling the method
41
+ # @return the result of calling the method
35
42
  def process_action(service_name)
36
43
  service = resolve_service(service_name)
37
44
  return unless service
38
- service.call(entity: entity,
39
- comment: comment,
40
- user: user)
45
+ result = service.call(target: target,
46
+ comment: comment,
47
+ user: user)
48
+ yield(result) if block_given?
49
+ result
41
50
  end
42
51
 
43
- def resolve_service(service_name)
44
- class_name = service_name.classify
52
+ # @param class_name [String] the fully qualified class name to run
53
+ # @return [Class, NilClass] return nil if unable to locate the class
54
+ def resolve_service(class_name)
45
55
  klass = begin
46
56
  class_name.constantize
47
57
  rescue NameError
@@ -1,8 +1,15 @@
1
1
  module CurationConcerns
2
2
  module Workflow
3
3
  class ActivateObject
4
- def self.call(entity:, **)
5
- entity.proxy_for.state = Vocab::FedoraResourceStatus.active
4
+ ##
5
+ # This is a built in function for workflow, setting the `#state`
6
+ # of the target to the Fedora 'active' status URI
7
+ #
8
+ # @param target [#state] an instance of a model that includes `CurationConcerns::Suppressible`
9
+ #
10
+ # @return [RDF::Vocabulary::Term] the Fedora Resource Status 'active' term
11
+ def self.call(target:, **)
12
+ target.state = Vocab::FedoraResourceStatus.active
6
13
  end
7
14
  end
8
15
  end