curation_concerns 0.12.0.pre1 → 0.12.0.pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +24 -16
  3. data/Gemfile +0 -4
  4. data/README.md +14 -0
  5. data/RELEASING.md +2 -2
  6. data/Rakefile +2 -0
  7. data/app/actors/concerns/curation_concerns/manages_embargoes_actor.rb +28 -0
  8. data/app/actors/curation_concerns/abstract_actor.rb +28 -0
  9. data/app/actors/curation_concerns/add_to_collection_actor.rb +38 -0
  10. data/app/actors/curation_concerns/apply_order_actor.rb +24 -0
  11. data/app/actors/curation_concerns/assign_identifier_actor.rb +7 -0
  12. data/app/actors/curation_concerns/assign_representative_actor.rb +18 -0
  13. data/app/actors/curation_concerns/attach_files_actor.rb +39 -0
  14. data/app/actors/curation_concerns/base_actor.rb +71 -0
  15. data/app/actors/curation_concerns/embargo_actor.rb +19 -0
  16. data/app/actors/curation_concerns/file_actor.rb +79 -0
  17. data/app/actors/curation_concerns/file_set_actor.rb +146 -0
  18. data/app/actors/curation_concerns/interpret_visibility_actor.rb +123 -0
  19. data/app/actors/curation_concerns/lease_actor.rb +19 -0
  20. data/app/actors/curation_concerns/root_actor.rb +17 -0
  21. data/app/actors/curation_concerns/work_actor_behavior.rb +8 -0
  22. data/app/assets/javascripts/curation_concerns/batch_select.js +42 -0
  23. data/app/assets/javascripts/curation_concerns/collections.js +13 -0
  24. data/app/assets/javascripts/curation_concerns/curation_concerns.js +2 -0
  25. data/app/assets/stylesheets/curation_concerns/_curation_concerns.scss +0 -3
  26. data/app/assets/stylesheets/curation_concerns/_modules.scss +1 -1
  27. data/app/assets/stylesheets/curation_concerns/_positioning.scss +3 -6
  28. data/app/assets/stylesheets/curation_concerns/_theme.scss +0 -39
  29. data/app/assets/stylesheets/curation_concerns/_typography.scss +0 -69
  30. data/app/assets/stylesheets/curation_concerns/modules/classify_work.scss +0 -2
  31. data/app/assets/stylesheets/curation_concerns/modules/collections.scss +4 -0
  32. data/app/assets/stylesheets/curation_concerns/modules/forms.scss +0 -4
  33. data/app/assets/stylesheets/curation_concerns/modules/site_actions.scss +34 -29
  34. data/app/assets/stylesheets/curation_concerns/modules/site_search.scss +0 -46
  35. data/app/assets/stylesheets/curation_concerns.scss +4 -0
  36. data/app/controllers/concerns/curation_concerns/collections_controller_behavior.rb +166 -21
  37. data/app/controllers/concerns/curation_concerns/embargoes_controller_behavior.rb +1 -1
  38. data/app/controllers/concerns/curation_concerns/leases_controller_behavior.rb +1 -1
  39. data/app/controllers/concerns/curation_concerns/selects_collections.rb +65 -0
  40. data/app/forms/curation_concerns/forms/collection_edit_form.rb +0 -29
  41. data/app/forms/curation_concerns/forms/work_form.rb +2 -1
  42. data/app/helpers/batch_select_helper.rb +23 -0
  43. data/app/helpers/collections_helper.rb +4 -0
  44. data/app/helpers/curation_concerns/collections_helper.rb +2 -2
  45. data/app/helpers/curation_concerns/collections_helper_behavior.rb +56 -0
  46. data/app/helpers/curation_concerns/render_constraints_helper.rb +14 -35
  47. data/app/helpers/curation_concerns/title_helper.rb +4 -0
  48. data/app/indexers/curation_concerns/collection_indexer.rb +16 -0
  49. data/app/indexers/curation_concerns/file_set_indexer.rb +46 -0
  50. data/app/indexers/curation_concerns/work_indexer.rb +15 -0
  51. data/app/jobs/audit_job.rb +49 -0
  52. data/app/jobs/characterize_job.rb +11 -0
  53. data/app/jobs/create_derivatives_job.rb +21 -0
  54. data/app/jobs/import_url_job.rb +48 -0
  55. data/app/jobs/ingest_file_job.rb +30 -0
  56. data/app/jobs/ingest_local_file_job.rb +20 -0
  57. data/app/jobs/resolrize_job.rb +7 -0
  58. data/app/models/checksum_audit_log.rb +20 -0
  59. data/app/models/collection.rb +6 -0
  60. data/app/models/concerns/curation_concerns/ability.rb +49 -0
  61. data/app/models/concerns/curation_concerns/basic_metadata.rb +64 -0
  62. data/app/models/concerns/curation_concerns/collection.rb +16 -0
  63. data/app/models/concerns/curation_concerns/collection_behavior.rb +62 -0
  64. data/app/models/concerns/curation_concerns/file_set/belongs_to_works.rb +47 -0
  65. data/app/models/concerns/curation_concerns/file_set/derivatives.rb +65 -0
  66. data/app/models/concerns/curation_concerns/file_set/full_text_indexing.rb +11 -0
  67. data/app/models/concerns/curation_concerns/file_set/indexing.rb +14 -0
  68. data/app/models/concerns/curation_concerns/file_set/querying.rb +17 -0
  69. data/app/models/concerns/curation_concerns/file_set_behavior.rb +36 -0
  70. data/app/models/concerns/curation_concerns/has_representative.rb +13 -0
  71. data/app/models/concerns/curation_concerns/human_readable_type.rb +17 -0
  72. data/app/models/concerns/curation_concerns/naming.rb +17 -0
  73. data/app/models/concerns/curation_concerns/permissions/readable.rb +18 -0
  74. data/app/models/concerns/curation_concerns/permissions/writable.rb +34 -0
  75. data/app/models/concerns/curation_concerns/permissions.rb +7 -0
  76. data/app/models/concerns/curation_concerns/required_metadata.rb +30 -0
  77. data/app/models/concerns/curation_concerns/serializers.rb +13 -0
  78. data/app/models/concerns/curation_concerns/solr_document_behavior.rb +147 -0
  79. data/app/models/concerns/curation_concerns/user.rb +18 -0
  80. data/app/models/concerns/curation_concerns/with_file_sets.rb +37 -0
  81. data/app/models/concerns/curation_concerns/work_behavior.rb +45 -0
  82. data/app/models/curation_concerns/classify_concern.rb +49 -0
  83. data/app/models/curation_concerns/quick_classification_query.rb +38 -0
  84. data/app/models/single_use_link.rb +34 -0
  85. data/app/models/version_committer.rb +2 -0
  86. data/app/search_builders/curation_concerns/collection_member_search_builder.rb +1 -1
  87. data/app/search_builders/curation_concerns/collection_search_builder.rb +33 -0
  88. data/app/search_builders/curation_concerns/member_search_builder.rb +17 -0
  89. data/app/services/curation_concerns/derivative_path.rb +49 -0
  90. data/app/services/curation_concerns/file_set_audit_service.rb +105 -0
  91. data/app/services/curation_concerns/indexes_thumbnails.rb +30 -0
  92. data/app/services/curation_concerns/local_file_service.rb +10 -0
  93. data/app/services/curation_concerns/lock_manager.rb +39 -0
  94. data/app/services/curation_concerns/lockable.rb +16 -0
  95. data/app/services/curation_concerns/noid.rb +23 -0
  96. data/app/services/curation_concerns/persist_derivatives.rb +33 -0
  97. data/app/services/curation_concerns/persist_directly_contained_output_file_service.rb +26 -0
  98. data/app/services/curation_concerns/repository_audit_service.rb +7 -0
  99. data/app/services/curation_concerns/thumbnail_path_service.rb +46 -0
  100. data/app/services/curation_concerns/time_service.rb +7 -0
  101. data/app/services/curation_concerns/versioning_service.rb +26 -0
  102. data/app/validators/has_one_title_validator.rb +8 -0
  103. data/app/views/batch_select/_add_button.html.erb +3 -0
  104. data/app/views/batch_select/_check_all.html.erb +4 -0
  105. data/app/views/batch_select/_tools.html.erb +10 -0
  106. data/app/views/catalog/_action_menu_partials/_collection.html.erb +3 -3
  107. data/app/views/catalog/_action_menu_partials/_default.html.erb +1 -1
  108. data/app/views/catalog/_document_list.html.erb +1 -1
  109. data/app/views/collections/_bookmark_control.html.erb +2 -0
  110. data/app/views/collections/_button_create_collection.html.erb +2 -0
  111. data/app/views/collections/_button_for_creating_empty_collection.html.erb +1 -1
  112. data/app/views/collections/_button_for_delete_collection.html.erb +4 -0
  113. data/app/views/collections/_button_for_remove_selected_from_collection.html.erb +8 -0
  114. data/app/views/collections/_button_for_update_collection.html.erb +4 -0
  115. data/app/views/collections/_button_remove_from_collection.html.erb +4 -0
  116. data/app/views/collections/_document_header.html.erb +9 -0
  117. data/app/views/collections/_edit_actions.html.erb +1 -1
  118. data/app/views/collections/_edit_descriptions.html.erb +1 -1
  119. data/app/views/collections/_form.html.erb +2 -2
  120. data/app/views/collections/_form_for_select_destination_collection.html.erb +21 -0
  121. data/app/views/collections/_form_to_add_member.html.erb +1 -1
  122. data/app/views/collections/_index_default.html.erb +2 -0
  123. data/app/views/collections/_index_header_default.html.erb +2 -0
  124. data/app/views/collections/_media_display.html.erb +1 -1
  125. data/app/views/collections/_paginate.html.erb +1 -1
  126. data/app/views/collections/_paginate_compact.html.erb +1 -0
  127. data/app/views/collections/_results_pagination.html.erb +9 -0
  128. data/app/views/collections/_search_collection_dashboard_form.html.erb +1 -1
  129. data/app/views/collections/_search_form.html.erb +1 -1
  130. data/app/views/collections/_search_results.html.erb +23 -0
  131. data/app/views/collections/_show_actions.html.erb +1 -1
  132. data/app/views/collections/_sort_and_per_page.html.erb +1 -1
  133. data/app/views/collections/_view_type_group.html.erb +1 -1
  134. data/app/views/collections/index.html.erb +9 -0
  135. data/app/views/collections/new.html.erb +3 -0
  136. data/app/views/curation_concerns/base/_form_permission.html.erb +10 -11
  137. data/app/views/curation_concerns/base/_form_permission_embargo.html.erb +1 -1
  138. data/app/views/curation_concerns/base/_form_permission_lease.html.erb +1 -1
  139. data/app/views/curation_concerns/base/_legally_binding_text.html.erb +7 -7
  140. data/app/views/curation_concerns/base/_related_files.html.erb +1 -1
  141. data/app/views/curation_concerns/base/_visibility.html.erb +2 -2
  142. data/app/views/curation_concerns/file_sets/_actions.html.erb +1 -1
  143. data/app/views/embargoes/_list_expired_active_embargoes.html.erb +1 -1
  144. data/app/views/error/single_use_error.html.erb +1 -1
  145. data/app/views/shared/_add_content.html.erb +17 -15
  146. data/app/views/shared/_brand_bar.html.erb +19 -10
  147. data/app/views/shared/_header.html.erb +2 -6
  148. data/app/views/shared/_my_actions.html.erb +28 -27
  149. data/app/views/shared/_site_actions.html.erb +5 -1
  150. data/app/views/shared/_site_search.html.erb +3 -2
  151. data/app/views/shared/_title_bar.html.erb +7 -16
  152. data/app/views/welcome/index.html.erb +2 -2
  153. data/config/locales/curation_concerns.en.yml +25 -1
  154. data/curation_concerns.gemspec +21 -5
  155. data/lib/curation_concerns/collections/accepts_batches.rb +53 -0
  156. data/lib/curation_concerns/collections/search_service.rb +57 -0
  157. data/lib/curation_concerns/collections.rb +10 -0
  158. data/lib/curation_concerns/configuration.rb +167 -0
  159. data/lib/curation_concerns/engine.rb +22 -1
  160. data/lib/curation_concerns/messages.rb +68 -0
  161. data/lib/curation_concerns/models.rb +42 -0
  162. data/lib/curation_concerns/name.rb +20 -0
  163. data/lib/curation_concerns/null_logger.rb +10 -0
  164. data/lib/curation_concerns/rails/routes.rb +1 -3
  165. data/lib/curation_concerns/version.rb +1 -1
  166. data/lib/curation_concerns.rb +2 -0
  167. data/lib/generators/curation_concerns/abstract_migration_generator.rb +31 -0
  168. data/lib/generators/curation_concerns/clamav_generator.rb +19 -0
  169. data/lib/generators/curation_concerns/collection_generator.rb +15 -0
  170. data/lib/generators/curation_concerns/install_generator.rb +1 -2
  171. data/lib/generators/curation_concerns/models_generator.rb +62 -0
  172. data/lib/generators/curation_concerns/templates/app/models/collection.rb +6 -0
  173. data/lib/generators/curation_concerns/templates/app/models/file_set.rb +4 -0
  174. data/lib/generators/curation_concerns/templates/config/clamav.rb +1 -0
  175. data/lib/generators/curation_concerns/templates/config/curation_concerns.rb +61 -0
  176. data/lib/generators/curation_concerns/templates/config/mime_types.rb +6 -0
  177. data/lib/generators/curation_concerns/templates/config/redis.yml +9 -0
  178. data/lib/generators/curation_concerns/templates/config/redis_config.rb +29 -0
  179. data/lib/generators/curation_concerns/templates/config/resque-pool.yml +1 -0
  180. data/lib/generators/curation_concerns/templates/config/resque_config.rb +6 -0
  181. data/lib/generators/curation_concerns/templates/curation_concerns.scss +3 -2
  182. data/lib/generators/curation_concerns/templates/migrations/create_checksum_audit_logs.rb +19 -0
  183. data/lib/generators/curation_concerns/templates/migrations/create_single_use_links.rb +12 -0
  184. data/lib/generators/curation_concerns/templates/migrations/create_version_committers.rb +15 -0
  185. data/lib/tasks/migrate.rake +11 -0
  186. data/lib/tasks/resque.rake +14 -0
  187. data/lib/tasks/solr_reindex.rake +8 -0
  188. data/spec/actors/curation_concerns/file_set_actor_spec.rb +31 -0
  189. data/spec/controllers/accepts_batches_controller_spec.rb +65 -0
  190. data/spec/controllers/collections_controller_spec.rb +272 -0
  191. data/spec/controllers/curation_concerns/collections_controller_spec.rb +1 -2
  192. data/spec/controllers/selects_collections_controller_spec.rb +109 -0
  193. data/spec/features/create_work_spec.rb +1 -1
  194. data/spec/features/work_generator_spec.rb +1 -1
  195. data/spec/forms/collection_edit_form_spec.rb +2 -9
  196. data/spec/forms/work_form_spec.rb +5 -0
  197. data/spec/helpers/collections_helper_spec.rb +129 -0
  198. data/spec/helpers/curation_concerns/collections_helper_spec.rb +2 -2
  199. data/spec/helpers/render_constraints_helper_spec.rb +23 -1
  200. data/spec/lib/curation_concerns/collections/search_service_spec.rb +33 -0
  201. data/spec/models/collection_spec.rb +165 -0
  202. data/spec/tasks/rake_spec.rb +1 -1
  203. data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
  204. data/spec/views/curation_concerns/base/_form_permission.html.erb_spec.rb +4 -1
  205. data/spec/views/curation_concerns/file_sets/show.html.erb_spec.rb +1 -0
  206. data/spec/views/shared/_add_content.html.erb_spec.rb +3 -3
  207. metadata +341 -24
  208. data/VERSION +0 -1
  209. data/app/assets/stylesheets/curation_concerns/_global-variables.scss +0 -5
  210. data/app/assets/stylesheets/curation_concerns/modules/multi_value_fields.scss +0 -52
  211. data/app/views/collections/_form_required_information.html.erb +0 -11
  212. data/tasks/release.rake +0 -93
@@ -1,46 +0,0 @@
1
- .search-form {
2
- position:relative;
3
-
4
- .search-query,
5
- .search-submit {
6
- -moz-box-sizing: border-box;
7
- box-sizing:border-box;
8
- }
9
-
10
- .search-query {
11
- width:100%;
12
- padding-left:.6em;
13
- padding-right:12%;
14
- height: 2.1em;
15
- border-radius: $input-border-radius;
16
- }
17
-
18
- .search-submit {
19
- padding:2px;
20
- position:absolute;
21
- right:2px;
22
- text-align:center;
23
- top:2px;
24
- white-space:nowrap;
25
- width:9%;
26
- }
27
- }
28
-
29
- .search-query-form {
30
- margin-bottom:.3em;
31
- padding-left:1em;
32
-
33
- .search-scope {
34
- display:block;
35
- line-height:1.5em;
36
- margin-bottom:.3em;
37
-
38
- input {
39
- margin-top:0;
40
- }
41
- }
42
-
43
- .label-text {
44
- padding-left:.3em;
45
- }
46
- }
@@ -0,0 +1,4 @@
1
+ @import 'bootstrap-sprockets';
2
+ @import 'bootstrap';
3
+
4
+ @import 'curation_concerns/curation_concerns';
@@ -1,54 +1,150 @@
1
1
  module CurationConcerns
2
2
  module CollectionsControllerBehavior
3
3
  extend ActiveSupport::Concern
4
- include Hydra::CollectionsControllerBehavior
5
4
  include Blacklight::AccessControls::Catalog
6
5
 
6
+ include Blacklight::Base
7
+ include CurationConcerns::SelectsCollections
8
+
7
9
  included do
8
10
  before_action :filter_docs_with_read_access!, except: :show
11
+ before_action :remove_select_something_first_flash, except: :show
9
12
  layout 'curation_concerns/1_column'
10
- skip_load_and_authorize_resource only: :show
13
+
14
+ include CurationConcerns::Collections::AcceptsBatches
15
+
16
+ # This is needed as of BL 3.7
17
+ copy_blacklight_config_from(::CatalogController)
18
+
19
+ # Catch permission errors
20
+ rescue_from Hydra::AccessDenied, CanCan::AccessDenied do |exception|
21
+ if exception.action == :edit
22
+ redirect_to(collections.url_for(action: 'show'), alert: 'You do not have sufficient privileges to edit this document')
23
+ elsif current_user && current_user.persisted?
24
+ redirect_to root_url, alert: exception.message
25
+ else
26
+ session['user_return_to'] = request.url
27
+ redirect_to new_user_session_url, alert: exception.message
28
+ end
29
+ end
30
+
31
+ # actions: audit, index, create, new, edit, show, update, destroy, permissions, citation
32
+ before_action :authenticate_user!, except: [:show]
33
+ load_and_authorize_resource except: [:index, :show], instance_name: :collection
11
34
 
12
35
  class_attribute :presenter_class, :form_class
13
36
  self.presenter_class = CurationConcerns::CollectionPresenter
14
37
  self.form_class = CurationConcerns::Forms::CollectionEditForm
15
38
  end
16
39
 
40
+ def index
41
+ # run the solr query to find the collections
42
+ query = collections_search_builder.with(params).query
43
+ @response = repository.search(query)
44
+ @document_list = @response.documents
45
+ end
46
+
17
47
  def new
18
- super
19
48
  form
20
49
  end
21
50
 
51
+ def show
52
+ presenter
53
+ query_collection_members
54
+ end
55
+
22
56
  def edit
23
- super
57
+ query_collection_members
58
+ find_collections
24
59
  form
25
60
  end
26
61
 
27
- def show
28
- presenter
29
- super
62
+ def after_create
63
+ form
64
+ respond_to do |format|
65
+ ActiveFedora::SolrService.instance.conn.commit
66
+ format.html { redirect_to collection_path(@collection), notice: 'Collection was successfully created.' }
67
+ format.json { render json: @collection, status: :created, location: @collection }
68
+ end
30
69
  end
31
70
 
32
71
  def after_create_error
33
72
  form
34
- super
73
+
74
+ respond_to do |format|
75
+ format.html { render action: 'new' }
76
+ format.json { render json: @collection.errors, status: :unprocessable_entity }
77
+ end
78
+ end
79
+
80
+ def create
81
+ @collection.apply_depositor_metadata(current_user.user_key)
82
+ add_members_to_collection unless batch.empty?
83
+ if @collection.save
84
+ after_create
85
+ else
86
+ after_create_error
87
+ end
88
+ end
89
+
90
+ def after_update
91
+ if flash[:notice].nil?
92
+ flash[:notice] = 'Collection was successfully updated.'
93
+ end
94
+ respond_to do |format|
95
+ format.html { redirect_to collection_path(@collection) }
96
+ format.json { render json: @collection, status: :updated, location: @collection }
97
+ end
35
98
  end
36
99
 
37
100
  def after_update_error
38
101
  form
39
102
  query_collection_members
40
- super
103
+
104
+ respond_to do |format|
105
+ format.html { render action: 'edit' }
106
+ format.json { render json: @collection.errors, status: :unprocessable_entity }
107
+ end
108
+ end
109
+
110
+ def update
111
+ process_member_changes
112
+ if @collection.update(collection_params.except(:members))
113
+ after_update
114
+ else
115
+ after_update_error
116
+ end
117
+ end
118
+
119
+ def after_destroy(id)
120
+ respond_to do |format|
121
+ format.html { redirect_to search_catalog_path, notice: 'Collection was successfully deleted.' }
122
+ format.json { render json: { id: id }, status: :destroyed, location: @collection }
123
+ end
124
+ end
125
+
126
+ def after_destroy_error(id)
127
+ respond_to do |format|
128
+ format.html { redirect_to search_catalog_path, notice: 'Collection could not be deleted.' }
129
+ format.json { render json: { id: id }, status: :destroy_error, location: @collection }
130
+ end
131
+ end
132
+
133
+ def destroy
134
+ if @collection.destroy
135
+ after_destroy(params[:id])
136
+ else
137
+ after_destroy_error(params[:id])
138
+ end
41
139
  end
42
140
 
43
- # overriding the method in Hydra::Collections so the search builder can find the collection
44
141
  def collection
45
142
  action_name == 'show' ? @presenter : @collection
46
143
  end
47
144
 
48
145
  protected
49
146
 
50
- def filter_docs_with_read_access!
51
- super
147
+ def remove_select_something_first_flash
52
148
  flash.delete(:notice) if flash.notice == 'Select something first'
53
149
  end
54
150
 
@@ -81,25 +177,74 @@ module CurationConcerns
81
177
  form_class.model_attributes(params[:collection])
82
178
  end
83
179
 
180
+ def form
181
+ @form ||= form_class.new(@collection)
182
+ end
183
+
184
+ # Queries Solr for members of the collection.
185
+ # Populates @response and @member_docs similar to Blacklight Catalog#index populating @response and @documents
84
186
  def query_collection_members
85
187
  params[:q] = params[:cq]
86
- super
188
+ @response = repository.search(query_for_collection_members)
189
+ @member_docs = @response.documents
190
+ end
191
+
192
+ # @return <Hash> a representation of the solr query that find the collection members
193
+ def query_for_collection_members
194
+ collection_member_search_builder.with(params_for_members_query).query
87
195
  end
88
196
 
89
- def after_destroy(id)
90
- respond_to do |format|
91
- format.html { redirect_to collections_path, notice: 'Collection was successfully deleted.' }
92
- format.json { render json: { id: id }, status: :destroyed, location: @collection }
197
+ # You can override this method if you need to provide additional inputs to the search
198
+ # builder. For example:
199
+ # search_field: 'all_fields'
200
+ # @return <Hash> the inputs required for the collection member search builder
201
+ def params_for_members_query
202
+ params.symbolize_keys.merge(q: params[:cq])
203
+ end
204
+
205
+ def collection_member_search_builder
206
+ @collection_member_search_builder ||= collection_member_search_builder_class.new(self).tap do |builder|
207
+ builder.current_ability = current_ability
93
208
  end
94
209
  end
95
210
 
96
- def form
97
- @form ||= form_class.new(@collection)
211
+ def process_member_changes
212
+ case params[:collection][:members]
213
+ when 'add' then add_members_to_collection
214
+ when 'remove' then remove_members_from_collection
215
+ when 'move' then move_members_between_collections
216
+ when Array then assign_batch_to_collection
217
+ end
218
+ end
219
+
220
+ def add_members_to_collection(collection = nil)
221
+ collection ||= @collection
222
+ collection.add_members batch
223
+ end
224
+
225
+ def remove_members_from_collection
226
+ @collection.members.delete(batch.map { |pid| ActiveFedora::Base.find(pid) })
227
+ end
228
+
229
+ def assign_batch_to_collection
230
+ @collection.members(true) # Force the members to get cached before (maybe) removing some of them
231
+ @collection.member_ids = batch
232
+ end
233
+
234
+ def move_members_between_collections
235
+ destination_collection = ::Collection.find(params[:destination_collection_id])
236
+ remove_members_from_collection
237
+ add_members_to_collection(destination_collection)
238
+ if destination_collection.save
239
+ flash[:notice] = "Successfully moved #{batch.count} files to #{destination_collection.title} Collection."
240
+ else
241
+ flash[:error] = "An error occured. Files were not moved to #{destination_collection.title} Collection."
242
+ end
98
243
  end
99
244
 
100
245
  # Include 'catalog' and 'curation_concerns/base' in the search path for views
101
246
  def _prefixes
102
247
  @_prefixes ||= super + ['catalog', 'curation_concerns/base']
103
248
  end
104
- end
105
- end
249
+ end # module CollectionsControllerBehavior
250
+ end # module Hydra
@@ -2,7 +2,7 @@ module CurationConcerns
2
2
  module EmbargoesControllerBehavior
3
3
  extend ActiveSupport::Concern
4
4
  include CurationConcerns::ManagesEmbargoes
5
- include Hydra::Collections::AcceptsBatches
5
+ include CurationConcerns::Collections::AcceptsBatches
6
6
 
7
7
  included do
8
8
  skip_before_action :normalize_identifier, only: :update
@@ -2,7 +2,7 @@ module CurationConcerns
2
2
  module LeasesControllerBehavior
3
3
  extend ActiveSupport::Concern
4
4
  include CurationConcerns::ManagesEmbargoes
5
- include Hydra::Collections::AcceptsBatches
5
+ include CurationConcerns::Collections::AcceptsBatches
6
6
 
7
7
  included do
8
8
  skip_before_action :normalize_identifier, only: :update
@@ -0,0 +1,65 @@
1
+ module CurationConcerns::SelectsCollections
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ configure_blacklight do |config|
6
+ config.search_builder_class = CurationConcerns::CollectionSearchBuilder
7
+ end
8
+ end
9
+
10
+ def access_levels
11
+ { read: [:read, :edit], edit: [:edit] }
12
+ end
13
+
14
+ ##
15
+ # Return list of collections for which the current user has read access.
16
+ # Add this or find_collections_with_edit_access as a before filter on any page that shows the form_for_select_collection
17
+ #
18
+ # @return [Array<SolrDocument>] Solr documents for each collection the user has read access
19
+ def find_collections_with_read_access
20
+ find_collections(:read)
21
+ end
22
+
23
+ ##
24
+ # Return list of collections for which the current user has edit access. Optionally prepend with default
25
+ # that can be used in a select menu to instruct user to select a collection.
26
+ # Add this or find_collections_with_read_access as a before filter on any page that shows the form_for_select_collection
27
+ #
28
+ # @param [TrueClass|FalseClass] :include_default if true, prepends the default_option; otherwise, if false, returns only collections
29
+ # @param [Fixnum] :default_id for select menus, this is the id of the first selection representing the instructions
30
+ # @param [String] :default_title for select menus, this is the text displayed as the first item serving as instructions
31
+ #
32
+ # @return [Array<SolrDocument>] Solr documents for each collection the user has edit access, plus optional instructions
33
+ def find_collections_with_edit_access(include_default = false, default_id = -1, default_title = 'Select collection...')
34
+ find_collections(:edit)
35
+ default_option = SolrDocument.new(id: default_id, title_tesim: default_title)
36
+ @user_collections.unshift(default_option) if include_default
37
+ end
38
+
39
+ ##
40
+ # Return list of collections matching the passed in access_level for the current user.
41
+ # @param [Symbol] :access_level one of :read or :edit
42
+ # @return [Array<SolrDocument>] Solr documents for each collection the user has the appropriate access level
43
+ def find_collections(access_level = nil)
44
+ # need to know the user if there is an access level applied otherwise we are just doing public collections
45
+ authenticate_user! unless access_level.blank?
46
+
47
+ # run the solr query to find the collections
48
+ query = collections_search_builder(access_level).with(q: '').query
49
+ response = repository.search(query)
50
+
51
+ # return the user's collections (or public collections if no access_level is applied)
52
+ @user_collections = response.documents
53
+ end
54
+
55
+ def collections_search_builder_class
56
+ CurationConcerns::CollectionSearchBuilder
57
+ end
58
+
59
+ def collections_search_builder(access_level = nil)
60
+ collections_search_builder_class.new(self).tap do |builder|
61
+ builder.current_ability = current_ability
62
+ builder.discovery_perms = access_levels[access_level] if access_level
63
+ end
64
+ end
65
+ end
@@ -18,35 +18,6 @@ module CurationConcerns
18
18
  model_class.validators_on(key).any? { |v| v.is_a? ActiveModel::Validations::PresenceValidator }
19
19
  end
20
20
 
21
- # The form only supports a single value for title, so use the first
22
- # @return [String] the first title
23
- def title
24
- model.title.first
25
- end
26
-
27
- # The form only supports a single value for description, so use the first
28
- # @return [String] the first description
29
- def description
30
- model.description.first
31
- end
32
-
33
- # @param [Symbol] key the property to test for cardinality
34
- # @return [FalseClass,TrueClass] whether the value on the form is singular or multipl
35
- def self.multiple?(key)
36
- return false if [:title, :description].include? key
37
- super
38
- end
39
-
40
- # @param [ActiveSupport::Parameters]
41
- # @return [Hash] a hash suitable for populating Collection attributes.
42
- def self.model_attributes(_)
43
- attrs = super
44
- # cast title and description back to multivalued
45
- attrs[:title] = Array(attrs[:title]) if attrs[:title]
46
- attrs[:description] = Array(attrs[:description]) if attrs[:description]
47
- attrs
48
- end
49
-
50
21
  # @return [Hash] All generic files in the collection, file.to_s is the key, file.id is the value
51
22
  def select_files
52
23
  Hash[all_files]
@@ -6,7 +6,8 @@ module CurationConcerns
6
6
 
7
7
  delegate :human_readable_type, :open_access?, :authenticated_only_access?,
8
8
  :open_access_with_embargo_release_date?, :private_access?,
9
- :embargo_release_date, :lease_expiration_date, :member_ids, to: :model
9
+ :embargo_release_date, :lease_expiration_date, :member_ids,
10
+ :visibility, to: :model
10
11
 
11
12
  self.terms = [:title, :creator, :contributor, :description,
12
13
  :tag, :rights, :publisher, :date_created, :subject, :language,
@@ -0,0 +1,23 @@
1
+ # View Helpers for Hydra Batch Edit functionality
2
+ module BatchSelectHelper
3
+ # determines if the given document id is in the batch
4
+ # def item_in_batch?(doc_id)
5
+ # session[:batch_document_ids] && session[:batch_document_ids].include?(doc_id) ? true : false
6
+ # end
7
+
8
+ # Displays the batch edit tools. Put this in your search result page template. We recommend putting it in catalog/_sort_and_per_page.html.erb
9
+ def batch_select_tools
10
+ render partial: '/batch_select/tools'
11
+ end
12
+
13
+ # Displays the button to select/deselect items for your batch. Call this in the index partial that's rendered for each search result.
14
+ # @param [Hash] document the Hash (aka Solr hit) for one Solr document
15
+ def button_for_add_to_batch(document)
16
+ render partial: '/batch_select/add_button', locals: { document: document }
17
+ end
18
+
19
+ # Displays the check all button to select/deselect items for your batch. Put this in your search result page template. We put it in catalog/index.html
20
+ def batch_check_all(label = 'Use all results')
21
+ render partial: '/batch_select/check_all', locals: { label: label }
22
+ end
23
+ end
@@ -0,0 +1,4 @@
1
+ # View Helpers for Hydra Collections functionality
2
+ module CollectionsHelper
3
+ include CurationConcerns::CollectionsHelperBehavior
4
+ end
@@ -14,8 +14,8 @@ module CurationConcerns::CollectionsHelper
14
14
  # override hydra-collections
15
15
  def link_to_remove_from_collection(document, label = 'Remove From Collection')
16
16
  collection_id = @collection ? @collection.id : @presenter.id
17
- link_to collections.collection_path(collection_id, collection: { members: 'remove' },
18
- batch_document_ids: [document.id]), method: :put do
17
+ link_to collection_path(collection_id, collection: { members: 'remove' },
18
+ batch_document_ids: [document.id]), method: :put do
19
19
  icon('minus-sign') + ' ' + label
20
20
  end
21
21
  end
@@ -0,0 +1,56 @@
1
+ # View Helpers for Hydra Collections functionality
2
+ module CurationConcerns
3
+ module CollectionsHelperBehavior
4
+ def has_collection_search_parameters?
5
+ !params[:cq].blank?
6
+ end
7
+
8
+ # Displays the Collections create collection button. Put this in your search result page template. We recommend putting it in catalog/_sort_and_per_page.html.erb
9
+ def button_for_create_collection(label = 'Create Collection')
10
+ render '/collections/button_create_collection', label: label
11
+ end
12
+
13
+ # Displays the Collections update collection button. Put this in your search result page template. We recommend putting it in catalog/_sort_and_per_page.html.erb
14
+ def button_for_update_collection(label = 'Update Collection', collection_id = 'collection_replace_id')
15
+ render '/collections/button_for_update_collection', label: label, collection_id: collection_id
16
+ end
17
+
18
+ # Displays the Collections delete collection button. Put this in your search result page for each collection found.
19
+ def button_for_delete_collection(collection, label = 'Delete Collection', confirm = 'Are you sure?')
20
+ render '/collections/button_for_delete_collection', collection: collection, label: label, confirm: confirm
21
+ end
22
+
23
+ def button_for_remove_from_collection(collection, document, label = 'Remove From Collection')
24
+ render '/collections/button_remove_from_collection', collection: collection, label: label, document: document
25
+ end
26
+
27
+ def button_for_remove_selected_from_collection(collection, label = 'Remove From Collection')
28
+ render '/collections/button_for_remove_selected_from_collection', collection: collection, label: label
29
+ end
30
+
31
+ # add hidden fields to a form for removing a single document from a collection
32
+ def single_item_action_remove_form_fields(form, document)
33
+ single_item_action_form_fields(form, document, 'remove')
34
+ end
35
+
36
+ # add hidden fields to a form for adding a single document to a collection
37
+ def single_item_action_add_form_fields(form, document)
38
+ single_item_action_form_fields(form, document, 'add')
39
+ end
40
+
41
+ # add hidden fields to a form for performing an action on a single document on a collection
42
+ def single_item_action_form_fields(form, document, action)
43
+ render '/collections/single_item_action_fields', form: form, document: document, action: action
44
+ end
45
+
46
+ def hidden_collection_members
47
+ erbout = ''
48
+ if params[:batch_document_ids].present?
49
+ params[:batch_document_ids].each do |batch_item|
50
+ erbout.concat hidden_field_tag('batch_document_ids[]', batch_item)
51
+ end
52
+ end
53
+ erbout.html_safe
54
+ end
55
+ end
56
+ end
@@ -1,42 +1,21 @@
1
1
  module CurationConcerns
2
2
  module RenderConstraintsHelper
3
- # Overridden to remove the 'search_field' tag from the localized params when the query is cleared.
4
- # This is because unlike Blacklight, there is no way to change the search_field in the curation_concerns UI
5
- ##
6
- # Render the query constraints
7
- #
8
- # @param [Hash] query parameters
9
- # @return [String]
10
- def render_constraints_query(localized_params = params)
11
- # So simple don't need a view template, we can just do it here.
12
- return ''.html_safe if localized_params[:q].blank?
13
-
14
- render_constraint_element(constraint_query_label(localized_params),
15
- localized_params[:q],
16
- classes: ['query'],
17
- remove: url_for(localized_params.except(:search_field).merge(q: nil, action: 'index')))
18
- end
19
-
20
- ##
21
- # We can remove this method once we use Blacklight > 5.4.0
22
- ##
23
- # Return a label for the currently selected search field.
24
- # If no "search_field" or the default (e.g. "all_fields") is selected, then return nil
25
- # Otherwise grab the label of the selected search field.
26
- # @param [Hash] query parameters
27
- # @return [String]
28
- def constraint_query_label(localized_params = params)
29
- label_for_search_field(localized_params[:search_field]) unless default_search_field?(localized_params[:search_field])
3
+ # This overrides Blacklight to remove the 'search_field' tag from the
4
+ # localized params when the query is cleared. This is because unlike
5
+ # Blacklight, there is no control to change the search_field in the
6
+ # curation_concerns UI
7
+ def remove_constraint_url(localized_params)
8
+ scope = localized_params.delete(:route_set) || self
9
+ options = localized_params.merge(q: nil, action: 'index')
10
+ .except(*fields_to_exclude_from_constraint_element)
11
+ options.permit!
12
+ scope.url_for(options)
30
13
  end
31
14
 
32
- ##
33
- # We can remove this method once we use Blacklight > 5.4.0
34
- ##
35
- # Is the search form using the default search field ("all_fields" by default)?
36
- # @param [String] the currently selected search_field
37
- # @return [Boolean]
38
- def default_search_field?(selected_search_field)
39
- selected_search_field.blank? || (default_search_field && selected_search_field == default_search_field[:key])
15
+ # @return [Array<Symbol>] a list of fields to remove on the render_constraint_element
16
+ # You can override this if you have different fields to remove
17
+ def fields_to_exclude_from_constraint_element
18
+ [:search_field]
40
19
  end
41
20
  end
42
21
  end
@@ -1,4 +1,8 @@
1
1
  module CurationConcerns::TitleHelper
2
+ def application_name
3
+ t('curation_concerns.product_name', default: super)
4
+ end
5
+
2
6
  def construct_page_title(*elements)
3
7
  (elements.flatten.compact + [application_name]).join(' // ')
4
8
  end
@@ -0,0 +1,16 @@
1
+ module CurationConcerns
2
+ class CollectionIndexer < Hydra::PCDM::CollectionIndexer
3
+ include IndexesThumbnails
4
+ STORED_LONG = Solrizer::Descriptor.new(:long, :stored)
5
+
6
+ def generate_solr_document
7
+ super.tap do |solr_doc|
8
+ # Makes Collections show under the "Collections" tab
9
+ Solrizer.set_field(solr_doc, 'generic_type', 'Collection', :facetable)
10
+ # Index the size of the collection in bytes
11
+ solr_doc[Solrizer.solr_name(:bytes, STORED_LONG)] = object.bytes
12
+ solr_doc['thumbnail_path_ss'] = thumbnail_path
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,46 @@
1
+ module CurationConcerns
2
+ class FileSetIndexer < ActiveFedora::IndexingService
3
+ include IndexesThumbnails
4
+ STORED_INTEGER = Solrizer::Descriptor.new(:integer, :stored)
5
+
6
+ def generate_solr_document
7
+ super.tap do |solr_doc|
8
+ solr_doc[Solrizer.solr_name('hasRelatedMediaFragment', :symbol)] = object.representative_id
9
+ solr_doc[Solrizer.solr_name('hasRelatedImage', :symbol)] = object.thumbnail_id
10
+ # Label is the actual file name. It's not editable by the user.
11
+ solr_doc[Solrizer.solr_name('label')] = object.label
12
+ solr_doc[Solrizer.solr_name('label', :stored_sortable)] = object.label
13
+ solr_doc[Solrizer.solr_name('file_format')] = file_format
14
+ solr_doc[Solrizer.solr_name('file_format', :facetable)] = file_format
15
+ solr_doc[Solrizer.solr_name(:file_size, STORED_INTEGER)] = object.file_size[0]
16
+ solr_doc['all_text_timv'] = object.full_text.content
17
+ solr_doc[Solrizer.solr_name('generic_work_ids', :symbol)] = object.generic_work_ids unless object.generic_work_ids.empty?
18
+ solr_doc['height_is'] = Integer(object.height.first) if object.height.present?
19
+ solr_doc['width_is'] = Integer(object.width.first) if object.width.present?
20
+ solr_doc[Solrizer.solr_name('mime_type', :stored_sortable)] = object.mime_type
21
+ solr_doc['thumbnail_path_ss'] = thumbnail_path
22
+ # Index the Fedora-generated SHA1 digest to create a linkage
23
+ # between files on disk (in fcrepo.binary-store-path) and objects
24
+ # in the repository.
25
+ solr_doc[Solrizer.solr_name('digest', :symbol)] = digest_from_content
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def digest_from_content
32
+ return unless object.original_file
33
+ object.original_file.digest.first.to_s
34
+ end
35
+
36
+ def file_format
37
+ if object.mime_type.present? && object.format_label.present?
38
+ "#{object.mime_type.split('/').last} (#{object.format_label.join(', ')})"
39
+ elsif object.mime_type.present?
40
+ object.mime_type.split('/').last
41
+ elsif object.format_label.present?
42
+ object.format_label
43
+ end
44
+ end
45
+ end
46
+ end