bulkrax 6.0.1 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +7 -7
- data/app/assets/javascripts/bulkrax/bulkrax.js +11 -0
- data/app/assets/javascripts/bulkrax/datatables.js +139 -0
- data/app/assets/javascripts/bulkrax/exporters.js +4 -4
- data/app/assets/javascripts/bulkrax/importers.js.erb +15 -1
- data/app/assets/stylesheets/bulkrax/import_export.scss +6 -1
- data/app/controllers/bulkrax/entries_controller.rb +52 -3
- data/app/controllers/bulkrax/exporters_controller.rb +20 -8
- data/app/controllers/bulkrax/importers_controller.rb +31 -12
- data/app/controllers/concerns/bulkrax/datatables_behavior.rb +201 -0
- data/app/factories/bulkrax/object_factory.rb +135 -163
- data/app/factories/bulkrax/object_factory_interface.rb +491 -0
- data/app/factories/bulkrax/valkyrie_object_factory.rb +402 -0
- data/app/helpers/bulkrax/application_helper.rb +7 -3
- data/app/helpers/bulkrax/importers_helper.rb +1 -1
- data/app/helpers/bulkrax/validation_helper.rb +4 -4
- data/app/jobs/bulkrax/create_relationships_job.rb +28 -17
- data/app/jobs/bulkrax/delete_and_import_collection_job.rb +8 -0
- data/app/jobs/bulkrax/delete_and_import_file_set_job.rb +8 -0
- data/app/jobs/bulkrax/delete_and_import_job.rb +20 -0
- data/app/jobs/bulkrax/delete_and_import_work_job.rb +8 -0
- data/app/jobs/bulkrax/delete_job.rb +8 -3
- data/app/jobs/bulkrax/download_cloud_file_job.rb +17 -4
- data/app/jobs/bulkrax/import_collection_job.rb +1 -1
- data/app/jobs/bulkrax/import_file_set_job.rb +6 -3
- data/app/jobs/bulkrax/import_job.rb +7 -0
- data/app/jobs/bulkrax/import_work_job.rb +1 -1
- data/app/jobs/bulkrax/importer_job.rb +19 -3
- data/app/matchers/bulkrax/application_matcher.rb +0 -2
- data/app/models/bulkrax/csv_collection_entry.rb +1 -3
- data/app/models/bulkrax/csv_entry.rb +9 -7
- data/app/models/bulkrax/entry.rb +9 -11
- data/app/models/bulkrax/exporter.rb +11 -4
- data/app/models/bulkrax/importer.rb +49 -10
- data/app/models/bulkrax/oai_entry.rb +0 -3
- data/app/models/bulkrax/oai_set_entry.rb +1 -3
- data/app/models/bulkrax/rdf_collection_entry.rb +1 -4
- data/app/models/bulkrax/rdf_entry.rb +70 -69
- data/app/models/bulkrax/status.rb +10 -1
- data/app/models/bulkrax/xml_entry.rb +0 -1
- data/app/models/concerns/bulkrax/dynamic_record_lookup.rb +2 -19
- data/app/models/concerns/bulkrax/export_behavior.rb +2 -2
- data/app/models/concerns/bulkrax/file_factory.rb +174 -118
- data/app/models/concerns/bulkrax/file_set_entry_behavior.rb +5 -3
- data/app/models/concerns/bulkrax/has_matchers.rb +28 -25
- data/app/models/concerns/bulkrax/import_behavior.rb +14 -33
- data/app/models/concerns/bulkrax/importer_exporter_behavior.rb +3 -2
- data/app/models/concerns/bulkrax/status_info.rb +8 -0
- data/app/parsers/bulkrax/application_parser.rb +116 -21
- data/app/parsers/bulkrax/bagit_parser.rb +173 -195
- data/app/parsers/bulkrax/csv_parser.rb +15 -57
- data/app/parsers/bulkrax/oai_dc_parser.rb +44 -16
- data/app/parsers/bulkrax/parser_export_record_set.rb +20 -24
- data/app/parsers/bulkrax/xml_parser.rb +18 -23
- data/app/services/bulkrax/factory_class_finder.rb +92 -0
- data/app/services/bulkrax/remove_relationships_for_importer.rb +3 -1
- data/app/services/hyrax/custom_queries/find_by_source_identifier.rb +50 -0
- data/app/services/wings/custom_queries/find_by_source_identifier.rb +32 -0
- data/app/views/bulkrax/entries/_parsed_metadata.html.erb +2 -2
- data/app/views/bulkrax/entries/_raw_metadata.html.erb +2 -2
- data/app/views/bulkrax/entries/show.html.erb +9 -8
- data/app/views/bulkrax/exporters/_form.html.erb +10 -10
- data/app/views/bulkrax/exporters/edit.html.erb +1 -1
- data/app/views/bulkrax/exporters/index.html.erb +13 -57
- data/app/views/bulkrax/exporters/new.html.erb +1 -1
- data/app/views/bulkrax/exporters/show.html.erb +6 -12
- data/app/views/bulkrax/importers/_browse_everything.html.erb +2 -2
- data/app/views/bulkrax/importers/_csv_fields.html.erb +8 -2
- data/app/views/bulkrax/importers/_edit_form_buttons.html.erb +8 -1
- data/app/views/bulkrax/importers/_edit_item_buttons.html.erb +18 -0
- data/app/views/bulkrax/importers/edit.html.erb +1 -1
- data/app/views/bulkrax/importers/index.html.erb +20 -64
- data/app/views/bulkrax/importers/new.html.erb +1 -1
- data/app/views/bulkrax/importers/show.html.erb +8 -14
- data/app/views/bulkrax/importers/upload_corrected_entries.html.erb +2 -2
- data/app/views/bulkrax/shared/_bulkrax_errors.html.erb +1 -1
- data/app/views/bulkrax/shared/_bulkrax_field_mapping.html.erb +1 -1
- data/app/views/bulkrax/shared/_entries_tab.html.erb +16 -0
- data/config/locales/bulkrax.en.yml +7 -0
- data/config/routes.rb +8 -2
- data/db/migrate/20230608153601_add_indices_to_bulkrax.rb +20 -9
- data/db/migrate/20240208005801_denormalize_status_message.rb +7 -0
- data/db/migrate/20240209070952_update_identifier_index.rb +6 -0
- data/db/migrate/20240307053156_add_index_to_metadata_bulkrax_identifier.rb +18 -0
- data/lib/bulkrax/engine.rb +23 -0
- data/lib/bulkrax/version.rb +1 -1
- data/lib/bulkrax.rb +107 -19
- data/lib/generators/bulkrax/templates/config/initializers/bulkrax.rb +2 -0
- data/lib/tasks/bulkrax_tasks.rake +13 -0
- data/lib/tasks/reset.rake +4 -4
- metadata +64 -8
- data/app/views/bulkrax/shared/_collection_entries_tab.html.erb +0 -39
- data/app/views/bulkrax/shared/_file_set_entries_tab.html.erb +0 -39
- data/app/views/bulkrax/shared/_work_entries_tab.html.erb +0 -39
@@ -0,0 +1,201 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bulkrax
|
4
|
+
# rubocop:disable Metrics/ModuleLength
|
5
|
+
module DatatablesBehavior
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def table_per_page
|
9
|
+
per_page = params[:length].to_i
|
10
|
+
per_page < 1 ? 30 : per_page
|
11
|
+
end
|
12
|
+
|
13
|
+
def order_value(column)
|
14
|
+
params['columns']&.[](column)&.[]('data')
|
15
|
+
end
|
16
|
+
|
17
|
+
def table_order
|
18
|
+
"#{order_value(params&.[]('order')&.[]('0')&.[]('column'))} #{params&.[]('order')&.[]('0')&.[]('dir')}" if params&.[]('order')&.[]('0')&.[]('column').present?
|
19
|
+
end
|
20
|
+
|
21
|
+
# convert offset to page number
|
22
|
+
def table_page
|
23
|
+
params[:start].blank? ? 1 : (params[:start].to_i / params[:length].to_i) + 1
|
24
|
+
end
|
25
|
+
|
26
|
+
def entry_table_search
|
27
|
+
return @entry_table_search if @entry_table_search
|
28
|
+
return @entry_table_search = false if params['search']&.[]('value').blank?
|
29
|
+
|
30
|
+
table_search_value = params['search']&.[]('value')&.downcase
|
31
|
+
|
32
|
+
['identifier', 'id', 'status_message', 'type', 'updated_at'].map do |col|
|
33
|
+
column = Bulkrax::Entry.arel_table[col]
|
34
|
+
column = Arel::Nodes::NamedFunction.new('CAST', [column.as('text')])
|
35
|
+
column = Arel::Nodes::NamedFunction.new('LOWER', [column])
|
36
|
+
@entry_table_search = if @entry_table_search
|
37
|
+
@entry_table_search.or(column.matches("%#{table_search_value}%"))
|
38
|
+
else
|
39
|
+
column.matches("%#{table_search_value}%")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
@entry_table_search
|
44
|
+
end
|
45
|
+
|
46
|
+
def importer_table_search
|
47
|
+
return @importer_table_search if @importer_table_search
|
48
|
+
return @importer_table_search = false if params['search']&.[]('value').blank?
|
49
|
+
|
50
|
+
table_search_value = params['search']&.[]('value')&.downcase
|
51
|
+
|
52
|
+
['name', 'id', 'status_message', 'last_error_at', 'last_succeeded_at', 'updated_at'].map do |col|
|
53
|
+
column = Bulkrax::Importer.arel_table[col]
|
54
|
+
column = Arel::Nodes::NamedFunction.new('CAST', [column.as('text')])
|
55
|
+
column = Arel::Nodes::NamedFunction.new('LOWER', [column])
|
56
|
+
@importer_table_search = if @importer_table_search
|
57
|
+
@importer_table_search.or(column.matches("%#{table_search_value}%"))
|
58
|
+
else
|
59
|
+
column.matches("%#{table_search_value}%")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
@importer_table_search
|
64
|
+
end
|
65
|
+
|
66
|
+
def exporter_table_search
|
67
|
+
return @exporter_table_search if @exporter_table_search
|
68
|
+
return @exporter_table_search = false if params['search']&.[]('value').blank?
|
69
|
+
|
70
|
+
table_search_value = params['search']&.[]('value')&.downcase
|
71
|
+
|
72
|
+
['name', 'status_message', 'created_at'].map do |col|
|
73
|
+
column = Bulkrax::Exporter.arel_table[col]
|
74
|
+
column = Arel::Nodes::NamedFunction.new('CAST', [column.as('text')])
|
75
|
+
column = Arel::Nodes::NamedFunction.new('LOWER', [column])
|
76
|
+
@exporter_table_search = if @exporter_table_search
|
77
|
+
@exporter_table_search.or(column.matches("%#{table_search_value}%"))
|
78
|
+
else
|
79
|
+
column.matches("%#{table_search_value}%")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
@exporter_table_search
|
84
|
+
end
|
85
|
+
|
86
|
+
def format_importers(importers)
|
87
|
+
result = importers.map do |i|
|
88
|
+
{
|
89
|
+
name: view_context.link_to(i.name, view_context.importer_path(i)),
|
90
|
+
status_message: status_message_for(i),
|
91
|
+
last_imported_at: i.last_imported_at&.strftime("%b %d, %Y"),
|
92
|
+
next_import_at: i.next_import_at&.strftime("%b %d, %Y"),
|
93
|
+
enqueued_records: i.last_run&.enqueued_records,
|
94
|
+
processed_records: i.last_run&.processed_records || 0,
|
95
|
+
failed_records: i.last_run&.failed_records || 0,
|
96
|
+
deleted_records: i.last_run&.deleted_records,
|
97
|
+
total_collection_entries: i.last_run&.total_collection_entries,
|
98
|
+
total_work_entries: i.last_run&.total_work_entries,
|
99
|
+
total_file_set_entries: i.last_run&.total_file_set_entries,
|
100
|
+
actions: importer_util_links(i)
|
101
|
+
}
|
102
|
+
end
|
103
|
+
{
|
104
|
+
data: result,
|
105
|
+
recordsTotal: Bulkrax::Importer.count,
|
106
|
+
recordsFiltered: Bulkrax::Importer.count
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
def format_exporters(exporters)
|
111
|
+
result = exporters.map do |e|
|
112
|
+
{
|
113
|
+
name: view_context.link_to(e.name, view_context.exporter_path(e)),
|
114
|
+
status_message: status_message_for(e),
|
115
|
+
created_at: e.created_at,
|
116
|
+
download: download_zip(e),
|
117
|
+
actions: exporter_util_links(e)
|
118
|
+
}
|
119
|
+
end
|
120
|
+
{
|
121
|
+
data: result,
|
122
|
+
recordsTotal: Bulkrax::Exporter.count,
|
123
|
+
recordsFiltered: Bulkrax::Exporter.count
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
def format_entries(entries, item)
|
128
|
+
result = entries.map do |e|
|
129
|
+
{
|
130
|
+
identifier: view_context.link_to(e.identifier, view_context.item_entry_path(item, e)),
|
131
|
+
id: e.id,
|
132
|
+
status_message: status_message_for(e),
|
133
|
+
type: e.type,
|
134
|
+
updated_at: e.updated_at,
|
135
|
+
errors: e.latest_status&.error_class&.present? ? view_context.link_to(e.latest_status.error_class, view_context.item_entry_path(item, e), title: e.latest_status.error_message) : "",
|
136
|
+
actions: entry_util_links(e, item)
|
137
|
+
}
|
138
|
+
end
|
139
|
+
{
|
140
|
+
data: result,
|
141
|
+
recordsTotal: item.entries.size,
|
142
|
+
recordsFiltered: item.entries.size
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
def entry_util_links(e, item)
|
147
|
+
links = []
|
148
|
+
links << view_context.link_to(view_context.raw('<span class="fa fa-info-circle"></span>'), view_context.item_entry_path(item, e))
|
149
|
+
links << "<a class='fa fa-repeat' data-toggle='modal' data-target='#bulkraxItemModal' data-entry-id='#{e.id}'></a>" if view_context.an_importer?(item)
|
150
|
+
links << view_context.link_to(view_context.raw('<span class="fa fa-trash"></span>'), view_context.item_entry_path(item, e), method: :delete, data: { confirm: 'This will delete the entry and any work associated with it. Are you sure?' })
|
151
|
+
links.join(" ")
|
152
|
+
end
|
153
|
+
|
154
|
+
def status_message_for(e)
|
155
|
+
if e.status_message == "Complete"
|
156
|
+
"<td><span class='fa fa-check' style='color: green;'></span> #{e.status_message}</td>"
|
157
|
+
elsif e.status_message == "Pending"
|
158
|
+
"<td><span class='fa fa-ellipsis-h' style='color: blue;'></span> #{e.status_message}</td>"
|
159
|
+
elsif e.status_message == "Skipped"
|
160
|
+
"<td><span class='fa fa-step-forward' style='color: yellow;'></span> #{e.status_message}</td>"
|
161
|
+
else
|
162
|
+
"<td><span class='fa fa-remove' style='color: #{e.status == 'Deleted' ? 'green' : 'red'};'></span> #{e.status_message}</td>"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def importer_util_links(i)
|
167
|
+
links = []
|
168
|
+
links << view_context.link_to(view_context.raw('<span class="fa fa-info-circle"></span>'), importer_path(i))
|
169
|
+
links << view_context.link_to(view_context.raw('<span class="fa fa-pencil"></span>'), edit_importer_path(i))
|
170
|
+
links << view_context.link_to(view_context.raw('<span class="fa fa-remove"></span>'), i, method: :delete, data: { confirm: 'Are you sure?' })
|
171
|
+
links.join(" ")
|
172
|
+
end
|
173
|
+
|
174
|
+
def exporter_util_links(i)
|
175
|
+
links = []
|
176
|
+
links << view_context.link_to(view_context.raw('<span class="fa fa-info-circle"></span>'), exporter_path(i))
|
177
|
+
links << view_context.link_to(view_context.raw('<span class="fa fa-pencil"></span>'), edit_exporter_path(i), data: { turbolinks: false })
|
178
|
+
links << view_context.link_to(view_context.raw('<span class="fa fa-remove"></span>'), i, method: :delete, data: { confirm: 'Are you sure?' })
|
179
|
+
links.join(" ")
|
180
|
+
end
|
181
|
+
|
182
|
+
def download_zip(e)
|
183
|
+
return unless File.exist?(e.exporter_export_zip_path)
|
184
|
+
|
185
|
+
options_html = e.exporter_export_zip_files.flatten.map do |file_name|
|
186
|
+
"<option value='#{CGI.escapeHTML(file_name)}'>#{CGI.escapeHTML(file_name)}</option>"
|
187
|
+
end.join
|
188
|
+
|
189
|
+
form_html = "<form class='simple_form edit_exporter' id='edit_exporter_#{e.id}' action='#{view_context.exporter_download_path(e)}' accept-charset='UTF-8' method='get'>"
|
190
|
+
form_html += "<input name='utf8' type='hidden' value='✓'>"
|
191
|
+
form_html += "<select class='btn btn-default form-control' style='width: 200px' name='exporter[exporter_export_zip_files]' id='exporter_#{e.id}_exporter_export_zip_files'>"
|
192
|
+
form_html += options_html
|
193
|
+
form_html += "</select>\n" # add newline here to add a space between the dropdown and the download button
|
194
|
+
form_html += "<input type='submit' name='commit' value='Download' class='btn btn-default'>"
|
195
|
+
form_html += "</form>"
|
196
|
+
|
197
|
+
form_html
|
198
|
+
end
|
199
|
+
end
|
200
|
+
# rubocop:enable Metrics/ModuleLength
|
201
|
+
end
|
@@ -1,153 +1,171 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Bulkrax
|
4
|
-
|
5
|
-
|
4
|
+
# rubocop:disable Metrics/ClassLength
|
5
|
+
class ObjectFactory < ObjectFactoryInterface
|
6
6
|
include Bulkrax::FileFactory
|
7
|
-
include DynamicRecordLookup
|
8
7
|
|
9
|
-
|
10
|
-
#
|
11
|
-
# These are the attributes that we assume all "work type" classes (e.g. the given :klass) will
|
12
|
-
# have in addition to their specific attributes.
|
13
|
-
#
|
14
|
-
# @return [Array<Symbol>]
|
15
|
-
# @see #permitted_attributes
|
16
|
-
class_attribute :base_permitted_attributes,
|
17
|
-
default: %i[id edit_users edit_groups read_groups visibility work_members_attributes admin_set_id]
|
8
|
+
##
|
9
|
+
# @!group Class Method Interface
|
18
10
|
|
19
|
-
|
20
|
-
#
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
# @see #transform_attributes
|
25
|
-
# @see https://github.com/samvera-labs/bulkrax/pull/708 For discussion concerning this feature
|
26
|
-
# @see https://github.com/samvera-labs/bulkrax/wiki/Interacting-with-Metadata For documentation
|
27
|
-
# concerning default behavior.
|
28
|
-
class_attribute :transformation_removes_blank_hash_values, default: false
|
11
|
+
##
|
12
|
+
# @note This does not save either object. We need to do that in another
|
13
|
+
# loop. Why? Because we might be adding many items to the parent.
|
14
|
+
def self.add_child_to_parent_work(parent:, child:)
|
15
|
+
return true if parent.ordered_members.to_a.include?(child_record)
|
29
16
|
|
30
|
-
|
31
|
-
|
17
|
+
parent.ordered_members << child
|
18
|
+
end
|
32
19
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
@user = user || User.batch_user
|
39
|
-
@work_identifier = work_identifier
|
40
|
-
@work_identifier_search_field = work_identifier_search_field
|
41
|
-
@related_parents_parsed_mapping = related_parents_parsed_mapping
|
42
|
-
@source_identifier_value = source_identifier_value
|
43
|
-
@klass = klass || Bulkrax.default_work_type.constantize
|
44
|
-
@importer_run_id = importer_run_id
|
20
|
+
def self.add_resource_to_collection(collection:, resource:, user:)
|
21
|
+
collection.try(:reindex_extent=, Hyrax::Adapters::NestingIndexAdapter::LIMITED_REINDEX) if
|
22
|
+
defined?(Hyrax::Adapters::NestingIndexAdapter)
|
23
|
+
resource.member_of_collections << collection
|
24
|
+
save!(resource: resource, user: user)
|
45
25
|
end
|
46
|
-
# rubocop:enable Metrics/ParameterLists
|
47
26
|
|
48
|
-
|
49
|
-
|
50
|
-
update_files || replace_files || !object
|
27
|
+
def self.update_index_for_file_sets_of(resource:)
|
28
|
+
resource.file_sets.each(&:update_index) if resource.respond_to?(:file_sets)
|
51
29
|
end
|
52
30
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
else
|
60
|
-
ActiveSupport::Notifications.instrument('import.importer', arg_hash.merge(name: 'CREATE')) { create }
|
61
|
-
end
|
62
|
-
yield(object) if block_given?
|
63
|
-
object
|
31
|
+
##
|
32
|
+
# @see Bulkrax::ObjectFactoryInterface
|
33
|
+
def self.export_properties
|
34
|
+
# TODO: Consider how this may or may not work for Valkyrie
|
35
|
+
properties = Bulkrax.curation_concerns.map { |work| work.properties.keys }.flatten.uniq.sort
|
36
|
+
properties.reject { |prop| Bulkrax.reserved_properties.include?(prop) }
|
64
37
|
end
|
65
38
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
39
|
+
def self.field_multi_value?(field:, model:)
|
40
|
+
return false unless field_supported?(field: field, model: model)
|
41
|
+
return false unless model.singleton_methods.include?(:properties)
|
42
|
+
|
43
|
+
model&.properties&.[](field)&.[]("multiple")
|
71
44
|
end
|
72
45
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
else
|
83
|
-
update_work(attrs)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
object.apply_depositor_metadata(@user) && object.save! if object.depositor.nil?
|
87
|
-
log_updated(object)
|
46
|
+
def self.field_supported?(field:, model:)
|
47
|
+
model.method_defined?(field) && model.properties[field].present?
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.file_sets_for(resource:)
|
51
|
+
return [] if resource.blank?
|
52
|
+
return [resource] if resource.is_a?(Bulkrax.file_model_class)
|
53
|
+
|
54
|
+
resource.file_sets
|
88
55
|
end
|
89
56
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
57
|
+
##
|
58
|
+
#
|
59
|
+
# @see Bulkrax::ObjectFactoryInterface
|
60
|
+
def self.find(id)
|
61
|
+
ActiveFedora::Base.find(id)
|
62
|
+
rescue ActiveFedora::ObjectNotFoundError => e
|
63
|
+
raise ObjectFactoryInterface::ObjectNotFoundError, e.message
|
94
64
|
end
|
95
65
|
|
96
|
-
def
|
97
|
-
|
66
|
+
def self.find_or_create_default_admin_set
|
67
|
+
# NOTE: Hyrax 5+ removed this method
|
68
|
+
AdminSet.find_or_create_default_admin_set_id
|
98
69
|
end
|
99
70
|
|
100
|
-
def
|
101
|
-
|
102
|
-
return o if o
|
103
|
-
run(&:save!)
|
71
|
+
def self.publish(**)
|
72
|
+
return true
|
104
73
|
end
|
105
74
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
75
|
+
##
|
76
|
+
# @param value [String]
|
77
|
+
# @param klass [Class, #where]
|
78
|
+
# @param field [String, Symbol] A convenience parameter where we pass the
|
79
|
+
# same value to search_field and name_field.
|
80
|
+
# @param search_field [String, Symbol] the Solr field name
|
81
|
+
# (e.g. "title_tesim")
|
82
|
+
# @param name_field [String] the ActiveFedora::Base property name
|
83
|
+
# (e.g. "title")
|
84
|
+
# @param verify_property [TrueClass] when true, verify that the given :klass
|
85
|
+
#
|
86
|
+
# @return [NilClass] when no object is found.
|
87
|
+
# @return [ActiveFedora::Base] when a match is found, an instance of given
|
88
|
+
# :klass
|
89
|
+
# rubocop:disable Metrics/ParameterLists
|
90
|
+
#
|
91
|
+
# @note HEY WE'RE USING THIS FOR A WINGS CUSTOM QUERY. BE CAREFUL WITH
|
92
|
+
# REMOVING IT.
|
93
|
+
#
|
94
|
+
# @see # {Wings::CustomQueries::FindBySourceIdentifier#find_by_model_and_property_value}
|
95
|
+
def self.search_by_property(value:, klass:, field: nil, search_field: nil, name_field: nil, verify_property: false)
|
96
|
+
return nil unless klass.respond_to?(:where)
|
97
|
+
# We're not going to try to match nil nor "".
|
98
|
+
return if value.blank?
|
99
|
+
return if verify_property && !klass.properties.keys.include?(search_field)
|
100
|
+
|
101
|
+
search_field ||= field
|
102
|
+
name_field ||= field
|
103
|
+
raise "You must provide either (search_field AND name_field) OR field parameters" if search_field.nil? || name_field.nil?
|
104
|
+
# NOTE: Query can return partial matches (something6 matches both
|
105
|
+
# something6 and something68) so we need to weed out any that are not the
|
106
|
+
# correct full match. But other items might be in the multivalued field,
|
107
|
+
# so we have to go through them one at a time.
|
108
|
+
#
|
109
|
+
# A ssi field is string, so we're looking at exact matches.
|
110
|
+
# A tesi field is text, so partial matches work.
|
111
|
+
#
|
112
|
+
# We need to wrap the result in an Array, else we might have a scalar that
|
113
|
+
# will result again in partial matches.
|
114
|
+
match = klass.where(search_field => value).detect do |m|
|
115
|
+
# Don't use Array.wrap as we likely have an ActiveTriples::Relation
|
116
|
+
# which defiantly claims to be an Array yet does not behave consistently
|
117
|
+
# with an Array. Hopefully the name_field is not a Date or Time object,
|
118
|
+
# Because that too will be a mess.
|
119
|
+
Array(m.send(name_field)).include?(value)
|
120
|
+
end
|
113
121
|
return match if match
|
114
122
|
end
|
123
|
+
# rubocop:enable Metrics/ParameterLists
|
124
|
+
|
125
|
+
def self.query(q, **kwargs)
|
126
|
+
ActiveFedora::SolrService.query(q, **kwargs)
|
127
|
+
end
|
115
128
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
def create
|
120
|
-
attrs = transform_attributes
|
121
|
-
@object = klass.new
|
122
|
-
object.reindex_extent = Hyrax::Adapters::NestingIndexAdapter::LIMITED_REINDEX if defined?(Hyrax::Adapters::NestingIndexAdapter) && object.respond_to?(:reindex_extent)
|
123
|
-
run_callbacks :save do
|
124
|
-
run_callbacks :create do
|
125
|
-
if klass == Collection
|
126
|
-
create_collection(attrs)
|
127
|
-
elsif klass == FileSet
|
128
|
-
create_file_set(attrs)
|
129
|
-
else
|
130
|
-
create_work(attrs)
|
131
|
-
end
|
132
|
-
end
|
129
|
+
def self.clean!
|
130
|
+
super do
|
131
|
+
ActiveFedora::Cleaner.clean!
|
133
132
|
end
|
134
|
-
object.apply_depositor_metadata(@user) && object.save! if object.depositor.nil?
|
135
|
-
log_created(object)
|
136
133
|
end
|
137
134
|
|
138
|
-
def
|
139
|
-
|
140
|
-
|
135
|
+
def self.solr_name(field_name)
|
136
|
+
if defined?(Hyrax)
|
137
|
+
Hyrax.index_field_mapper.solr_name(field_name)
|
138
|
+
else
|
139
|
+
ActiveFedora.index_field_mapper.solr_name(field_name)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.ordered_file_sets_for(object)
|
144
|
+
object&.ordered_members.to_a.select(&:file_set?)
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.save!(resource:, **)
|
148
|
+
resource.save!
|
141
149
|
end
|
142
150
|
|
143
|
-
def
|
144
|
-
|
145
|
-
Rails.logger.info("#{msg} (#{Array(attributes[work_identifier]).first})")
|
151
|
+
def self.update_index(resources: [])
|
152
|
+
Array(resources).each(&:update_index)
|
146
153
|
end
|
154
|
+
# @!endgroup Class Method Interface
|
155
|
+
##
|
147
156
|
|
148
|
-
def
|
149
|
-
|
150
|
-
Rails
|
157
|
+
def find_by_id
|
158
|
+
return false if attributes[:id].blank?
|
159
|
+
# Rails / Ruby upgrade, we moved from :exists? to :exist? However we want to continue (for a
|
160
|
+
# bit) to support older versions.
|
161
|
+
method_name = klass.respond_to?(:exist?) ? :exist? : :exists?
|
162
|
+
klass.find(attributes[:id]) if klass.send(method_name, attributes[:id])
|
163
|
+
rescue Valkyrie::Persistence::ObjectNotFoundError
|
164
|
+
false
|
165
|
+
end
|
166
|
+
|
167
|
+
def delete(_user)
|
168
|
+
find&.delete
|
151
169
|
end
|
152
170
|
|
153
171
|
private
|
@@ -238,52 +256,6 @@ module Bulkrax
|
|
238
256
|
update == true ? actor.update_content(tmp_file) : actor.create_content(tmp_file, from_url: true)
|
239
257
|
tmp_file.close
|
240
258
|
end
|
241
|
-
|
242
|
-
def clean_attrs(attrs)
|
243
|
-
# avoid the "ArgumentError: Identifier must be a string of size > 0 in order to be treeified" error
|
244
|
-
# when setting object.attributes
|
245
|
-
attrs.delete('id') if attrs['id'].blank?
|
246
|
-
attrs
|
247
|
-
end
|
248
|
-
|
249
|
-
def collection_type(attrs)
|
250
|
-
return attrs if attrs['collection_type_gid'].present?
|
251
|
-
|
252
|
-
attrs['collection_type_gid'] = Hyrax::CollectionType.find_or_create_default_collection_type.to_global_id.to_s
|
253
|
-
attrs
|
254
|
-
end
|
255
|
-
|
256
|
-
# Override if we need to map the attributes from the parser in
|
257
|
-
# a way that is compatible with how the factory needs them.
|
258
|
-
def transform_attributes(update: false)
|
259
|
-
@transform_attributes = attributes.slice(*permitted_attributes)
|
260
|
-
@transform_attributes.merge!(file_attributes(update_files)) if with_files
|
261
|
-
@transform_attributes = remove_blank_hash_values(@transform_attributes) if transformation_removes_blank_hash_values?
|
262
|
-
update ? @transform_attributes.except(:id) : @transform_attributes
|
263
|
-
end
|
264
|
-
|
265
|
-
# Regardless of what the Parser gives us, these are the properties we are prepared to accept.
|
266
|
-
def permitted_attributes
|
267
|
-
klass.properties.keys.map(&:to_sym) + base_permitted_attributes
|
268
|
-
end
|
269
|
-
|
270
|
-
# Return a copy of the given attributes, such that all values that are empty or an array of all
|
271
|
-
# empty values are fully emptied. (See implementation details)
|
272
|
-
#
|
273
|
-
# @param attributes [Hash]
|
274
|
-
# @return [Hash]
|
275
|
-
#
|
276
|
-
# @see https://github.com/emory-libraries/dlp-curate/issues/1973
|
277
|
-
def remove_blank_hash_values(attributes)
|
278
|
-
dupe = attributes.dup
|
279
|
-
dupe.each do |key, values|
|
280
|
-
if values.is_a?(Array) && values.all? { |value| value.is_a?(String) && value.empty? }
|
281
|
-
dupe[key] = []
|
282
|
-
elsif values.is_a?(String) && values.empty?
|
283
|
-
dupe[key] = nil
|
284
|
-
end
|
285
|
-
end
|
286
|
-
dupe
|
287
|
-
end
|
288
259
|
end
|
260
|
+
# rubocop:enable Metrics/ClassLength
|
289
261
|
end
|