hyrax 2.1.0.rc3 → 2.1.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -2
  3. data/app/assets/javascripts/hyrax/save_work/visibility_component.es6 +4 -4
  4. data/app/controllers/concerns/hyrax/controller.rb +15 -0
  5. data/app/controllers/concerns/hyrax/works_controller_behavior.rb +3 -1
  6. data/app/helpers/hyrax/hyrax_helper_behavior.rb +8 -0
  7. data/app/jobs/attach_files_to_work_job.rb +1 -1
  8. data/app/presenters/hyrax/admin_set_presenter.rb +1 -1
  9. data/app/presenters/hyrax/collection_presenter.rb +1 -1
  10. data/app/presenters/hyrax/file_set_presenter.rb +10 -6
  11. data/app/presenters/hyrax/member_presenter_factory.rb +10 -10
  12. data/app/presenters/hyrax/work_show_presenter.rb +43 -4
  13. data/app/renderers/hyrax/renderers/faceted_attribute_renderer.rb +1 -1
  14. data/app/renderers/hyrax/renderers/linked_attribute_renderer.rb +1 -1
  15. data/app/search_builders/hyrax/catalog_search_builder.rb +14 -1
  16. data/app/services/hyrax/collections/permissions_service.rb +13 -0
  17. data/app/views/hyrax/base/_items.html.erb +9 -1
  18. data/app/views/hyrax/dashboard/collections/_form_for_select_collection.html.erb +4 -3
  19. data/lib/generators/hyrax/templates/catalog_controller.rb +1 -1
  20. data/lib/generators/hyrax/templates/config/initializers/hyrax.rb +4 -0
  21. data/lib/hyrax/configuration.rb +5 -0
  22. data/lib/hyrax/version.rb +1 -1
  23. data/spec/features/catalog_search_spec.rb +41 -0
  24. data/spec/helpers/hyrax_helper_spec.rb +30 -0
  25. data/spec/lib/hyrax/configuration_spec.rb +1 -0
  26. data/spec/presenters/hyrax/admin_set_presenter_spec.rb +1 -1
  27. data/spec/presenters/hyrax/collection_presenter_spec.rb +1 -1
  28. data/spec/presenters/hyrax/work_show_presenter_spec.rb +79 -4
  29. data/spec/renderers/hyrax/renderers/faceted_attribute_renderer_spec.rb +3 -3
  30. data/spec/renderers/hyrax/renderers/linked_attribute_renderer_spec.rb +2 -2
  31. data/spec/search_builders/hyrax/catalog_search_builder_spec.rb +35 -0
  32. data/spec/services/hyrax/collections/permissions_service_spec.rb +14 -0
  33. data/spec/views/hyrax/base/_items.html.erb_spec.rb +18 -24
  34. data/spec/views/hyrax/my/collections/_list_collections.html.erb_spec.rb +1 -1
  35. data/template.rb +1 -1
  36. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 025cc22285626454040035c0cf14f5d89d818fa8
4
- data.tar.gz: 5669ec53c680bfe657ba9f1064102b43c5cd6efd
3
+ metadata.gz: bf48eeb6b1a64bc4d19de32c47c6c776387ec40d
4
+ data.tar.gz: f5ee75a59cd3f27fa135d4a623e47430594037d0
5
5
  SHA512:
6
- metadata.gz: a301edaa82e5823d68d31c7e4dd547848fbac537ae1e30ff6db7de2fc45897d7a2b3171560a1a8f62f242e921d259f5e0ab1a682ff4e7393ba9f446c17745b6e
7
- data.tar.gz: b034075731f730600bb4415a273e9ae2ed641615052275a91e6d15e52a77af64c15d55687a6922cab802e6cf64118fc365b0e00ab245aafbe0c5014611b2fc0c
6
+ metadata.gz: f7699b73766c7b747377e563f16049a02339cefb40cac5b2190795cb1045fce60a64f85d0c4571851fe36ca6ef7f53e16a566faca7711798627cbf15a9746f8e
7
+ data.tar.gz: b528b5192fc06718fecb1130b4d17b792a4e11b116a7a383091193632b62ccf0d29901004a599f2a4abbf7c9377ef98d8b6230bdc028569b8ccb73b2fe77ba48
data/README.md CHANGED
@@ -63,7 +63,7 @@ The Samvera community is here to help. Please see our [support guide](./.github/
63
63
  # Getting started
64
64
 
65
65
  This document contains instructions specific to setting up an app with __Hyrax
66
- v2.1.0.rc3__. If you are looking for instructions on installing a different
66
+ v2.1.0.rc4__. If you are looking for instructions on installing a different
67
67
  version, be sure to select the appropriate branch or tag from the drop-down
68
68
  menu above.
69
69
 
@@ -162,7 +162,7 @@ NOTE: The steps need to be done in order to create a new Hyrax based app.
162
162
  Generate a new Rails application using the template.
163
163
 
164
164
  ```
165
- rails _5.1.6_ new my_app -m https://raw.githubusercontent.com/samvera/hyrax/v2.1.0.rc3/template.rb
165
+ rails _5.1.6_ new my_app -m https://raw.githubusercontent.com/samvera/hyrax/v2.1.0.rc4/template.rb
166
166
  ```
167
167
 
168
168
  Generating a new Rails application using Hyrax's template above takes cares of a number of steps for you, including:
@@ -188,6 +188,13 @@ Notes:
188
188
  * This web server is purely for development purposes. You will want to use a more fully featured [web server](#web-server) for production-like environments.
189
189
  * You have the option to start each of these services individually. More information on [solr_wrapper](https://github.com/cbeer/solr_wrapper) and [fcrepo_wrapper](https://github.com/cbeer/fcrepo_wrapper) will help you set this up. Start rails with `rails s`.
190
190
 
191
+ ### Avoid Performance Issues with solr-suggest
192
+
193
+ *Recommended for all Hyrax apps.*
194
+
195
+ It is strongly suggested that you turn off solr-suggest. Details on why and how to do this are in
196
+ [Fix performance issue caused by solr-suggest](https://github.com/samvera/hyrax/wiki/Troubleshooting-Migration-from-2.0.x-to-2.1.0#fix-performance-issue-caused-by-solr-suggest-after-200-objects-are-in-the-repository).
197
+
191
198
  ## Start background workers
192
199
 
193
200
  Many of the services performed by Hyrax are resource intensive, and therefore are well suited to running as background jobs that can be managed and executed by a message queuing system. Examples include:
@@ -8,7 +8,7 @@ export default class VisibilityComponent {
8
8
  this.element = element
9
9
  this.adminSetWidget = adminSetWidget
10
10
  this.form = element.closest('form')
11
- $('.collapse').collapse({ toggle: false })
11
+ this.element.find('.collapse').collapse({ toggle: false })
12
12
  element.find("[type='radio']").on('change', () => { this.showForm() })
13
13
  // Ensure any disabled options are re-enabled when form submits
14
14
  this.form.on('submit', () => { this.enableAllOptions() })
@@ -22,7 +22,7 @@ export default class VisibilityComponent {
22
22
 
23
23
  // Collapse all Visibility sub-options
24
24
  collapseAll() {
25
- $('.collapse').collapse('hide');
25
+ this.element.find('.collapse').collapse('hide');
26
26
  }
27
27
 
28
28
  // Open the selected Visibility's sub-options, collapsing all others
@@ -33,8 +33,8 @@ export default class VisibilityComponent {
33
33
 
34
34
  if(target) {
35
35
  // Show the target suboption and hide all others
36
- $('.collapse' + target).collapse('show');
37
- $('.collapse:not(' + target + ')').collapse('hide');
36
+ this.element.find('.collapse' + target).collapse('show');
37
+ this.element.find('.collapse:not(' + target + ')').collapse('hide');
38
38
  }
39
39
  else {
40
40
  this.collapseAll()
@@ -6,6 +6,7 @@ module Hyrax::Controller
6
6
 
7
7
  # Adds Hydra behaviors into the application controller
8
8
  include Hydra::Controller::ControllerBehavior
9
+ helper_method :create_work_presenter
9
10
  before_action :set_locale
10
11
  end
11
12
 
@@ -14,6 +15,20 @@ module Hyrax::Controller
14
15
  hyrax.dashboard_path
15
16
  end
16
17
 
18
+ ##
19
+ # @deprecated this helper is no longer used by Hyrax; if you need access to
20
+ # this presenter on every page, you may need to readd it manually.
21
+ #
22
+ # A presenter for selecting a work type to create this is needed here because
23
+ # the selector is in the header on every page.
24
+ def create_work_presenter
25
+ Deprecation.warn(self, "The `create_work_presenter` helper is deprecated " \
26
+ "for removal in Hyrax 3.0. The work selector has " \
27
+ "been removed the masthead in Hyrax 2.1.")
28
+
29
+ Hyrax::SelectTypeListPresenter.new(current_user)
30
+ end
31
+
17
32
  # Ensure that the locale choice is persistent across requests
18
33
  def default_url_options
19
34
  super.merge(locale: I18n.locale)
@@ -192,7 +192,9 @@ module Hyrax
192
192
  end
193
193
 
194
194
  def curation_concern_from_search_results
195
- search_result_document(params)
195
+ search_params = params
196
+ search_params.delete :page
197
+ search_result_document(search_params)
196
198
  end
197
199
 
198
200
  # Only returns unsuppressed documents the user has read access to
@@ -257,6 +257,14 @@ module Hyrax
257
257
  content_tag(:span, "", class: [Hyrax::ModelIcon.css_class_for(Collection), "collection-icon-search"])
258
258
  end
259
259
 
260
+ def collection_title_by_id(id)
261
+ solr_docs = controller.repository.find(id).docs
262
+ return nil if solr_docs.empty?
263
+ solr_field = solr_docs.first[Solrizer.solr_name("title", :stored_searchable)]
264
+ return nil if solr_field.nil?
265
+ solr_field.first
266
+ end
267
+
260
268
  private
261
269
 
262
270
  def user_agent
@@ -12,10 +12,10 @@ class AttachFilesToWorkJob < Hyrax::ApplicationJob
12
12
  metadata = visibility_attributes(work_attributes)
13
13
  uploaded_files.each do |uploaded_file|
14
14
  actor = Hyrax::Actors::FileSetActor.new(FileSet.create, user)
15
+ actor.file_set.permissions_attributes = work_permissions
15
16
  actor.create_metadata(metadata)
16
17
  actor.create_content(uploaded_file)
17
18
  actor.attach_to_work(work)
18
- actor.file_set.permissions_attributes = work_permissions
19
19
  uploaded_file.update(file_set_uri: actor.file_set.uri)
20
20
  end
21
21
  end
@@ -24,7 +24,7 @@ module Hyrax
24
24
  end
25
25
 
26
26
  def show_path
27
- Hyrax::Engine.routes.url_helpers.admin_admin_set_path(id)
27
+ Hyrax::Engine.routes.url_helpers.admin_admin_set_path(id, locale: I18n.locale)
28
28
  end
29
29
 
30
30
  def available_parent_collections(*)
@@ -104,7 +104,7 @@ module Hyrax
104
104
  end
105
105
 
106
106
  def show_path
107
- Hyrax::Engine.routes.url_helpers.dashboard_collection_path(id)
107
+ Hyrax::Engine.routes.url_helpers.dashboard_collection_path(id, locale: I18n.locale)
108
108
  end
109
109
 
110
110
  def banner_file
@@ -82,12 +82,7 @@ module Hyrax
82
82
  end
83
83
 
84
84
  def parent
85
- ids = ActiveFedora::SolrService.query("{!field f=member_ids_ssim}#{id}",
86
- fl: ActiveFedora.id_field)
87
- .map { |x| x.fetch(ActiveFedora.id_field) }
88
- @parent_presenter ||= Hyrax::PresenterFactory.build_for(ids: ids,
89
- presenter_class: WorkShowPresenter,
90
- presenter_args: current_ability).first
85
+ @parent_presenter ||= fetch_parent_presenter
91
86
  end
92
87
 
93
88
  def user_can_perform_any_action?
@@ -99,5 +94,14 @@ module Hyrax
99
94
  def link_presenter_class
100
95
  SingleUseLinkPresenter
101
96
  end
97
+
98
+ def fetch_parent_presenter
99
+ ids = ActiveFedora::SolrService.query("{!field f=member_ids_ssim}#{id}",
100
+ fl: ActiveFedora.id_field)
101
+ .map { |x| x.fetch(ActiveFedora.id_field) }
102
+ Hyrax::PresenterFactory.build_for(ids: ids,
103
+ presenter_class: WorkShowPresenter,
104
+ presenter_args: current_ability).first
105
+ end
102
106
  end
103
107
  end
@@ -36,17 +36,17 @@ module Hyrax
36
36
  @work_presenters ||= member_presenters(ordered_ids - file_set_ids, work_presenter_class)
37
37
  end
38
38
 
39
- private
39
+ # TODO: Extract this to ActiveFedora::Aggregations::ListSource
40
+ def ordered_ids
41
+ @ordered_ids ||= begin
42
+ ActiveFedora::SolrService.query("proxy_in_ssi:#{id}",
43
+ rows: 10_000,
44
+ fl: "ordered_targets_ssim")
45
+ .flat_map { |x| x.fetch("ordered_targets_ssim", []) }
46
+ end
47
+ end
40
48
 
41
- # TODO: Extract this to ActiveFedora::Aggregations::ListSource
42
- def ordered_ids
43
- @ordered_ids ||= begin
44
- ActiveFedora::SolrService.query("proxy_in_ssi:#{id}",
45
- rows: 10_000,
46
- fl: "ordered_targets_ssim")
47
- .flat_map { |x| x.fetch("ordered_targets_ssim", []) }
48
- end
49
- end
49
+ private
50
50
 
51
51
  # These are the file sets that belong to this work, but not necessarily
52
52
  # in order.
@@ -69,7 +69,7 @@ module Hyrax
69
69
  return nil if representative_id.blank?
70
70
  @representative_presenter ||=
71
71
  begin
72
- result = member_presenters([representative_id]).first
72
+ result = member_presenters_for([representative_id]).first
73
73
  return nil if result.try(:id) == id
74
74
  if result.respond_to?(:representative_presenter)
75
75
  result.representative_presenter
@@ -153,10 +153,22 @@ module Hyrax
153
153
  solr_document.to_model
154
154
  end
155
155
 
156
- delegate :member_presenters, :file_set_presenters, :work_presenters, to: :member_presenter_factory
156
+ delegate :member_presenters, :ordered_ids, :file_set_presenters, :work_presenters, to: :member_presenter_factory
157
157
 
158
- def exclude_unauthorized_members
159
- member_presenters.delete_if { |m| !current_ability.can?(:read, m.id) }
158
+ # @return [Array] list to display with Kaminari pagination
159
+ def list_of_item_ids_to_display
160
+ paginated_item_list(page_array: authorized_item_ids)
161
+ end
162
+
163
+ # @param [Array<String>] ids a list of ids to build presenters for
164
+ # @return [Array<presenter_class>] presenters for the array of ids (not filtered by class)
165
+ def member_presenters_for(an_array_of_ids)
166
+ member_presenters(an_array_of_ids)
167
+ end
168
+
169
+ # @return [Integer] total number of pages of viewable items
170
+ def total_pages
171
+ (total_items.to_f / rows_from_params.to_f).ceil
160
172
  end
161
173
 
162
174
  def manifest_url
@@ -194,6 +206,33 @@ module Hyrax
194
206
 
195
207
  private
196
208
 
209
+ # list of item ids to display is based on ordered_ids
210
+ def authorized_item_ids
211
+ @member_item_list_ids ||= begin
212
+ items = ordered_ids
213
+ items.delete_if { |m| !current_ability.can?(:read, m) } if Flipflop.hide_private_items?
214
+ items
215
+ end
216
+ end
217
+
218
+ # Uses kaminari to paginate an array to avoid need for solr documents for items here
219
+ def paginated_item_list(page_array:)
220
+ Kaminari.paginate_array(page_array, total_count: page_array.size).page(current_page).per(rows_from_params)
221
+ end
222
+
223
+ def total_items
224
+ authorized_item_ids.size
225
+ end
226
+
227
+ def rows_from_params
228
+ request.params[:rows].nil? ? Hyrax.config.show_work_item_rows : request.params[:rows].to_i
229
+ end
230
+
231
+ def current_page
232
+ page = request.params[:page].nil? ? 1 : request.params[:page].to_i
233
+ page > total_pages ? total_pages : page
234
+ end
235
+
197
236
  def manifest_helper
198
237
  @manifest_helper ||= ManifestHelper.new(request.base_url)
199
238
  end
@@ -8,7 +8,7 @@ module Hyrax
8
8
  end
9
9
 
10
10
  def search_path(value)
11
- Rails.application.routes.url_helpers.search_catalog_path(:"f[#{search_field}][]" => value)
11
+ Rails.application.routes.url_helpers.search_catalog_path(:"f[#{search_field}][]" => value, locale: I18n.locale)
12
12
  end
13
13
 
14
14
  def search_field
@@ -9,7 +9,7 @@ module Hyrax
9
9
 
10
10
  def search_path(value)
11
11
  Rails.application.routes.url_helpers.search_catalog_path(
12
- search_field: search_field, q: ERB::Util.h(value)
12
+ search_field: search_field, q: ERB::Util.h(value), locale: I18n.locale
13
13
  )
14
14
  end
15
15
 
@@ -2,7 +2,8 @@ class Hyrax::CatalogSearchBuilder < Hyrax::SearchBuilder
2
2
  self.default_processor_chain += [
3
3
  :add_access_controls_to_solr_params,
4
4
  :show_works_or_works_that_contain_files,
5
- :show_only_active_records
5
+ :show_only_active_records,
6
+ :filter_collection_facet_for_access
6
7
  ]
7
8
 
8
9
  # show both works that match the query and works that contain files that match the query
@@ -19,6 +20,18 @@ class Hyrax::CatalogSearchBuilder < Hyrax::SearchBuilder
19
20
  solr_parameters[:fq] << '-suppressed_bsi:true'
20
21
  end
21
22
 
23
+ # only return facet counts for collections that this user has access to see
24
+ def filter_collection_facet_for_access(solr_parameters)
25
+ return if current_ability.admin?
26
+
27
+ collection_ids = Hyrax::Collections::PermissionsService.collection_ids_for_view(ability: current_ability).map { |id| "^#{id}$" }
28
+ solr_parameters['f.member_of_collection_ids_ssim.facet.matches'] = if collection_ids.present?
29
+ collection_ids.join('|')
30
+ else
31
+ "^$"
32
+ end
33
+ end
34
+
22
35
  private
23
36
 
24
37
  # the {!lucene} gives us the OR syntax
@@ -97,6 +97,19 @@ module Hyrax
97
97
  source_ids_for_deposit(ability: ability, source_type: 'collection')
98
98
  end
99
99
 
100
+ # @api public
101
+ #
102
+ # IDs of collections which the user can view.
103
+ #
104
+ # @param ability [Ability] the ability coming from cancan ability check
105
+ # @return [Array<String>] IDs of collections into which the user can view
106
+ # @note Several checks get the user's groups from the user's ability. The same values can be retrieved directly from a passed in ability.
107
+ def self.collection_ids_for_view(ability:)
108
+ collection_ids_for_user(ability: ability, access: [Hyrax::PermissionTemplateAccess::MANAGE,
109
+ Hyrax::PermissionTemplateAccess::DEPOSIT,
110
+ Hyrax::PermissionTemplateAccess::VIEW])
111
+ end
112
+
100
113
  # @api public
101
114
  #
102
115
  # Determine if the given user has permissions to view the admin show page for at least one collection
@@ -1,5 +1,6 @@
1
1
  <h2><%= t('.header') %></h2>
2
- <% members = Flipflop.hide_private_items? ? presenter.exclude_unauthorized_members : presenter.member_presenters %>
2
+ <% array_of_ids = presenter.list_of_item_ids_to_display %>
3
+ <% members = presenter.member_presenters_for(array_of_ids) %>
3
4
  <% if members.present? %>
4
5
  <table class="table table-striped related-files">
5
6
  <thead>
@@ -15,6 +16,13 @@
15
16
  <%= render partial: 'member', collection: members %>
16
17
  </tbody>
17
18
  </table>
19
+ <div class="row">
20
+ <% if presenter.total_pages > 1 %>
21
+ <div class="row record-padding col-md-9">
22
+ <%= paginate array_of_ids, outer_window: 2, theme: 'blacklight', param_name: :page, route_set: main_app %>
23
+ </div><!-- /pager -->
24
+ <% end %>
25
+ </div>
18
26
  <% elsif can? :edit, presenter.id %>
19
27
  <div class="alert alert-warning" role="alert"><%= t('.empty', type: presenter.human_readable_type) %></div>
20
28
  <% else %>
@@ -33,11 +33,12 @@
33
33
  <div class="modal-footer">
34
34
  <button type="button" class="btn btn-default" data-dismiss="modal"><%= t("hyrax.collection.select_form.close") %></button>
35
35
  <% if user_collections.blank? %>
36
- <%= render 'hyrax/dashboard/collections/button_create_collection', label: t("hyrax.collection.select_form.create") %>
37
-
36
+ <% # TODO: Uncomment the following line when errors with adding works to a new collection are resolved. See Issue hyrax#3088 %>
37
+ <% # = render 'hyrax/dashboard/collections/button_create_collection', label: t("hyrax.collection.select_form.create") %>
38
38
  <% else %>
39
39
  <%= render 'hyrax/dashboard/collections/button_for_update_collection', label: t("hyrax.collection.select_form.update"), collection_id: 'collection_replace_id' %>
40
- <%= render 'hyrax/dashboard/collections/button_create_collection', label: t("hyrax.collection.select_form.create_new") %>
40
+ <% # TODO: Uncomment the following line when errors with adding works to a new collection are resolved. See Issue hyrax#3088 %>
41
+ <% # = render 'hyrax/dashboard/collections/button_create_collection', label: t("hyrax.collection.select_form.create_new") %>
41
42
  <% end %>
42
43
  </div>
43
44
  </div><!-- /.modal-content -->
@@ -44,7 +44,7 @@ class CatalogController < ApplicationController
44
44
  config.add_facet_field solr_name("based_near_label", :facetable), limit: 5
45
45
  config.add_facet_field solr_name("publisher", :facetable), limit: 5
46
46
  config.add_facet_field solr_name("file_format", :facetable), limit: 5
47
- config.add_facet_field solr_name('member_of_collections', :symbol), limit: 5, label: 'Collections'
47
+ config.add_facet_field solr_name('member_of_collection_ids', :symbol), limit: 5, label: 'Collections', helper_method: :collection_title_by_id
48
48
 
49
49
  # The generic_type isn't displayed on the facet list
50
50
  # It's used to give a label to the filter that comes from the user profile
@@ -116,6 +116,10 @@ Hyrax.config do |config|
116
116
  # The default is true.
117
117
  # config.work_requires_files = true
118
118
 
119
+ # How many rows of items should appear on the work show view?
120
+ # The default is 10
121
+ # config.show_work_item_rows = 10
122
+
119
123
  # Enable IIIF image service. This is required to use the
120
124
  # UniversalViewer-ified show page
121
125
  #
@@ -340,6 +340,11 @@ module Hyrax
340
340
  @work_requires_files
341
341
  end
342
342
 
343
+ attr_writer :show_work_item_rows
344
+ def show_work_item_rows
345
+ @show_work_item_rows ||= 10 # rows on show view
346
+ end
347
+
343
348
  attr_writer :batch_user_key
344
349
  def batch_user_key
345
350
  @batch_user_key ||= 'batchuser@example.com'
@@ -1,3 +1,3 @@
1
1
  module Hyrax
2
- VERSION = '2.1.0.rc3'.freeze
2
+ VERSION = '2.1.0.rc4'.freeze
3
3
  end
@@ -28,4 +28,45 @@ RSpec.describe 'catalog searching', type: :feature do
28
28
  expect(page).to have_content(collection.title.first)
29
29
  end
30
30
  end
31
+
32
+ context 'with public works and private collections', clean_repo: true do
33
+ let!(:collection) { create(:private_collection) }
34
+
35
+ let!(:jills_work) do
36
+ create(:public_work, title: ["Jill's Research"], keyword: ['jills_keyword'], member_of_collections: [collection])
37
+ end
38
+
39
+ it "hides collection facet values the user doesn't have access to view when performing a search" do
40
+ within('#search-form-header') do
41
+ fill_in('search-field-header', with: 'jills_keyword')
42
+ click_button('Go')
43
+ end
44
+
45
+ expect(page).to have_content('Search Results')
46
+ expect(page).to have_content(jills_work.title.first)
47
+ expect(page).not_to have_content(collection.title.first)
48
+ expect(page).not_to have_css('.blacklight-member_of_collection_ids_ssim')
49
+ end
50
+
51
+ context 'as an admin' do
52
+ let(:admin_user) { create :admin }
53
+
54
+ before do
55
+ sign_in admin_user
56
+ visit '/'
57
+ end
58
+
59
+ it "shows collection facet values the user has access to view when performing a search" do
60
+ within('#search-form-header') do
61
+ fill_in('search-field-header', with: 'jills_keyword')
62
+ click_button('Go')
63
+ end
64
+
65
+ expect(page).to have_content('Search Results')
66
+ expect(page).to have_content(jills_work.title.first)
67
+ find('.blacklight-member_of_collection_ids_ssim').click
68
+ expect(page).to have_content(collection.title.first)
69
+ end
70
+ end
71
+ end
31
72
  end
@@ -366,4 +366,34 @@ RSpec.describe HyraxHelper, type: :helper do
366
366
  expect(helper.banner_image).to eq(Hyrax.config.banner_image)
367
367
  end
368
368
  end
369
+
370
+ describe "#collection_title_by_id" do
371
+ let(:solr_doc) { double(id: "abcd12345") }
372
+ let(:bad_solr_doc) { double(id: "efgh67890") }
373
+ let(:solr_response) { double(docs: [solr_doc]) }
374
+ let(:bad_solr_response) { double(docs: [bad_solr_doc]) }
375
+ let(:empty_solr_response) { double(docs: []) }
376
+ let(:repository) { double }
377
+
378
+ before do
379
+ allow(controller).to receive(:repository).and_return(repository)
380
+ allow(solr_doc).to receive(:[]).with("title_tesim").and_return(["Collection of Awesomeness"])
381
+ allow(bad_solr_doc).to receive(:[]).with("title_tesim").and_return(nil)
382
+ allow(repository).to receive(:find).with("abcd12345").and_return(solr_response)
383
+ allow(repository).to receive(:find).with("efgh67890").and_return(bad_solr_response)
384
+ allow(repository).to receive(:find).with("bad-id").and_return(empty_solr_response)
385
+ end
386
+
387
+ it "returns the first title of the collection" do
388
+ expect(helper.collection_title_by_id("abcd12345")).to eq "Collection of Awesomeness"
389
+ end
390
+
391
+ it "returns nil if collection doesn't have title_tesim field" do
392
+ expect(helper.collection_title_by_id("efgh67890")).to eq nil
393
+ end
394
+
395
+ it "returns nil if collection not found" do
396
+ expect(helper.collection_title_by_id("bad-id")).to eq nil
397
+ end
398
+ end
369
399
  end
@@ -72,6 +72,7 @@ RSpec.describe Hyrax::Configuration do
72
72
  it { is_expected.to respond_to(:rights_statement_service_class=) }
73
73
  it { is_expected.to respond_to(:redis_namespace) }
74
74
  it { is_expected.to respond_to(:subject_prefix) }
75
+ it { is_expected.to respond_to(:show_work_item_rows) }
75
76
  it { is_expected.to respond_to(:translate_id_to_uri) }
76
77
  it { is_expected.to respond_to(:translate_uri_to_id) }
77
78
  it { is_expected.to respond_to(:upload_path) }
@@ -68,7 +68,7 @@ RSpec.describe Hyrax::AdminSetPresenter do
68
68
 
69
69
  subject { presenter.show_path }
70
70
 
71
- it { is_expected.to eq "/admin/admin_sets/#{admin_set.id}" }
71
+ it { is_expected.to eq "/admin/admin_sets/#{admin_set.id}?locale=en" }
72
72
  end
73
73
 
74
74
  describe '#managed_access' do
@@ -357,7 +357,7 @@ RSpec.describe Hyrax::CollectionPresenter do
357
357
  describe '#show_path' do
358
358
  subject { presenter.show_path }
359
359
 
360
- it { is_expected.to eq "/dashboard/collections/#{solr_doc.id}" }
360
+ it { is_expected.to eq "/dashboard/collections/#{solr_doc.id}?locale=en" }
361
361
  end
362
362
 
363
363
  describe "banner_file" do
@@ -30,6 +30,8 @@ RSpec.describe Hyrax::WorkShowPresenter do
30
30
  it { is_expected.to delegate_method(:resource_type).to(:solr_document) }
31
31
  it { is_expected.to delegate_method(:keyword).to(:solr_document) }
32
32
  it { is_expected.to delegate_method(:itemtype).to(:solr_document) }
33
+ it { is_expected.to delegate_method(:member_presenters).to(:member_presenter_factory) }
34
+ it { is_expected.to delegate_method(:ordered_ids).to(:member_presenter_factory) }
33
35
 
34
36
  describe "#model_name" do
35
37
  subject { presenter.model_name }
@@ -224,13 +226,86 @@ RSpec.describe Hyrax::WorkShowPresenter do
224
226
  end
225
227
  end
226
228
 
227
- describe "exclude_unauthorized_members" do
229
+ describe "#member_presenters_for" do
228
230
  let(:obj) { create(:work_with_file_and_work) }
229
231
  let(:attributes) { obj.to_solr }
230
- let(:ability) { double Ability, can?: false }
232
+ let(:items) { presenter.ordered_ids }
233
+ let(:subject) { presenter.member_presenters_for(items) }
231
234
 
232
- it 'filters out unauthorized members' do
233
- expect(presenter.exclude_unauthorized_members.count).to eq 0
235
+ it "returns appropriate classes for each item" do
236
+ expect(subject.size).to eq 2
237
+ expect(subject.first).to be_instance_of(Hyrax::FileSetPresenter)
238
+ expect(subject.last).to be_instance_of(described_class)
239
+ end
240
+ end
241
+
242
+ describe "#list_of_item_ids_to_display" do
243
+ let(:subject) { presenter.list_of_item_ids_to_display }
244
+ let(:items_list) { ['item0', 'item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7', 'item8', 'item9'] }
245
+ let(:rows) { 10 }
246
+ let(:page) { 1 }
247
+ let(:ability) { double "Ability" }
248
+ let(:current_ability) { ability }
249
+
250
+ before do
251
+ allow(presenter).to receive(:ordered_ids).and_return(items_list)
252
+ allow(current_ability).to receive(:can?).with(:read, 'item0').and_return true
253
+ allow(current_ability).to receive(:can?).with(:read, 'item1').and_return false
254
+ allow(current_ability).to receive(:can?).with(:read, 'item2').and_return true
255
+ allow(current_ability).to receive(:can?).with(:read, 'item3').and_return false
256
+ allow(current_ability).to receive(:can?).with(:read, 'item4').and_return true
257
+ allow(current_ability).to receive(:can?).with(:read, 'item5').and_return true
258
+ allow(current_ability).to receive(:can?).with(:read, 'item6').and_return false
259
+ allow(current_ability).to receive(:can?).with(:read, 'item7').and_return true
260
+ allow(current_ability).to receive(:can?).with(:read, 'item8').and_return false
261
+ allow(current_ability).to receive(:can?).with(:read, 'item9').and_return true
262
+ allow(presenter).to receive(:rows_from_params).and_return(rows)
263
+ allow(presenter).to receive(:current_page).and_return(page)
264
+ allow(Flipflop).to receive(:hide_private_items?).and_return(answer)
265
+ end
266
+
267
+ context 'when hiding private items' do
268
+ let(:answer) { true }
269
+
270
+ it "returns viewable items" do
271
+ expect(subject.size).to eq 6
272
+ expect(subject).to be_instance_of(Kaminari::PaginatableArray)
273
+ expect(subject).to include("item0", "item2", "item4", "item5", "item7", "item9")
274
+ end
275
+ end
276
+ context 'when including private items' do
277
+ let(:answer) { false }
278
+
279
+ it "returns appropriate items" do
280
+ expect(subject.size).to eq 10
281
+ expect(subject).to be_instance_of(Kaminari::PaginatableArray)
282
+ expect(subject).to eq(items_list)
283
+ end
284
+ end
285
+ context 'with pagination' do
286
+ let(:rows) { 3 }
287
+ let(:page) { 2 }
288
+
289
+ let(:answer) { true }
290
+ it 'partitions the item list and excluding hidden items' do
291
+ expect(subject).to eq(['item5', 'item7', 'item9'])
292
+ end
293
+ end
294
+ end
295
+
296
+ describe "#total_pages" do
297
+ let(:subject) { presenter.total_pages }
298
+ let(:items) { 17 }
299
+ let(:rows) { 4 }
300
+
301
+ before do
302
+ allow(Flipflop).to receive(:hide_private_items?).and_return(false)
303
+ allow(presenter).to receive(:total_items).and_return(items)
304
+ allow(presenter).to receive(:rows_from_params).and_return(rows)
305
+ end
306
+
307
+ it 'calculates number of pages from items and rows' do
308
+ expect(subject).to eq(5)
234
309
  end
235
310
  end
236
311
 
@@ -11,8 +11,8 @@ RSpec.describe Hyrax::Renderers::FacetedAttributeRenderer do
11
11
  %(
12
12
  <tr><th>Name</th>
13
13
  <td><ul class='tabular'>
14
- <li class="attribute attribute-name"><a href="/catalog?f%5Bname_sim%5D%5B%5D=Bob">Bob</a></li>
15
- <li class="attribute attribute-name"><a href="/catalog?f%5Bname_sim%5D%5B%5D=Jessica">Jessica</a></li>
14
+ <li class="attribute attribute-name"><a href="/catalog?f%5Bname_sim%5D%5B%5D=Bob&locale=en">Bob</a></li>
15
+ <li class="attribute attribute-name"><a href="/catalog?f%5Bname_sim%5D%5B%5D=Jessica&locale=en">Jessica</a></li>
16
16
  </ul></td></tr>
17
17
  )
18
18
  end
@@ -28,7 +28,7 @@ RSpec.describe Hyrax::Renderers::FacetedAttributeRenderer do
28
28
  let(:rendered_link_query) { URI.parse(rendered_link['href']).query }
29
29
 
30
30
  it "escapes content properly" do
31
- expect(rendered_link_query).to eq "#{CGI.escape('f[name_sim][]')}=#{CGI.escape('John & Bob')}"
31
+ expect(rendered_link_query).to eq "#{CGI.escape('f[name_sim][]')}=#{CGI.escape('John & Bob')}&locale=en"
32
32
  end
33
33
  end
34
34
  end
@@ -10,8 +10,8 @@ RSpec.describe Hyrax::Renderers::LinkedAttributeRenderer do
10
10
  let(:tr_content) do
11
11
  "<tr><th>Name</th>\n" \
12
12
  "<td><ul class='tabular'>" \
13
- "<li class=\"attribute attribute-name\"><a href=\"/catalog?q=Bob&amp;search_field=name\">Bob</a></li>\n" \
14
- "<li class=\"attribute attribute-name\"><a href=\"/catalog?q=Jessica&amp;search_field=name\">Jessica</a></li>\n" \
13
+ "<li class=\"attribute attribute-name\"><a href=\"/catalog?locale=en&q=Bob&amp;search_field=name\">Bob</a></li>\n" \
14
+ "<li class=\"attribute attribute-name\"><a href=\"/catalog?locale=en&q=Jessica&amp;search_field=name\">Jessica</a></li>\n" \
15
15
  "</ul></td></tr>"
16
16
  end
17
17
 
@@ -48,4 +48,39 @@ RSpec.describe Hyrax::CatalogSearchBuilder do
48
48
  expect(solr_params[:fq]).to eq ["-suppressed_bsi:true"]
49
49
  end
50
50
  end
51
+
52
+ describe "#filter_collection_facet_for_access" do
53
+ let(:user) { build(:user) }
54
+ let(:ability) { Ability.new(user) }
55
+ let(:context) { double(current_ability: ability) }
56
+
57
+ subject { builder.filter_collection_facet_for_access(solr_params) }
58
+
59
+ context 'with an admin' do
60
+ let(:user) { build(:admin) }
61
+
62
+ it "does nothing if user is an admin" do
63
+ subject
64
+ expect(solr_params['f.member_of_collection_ids_ssim.facet.matches']).to be_blank
65
+ end
66
+ end
67
+
68
+ context 'when the user has view access to collections' do
69
+ let(:collection_ids) { ['abcd12345', 'efgh67890'] }
70
+
71
+ before do
72
+ allow(Hyrax::Collections::PermissionsService).to receive(:collection_ids_for_view).with(ability: ability).and_return(collection_ids)
73
+ end
74
+
75
+ it "includes a regex of the ids of collections" do
76
+ subject
77
+ expect(solr_params['f.member_of_collection_ids_ssim.facet.matches']).to eq '^abcd12345$|^efgh67890$'
78
+ end
79
+ end
80
+
81
+ it "includes an empty regex when user doesn't have access to view any collections" do
82
+ subject
83
+ expect(solr_params['f.member_of_collection_ids_ssim.facet.matches']).to eq '^$'
84
+ end
85
+ end
51
86
  end
@@ -299,6 +299,20 @@ RSpec.describe Hyrax::Collections::PermissionsService do
299
299
  end
300
300
  end
301
301
 
302
+ describe '.collection_ids_for_view' do
303
+ it 'returns collection ids where user has view access' do
304
+ expect(described_class.collection_ids_for_view(ability: ability)).to match_array [col_du.id, col_dg.id, col_mu.id, col_mg.id, col_vu.id, col_vg.id]
305
+ end
306
+
307
+ context 'when user has no access' do
308
+ let(:ability) { Ability.new(user2) }
309
+
310
+ it 'returns empty array' do
311
+ expect(described_class.collection_ids_for_view(ability: ability)).to match_array []
312
+ end
313
+ end
314
+ end
315
+
302
316
  describe '.can_manage_any_collection?' do
303
317
  it 'returns true when user has manage access to at least one collection' do
304
318
  expect(described_class.can_manage_any_collection?(ability: ability)).to be true
@@ -1,11 +1,17 @@
1
1
  RSpec.describe 'hyrax/base/_items.html.erb', type: :view do
2
2
  let(:ability) { double }
3
- let(:presenter) { double(:presenter, member_presenters: member_presenters, id: 'the-id', human_readable_type: 'Thing') }
3
+ let(:request) { double "request", params: params }
4
+ let(:params) { ActionController::Parameters.new(rows: 10) }
4
5
 
5
6
  context 'when children are not present' do
6
- let(:member_presenters) { [] }
7
+ let(:member_list) { [] }
8
+ let(:presenter) { double(:presenter, list_of_items_to_display: member_list, member_presenters_for: member_list, id: 'the-id', human_readable_type: 'Thing') }
7
9
 
8
- context 'and the current user edit the presenter' do
10
+ before do
11
+ expect(presenter).to receive(:list_of_item_ids_to_display).and_return(member_list)
12
+ end
13
+
14
+ context 'and the current user can edit the presenter' do
9
15
  it 'renders an alert' do
10
16
  expect(view).to receive(:can?).with(:edit, presenter.id).and_return(true)
11
17
  render 'hyrax/base/items', presenter: presenter
@@ -25,35 +31,23 @@ RSpec.describe 'hyrax/base/_items.html.erb', type: :view do
25
31
  let(:child1) { double('Thing1', id: 'Thing 1', title: 'Title 1') }
26
32
  let(:child2) { double('Thing2', id: 'Thing 2', title: 'Title 2') }
27
33
  let(:child3) { double('Thing3', id: 'Thing 3', title: 'Title 3') }
28
- let(:member_presenters) { [child1, child2, child3] }
29
- let(:authorized_presenters) { [child1, child3] }
34
+ let(:member_list) { [child1, child2, child3] }
30
35
  let(:solr_document) { double('Solr Doc', id: 'the-id') }
31
36
  let(:presenter) { Hyrax::WorkShowPresenter.new(solr_document, ability, request) }
32
37
 
33
38
  before do
34
39
  stub_template 'hyrax/base/_member.html.erb' => '<%= member %>'
35
40
  expect(Flipflop).to receive(:hide_private_items?).and_return(:flipflop)
36
- expect(presenter).to receive(:member_presenters).and_return(member_presenters)
37
- expect(ability).to receive(:can?).with(:read, child1.id).and_return true
38
- expect(ability).to receive(:can?).with(:read, child2.id).and_return false
39
- expect(ability).to receive(:can?).with(:read, child3.id).and_return true
40
- end
41
-
42
- context 'and hide_private_items is on' do
43
- let(:flip_flop) { true }
44
-
45
- it "displays only authorized children" do
46
- render 'hyrax/base/items', presenter: presenter
47
- expect(rendered).to have_css('tbody', text: authorized_presenters.join)
48
- end
41
+ allow(presenter).to receive(:list_of_item_ids_to_display).and_return(member_list)
42
+ allow(presenter).to receive(:member_presenters_for).with(member_list).and_return(member_list)
43
+ allow(ability).to receive(:can?).with(:read, child1.id).and_return true
44
+ allow(ability).to receive(:can?).with(:read, child2.id).and_return false
45
+ allow(ability).to receive(:can?).with(:read, child3.id).and_return true
49
46
  end
50
- context 'and hide_private_items is off' do
51
- let(:flip_flop) { false }
52
47
 
53
- it "displays all children" do
54
- render 'hyrax/base/items', presenter: presenter
55
- expect(rendered).to have_css('tbody', text: member_presenters.join)
56
- end
48
+ it "displays children" do
49
+ render 'hyrax/base/items', presenter: presenter
50
+ expect(rendered).to have_css('tbody')
57
51
  end
58
52
  end
59
53
  end
@@ -45,7 +45,7 @@ RSpec.describe 'hyrax/my/collections/_list_collections.html.erb', type: :view do
45
45
  expect(rendered).to have_selector("tr#document_#{id}")
46
46
  check_tr_data_attributes
47
47
  expect(rendered).to have_selector("tr[data-post-delete-url='/dashboard/collections/#{id}']")
48
- expect(rendered).to have_link 'Collection Title', href: hyrax.dashboard_collection_path(id)
48
+ expect(rendered).to have_link 'Collection Title', href: hyrax.dashboard_collection_path(id, locale: I18n.locale)
49
49
  expect(rendered).to have_link 'Edit collection', href: hyrax.edit_dashboard_collection_path(id)
50
50
  expect(rendered).to have_link 'Delete collection'
51
51
  expect(rendered).to have_link 'Add to collection'
@@ -1,4 +1,4 @@
1
- gem 'hyrax', '2.1.0.rc3'
1
+ gem 'hyrax', '2.1.0.rc4'
2
2
  run 'bundle install'
3
3
  generate 'hyrax:install', '-f'
4
4
  rails_command 'db:migrate'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyrax
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0.rc3
4
+ version: 2.1.0.rc4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Coyne
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2018-05-15 00:00:00.000000000 Z
17
+ date: 2018-05-30 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: rails