geoblacklight_admin 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +18 -9
- data/Rakefile +83 -47
- data/app/assets/javascripts/geoblacklight_admin/chosen.js +1 -0
- data/app/assets/stylesheets/geoblacklight_admin/_core.scss +24 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_nav.scss +0 -5
- data/app/assets/stylesheets/geoblacklight_admin/modules/_tables.scss +1 -1
- data/app/controllers/admin/admin_controller.rb +16 -0
- data/app/controllers/admin/advanced_search_controller.rb +1 -1
- data/app/controllers/admin/assets_controller.rb +41 -5
- data/app/controllers/admin/bookmarks_controller.rb +14 -2
- data/app/controllers/admin/bulk_actions_controller.rb +31 -0
- data/app/controllers/admin/document_accesses_controller.rb +38 -0
- data/app/controllers/admin/document_assets_controller.rb +46 -9
- data/app/controllers/admin/document_distributions_controller.rb +172 -0
- data/app/controllers/admin/documents_controller.rb +41 -55
- data/app/controllers/admin/elements_controller.rb +22 -0
- data/app/controllers/admin/form_elements_controller.rb +31 -0
- data/app/controllers/admin/import_documents_controller.rb +11 -1
- data/app/controllers/admin/imports_controller.rb +32 -2
- data/app/controllers/admin/mappings_controller.rb +15 -0
- data/app/controllers/admin/notifications_controller.rb +27 -0
- data/app/controllers/admin/reference_types_controller.rb +106 -0
- data/app/controllers/admin/search_controller.rb +7 -0
- data/app/controllers/admin/users_controller.rb +10 -0
- data/app/helpers/asset_helper.rb +6 -0
- data/app/helpers/bulk_actions_helper.rb +9 -0
- data/app/helpers/document_helper.rb +36 -0
- data/app/helpers/geoblacklight_admin_helper.rb +88 -8
- data/app/helpers/mappings_helper.rb +26 -0
- data/app/indexers/document_indexer.rb +22 -2
- data/app/javascript/channels/consumer.js +6 -0
- data/app/javascript/channels/export_channel.js +30 -0
- data/app/javascript/channels/index.js +3 -0
- data/app/javascript/controllers/results_controller.js +14 -0
- data/app/javascript/index.js +8 -2
- data/app/jobs/export_job.rb +35 -8
- data/app/jobs/geoblacklight_admin/delete_thumbnail_job.rb +19 -0
- data/app/jobs/geoblacklight_admin/remove_parent_dct_references_uri_job.rb +16 -0
- data/app/jobs/geoblacklight_admin/set_parent_dct_references_uri_job.rb +17 -0
- data/app/jobs/geoblacklight_admin/store_image_job.rb +22 -0
- data/app/models/asset.rb +20 -0
- data/app/models/bulk_action.rb +2 -1
- data/app/models/document/geom_validator.rb +8 -0
- data/app/models/document/reference.rb +65 -65
- data/app/models/document.rb +128 -71
- data/app/models/document_distribution.rb +145 -0
- data/app/models/element.rb +2 -0
- data/app/models/geoblacklight_admin/schema.rb +10 -2
- data/app/models/import_document_state_machine.rb +1 -0
- data/app/models/reference_type.rb +40 -0
- data/app/models/user.rb +4 -2
- data/app/services/export_csv_document_distributions_service.rb +61 -0
- data/app/services/geoblacklight_admin/image_service/tms.rb +0 -4
- data/app/services/geoblacklight_admin/image_service.rb +1 -1
- data/app/services/geoblacklight_admin/item_viewer.rb +4 -4
- data/app/views/admin/bulk_actions/show.html.erb +1 -1
- data/app/views/admin/document_accesses/import.html.erb +6 -2
- data/app/views/admin/document_assets/_assets_table.html.erb +49 -0
- data/app/views/admin/document_assets/_form.html.erb +2 -3
- data/app/views/admin/document_assets/index.html.erb +1 -47
- data/app/views/admin/document_distributions/_document_distribution.html.erb +39 -0
- data/app/views/admin/document_distributions/_document_distribution.json.jbuilder +2 -0
- data/app/views/admin/document_distributions/_form.html.erb +34 -0
- data/app/views/admin/document_distributions/destroy_all.html.erb +82 -0
- data/app/views/admin/document_distributions/edit.html.erb +12 -0
- data/app/views/admin/document_distributions/import.html.erb +80 -0
- data/app/views/admin/document_distributions/index.html.erb +143 -0
- data/app/views/admin/document_distributions/index.json.jbuilder +1 -0
- data/app/views/admin/document_distributions/new.html.erb +11 -0
- data/app/views/admin/document_distributions/show.html.erb +10 -0
- data/app/views/admin/document_distributions/show.json.jbuilder +1 -0
- data/app/views/admin/documents/_document.html.erb +1 -3
- data/app/views/admin/documents/_form.html.erb +2 -4
- data/app/views/admin/documents/_form_control.html.erb +5 -2
- data/app/views/admin/documents/_form_nav.html.erb +14 -5
- data/app/views/admin/documents/_form_nav_kithe.html.erb +4 -1
- data/app/views/admin/documents/_json_aardvark.jbuilder +1 -1
- data/app/views/admin/documents/_json_gbl_v1.jbuilder +1 -1
- data/app/views/admin/documents/_result_selected_options.html.erb +5 -2
- data/app/views/admin/documents/admin.html.erb +5 -5
- data/app/views/admin/documents/features/_document_references.html.erb +23 -0
- data/app/views/admin/documents/features/_multiple_download_links.html.erb +29 -26
- data/app/views/admin/ids/fetch.json.jbuilder +0 -2
- data/app/views/admin/ids/index.json.jbuilder +0 -2
- data/app/views/admin/imports/_form.html.erb +1 -1
- data/app/views/admin/imports/show.html.erb +1 -1
- data/app/views/admin/layouts/application.html.erb +4 -2
- data/app/views/admin/reference_types/_form.html.erb +25 -0
- data/app/views/admin/reference_types/_reference_type.html.erb +52 -0
- data/app/views/admin/reference_types/_reference_type.json.jbuilder +2 -0
- data/app/views/admin/reference_types/edit.html.erb +12 -0
- data/app/views/admin/reference_types/index.html.erb +52 -0
- data/app/views/admin/reference_types/index.json.jbuilder +1 -0
- data/app/views/admin/reference_types/new.html.erb +11 -0
- data/app/views/admin/reference_types/show.html.erb +3 -0
- data/app/views/admin/reference_types/show.json.jbuilder +1 -0
- data/app/views/admin/shared/_footer.html.erb +5 -2
- data/app/views/admin/shared/_js_behaviors.html.erb +2 -3
- data/app/views/admin/shared/_navbar.html.erb +9 -2
- data/app/views/admin/users/index.html.erb +0 -1
- data/app/views/catalog/_show_gbl_admin.html.erb +1 -1
- data/config/initializers/defaults.yml +310 -0
- data/config/initializers/rails_config.rb +8 -0
- data/config/locales/documents.en.yml +14 -0
- data/config/routes.rb +30 -5
- data/db/import_references_schema_support.numbers +0 -0
- data/db/migrate/20230316183001_add_geoblacklight_admin_gem.rb +0 -12
- data/db/migrate/20241009200524_create_admin_reference_types.rb +13 -0
- data/db/migrate/20241010161420_create_document_references.rb +14 -0
- data/db/migrate/20241120238823_rename_references_to_distributions.rb +5 -0
- data/db/seeds.rb +5 -0
- data/db/seeds_elements.csv +1 -1
- data/db/seeds_elements.numbers +0 -0
- data/db/seeds_reference_types.csv +29 -0
- data/db/seeds_reference_types.numbers +0 -0
- data/db/structure.sql +1 -38
- data/lib/compose.yml +31 -0
- data/lib/generators/geoblacklight_admin/config_generator.rb +48 -12
- data/lib/generators/geoblacklight_admin/install_generator.rb +8 -0
- data/lib/generators/geoblacklight_admin/templates/config/database.yml +1 -1
- data/lib/generators/geoblacklight_admin/templates/config/initializers/devise.rb +0 -2
- data/lib/generators/geoblacklight_admin/templates/config/initializers/mime_types.rb +1 -0
- data/lib/generators/geoblacklight_admin/templates/demo-app/Dockerfile +31 -0
- data/lib/generators/geoblacklight_admin/templates/demo-app/compose.yml +42 -0
- data/lib/generators/geoblacklight_admin/templates/demo-app/start-server.sh +21 -0
- data/lib/geoblacklight_admin/engine.rb +4 -0
- data/lib/geoblacklight_admin/tasks/distributions.rake +69 -0
- data/lib/geoblacklight_admin/tasks/images.rake +1 -0
- data/lib/geoblacklight_admin/tasks/solr.rake +31 -0
- data/lib/geoblacklight_admin/version.rb +1 -1
- data/lib/geoblacklight_admin.rb +4 -0
- metadata +78 -41
- data/app/javascript/entrypoints/engine.js +0 -8
- data/config/locales/devise_invitable.en.yml +0 -31
- data/lib/generators/geoblacklight_admin/templates/devise/invitations/edit.html.erb +0 -15
- data/lib/generators/geoblacklight_admin/templates/devise/invitations/new.html.erb +0 -15
- data/lib/generators/geoblacklight_admin/templates/devise/mailer/invitation_instructions.html.erb +0 -11
- data/lib/generators/geoblacklight_admin/templates/devise/mailer/invitation_instructions.text.erb +0 -11
@@ -1,7 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# DocumentHelper
|
4
|
+
#
|
5
|
+
# This module provides helper methods for handling document-related
|
6
|
+
# functionalities such as generating publication state badges, creating
|
7
|
+
# localized links, and handling pagination links.
|
4
8
|
module DocumentHelper
|
9
|
+
# Generates a badge for the publication state of a document.
|
10
|
+
#
|
11
|
+
# @param document [Object] the document object containing the publication state
|
12
|
+
# @return [String] HTML link with a span element representing the publication state
|
5
13
|
def publication_state_badge(document)
|
6
14
|
case document.publication_state
|
7
15
|
when "draft"
|
@@ -19,15 +27,27 @@ module DocumentHelper
|
|
19
27
|
end
|
20
28
|
end
|
21
29
|
|
30
|
+
# Localizes a given link by parsing its URI and appending it to a base path.
|
31
|
+
#
|
32
|
+
# @param link [String] the link to be localized
|
33
|
+
# @return [String] the localized link
|
22
34
|
def localize_link(link)
|
23
35
|
uri = URI.parse(link)
|
24
36
|
"/admin/documents?#{uri.query}"
|
25
37
|
end
|
26
38
|
|
39
|
+
# Creates a sort link with a label and localized URL.
|
40
|
+
#
|
41
|
+
# @param link [Hash] a hash containing link attributes and self link
|
42
|
+
# @return [String] HTML link element for sorting
|
27
43
|
def sort_link(link)
|
28
44
|
link_to link["attributes"]["label"], localize_link(link["links"]["self"]), {class: "dropdown-item"}
|
29
45
|
end
|
30
46
|
|
47
|
+
# Processes a link from an API response, determining whether to add or remove a facet.
|
48
|
+
#
|
49
|
+
# @param link [Hash] a hash containing links for adding or removing facets
|
50
|
+
# @return [Hash] a hash with action and link keys
|
31
51
|
def link_from_api(link)
|
32
52
|
# Append facet - Full URI returned
|
33
53
|
uri = URI.parse(link["links"]["self"])
|
@@ -38,6 +58,10 @@ module DocumentHelper
|
|
38
58
|
{action: "remove", link: "/admin/documents?#{uri.query}"}
|
39
59
|
end
|
40
60
|
|
61
|
+
# Generates a link to the previous page in a paginated list.
|
62
|
+
#
|
63
|
+
# @param links [Hash] a hash containing pagination links
|
64
|
+
# @return [String] HTML link element for the previous page
|
41
65
|
def previous_link(links)
|
42
66
|
if links["prev"]
|
43
67
|
link_to "Previous", localize_link(links["prev"]), {class: "btn btn-outline-primary btn-sm"}
|
@@ -46,6 +70,10 @@ module DocumentHelper
|
|
46
70
|
end
|
47
71
|
end
|
48
72
|
|
73
|
+
# Generates a link to the next page in a paginated list.
|
74
|
+
#
|
75
|
+
# @param links [Hash] a hash containing pagination links
|
76
|
+
# @return [String] HTML link element for the next page
|
49
77
|
def next_link(links)
|
50
78
|
if links["next"]
|
51
79
|
link_to "Next", localize_link(links["next"]), {class: "btn btn-outline-primary btn-sm"}
|
@@ -54,10 +82,18 @@ module DocumentHelper
|
|
54
82
|
end
|
55
83
|
end
|
56
84
|
|
85
|
+
# Constructs a link to a document's page in the Blacklight catalog.
|
86
|
+
#
|
87
|
+
# @param document [Object] the document object
|
88
|
+
# @return [String] the URL to the document's Blacklight catalog page
|
57
89
|
def blacklight_link(document)
|
58
90
|
"#{BLACKLIGHT_URL}/catalog/#{document.friendlier_id}"
|
59
91
|
end
|
60
92
|
|
93
|
+
# Determines if a document's thumbnail should be rendered.
|
94
|
+
#
|
95
|
+
# @param document [Object] the document object
|
96
|
+
# @return [Boolean] true if the thumbnail should be rendered, false otherwise
|
61
97
|
def thumb_to_render?(document)
|
62
98
|
document&.thumbnail&.file_url&.present? && document&.thumbnail&.file_derivatives&.present?
|
63
99
|
end
|
@@ -1,13 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# GeoblacklightAdminHelper
|
4
|
+
#
|
5
|
+
# This module provides helper methods for the GeoBlacklight admin interface.
|
6
|
+
# It includes methods for handling JSON data, generating paths, formatting
|
7
|
+
# flash messages, and more.
|
4
8
|
module GeoblacklightAdminHelper
|
5
9
|
# @TODO:
|
6
10
|
# Cannot generate app if uncommented...
|
7
11
|
# Uncomment after app is generated to fix view errors
|
8
12
|
include ::Pagy::Frontend if defined?(Pagy)
|
9
13
|
|
10
|
-
#
|
14
|
+
# Removes blank values from JSON data.
|
15
|
+
#
|
16
|
+
# @param value [String, Array] the value to check for presence
|
17
|
+
# @return [String, Array, nil] the original value if present, otherwise nil
|
11
18
|
def no_json_blanks(value)
|
12
19
|
case value
|
13
20
|
when String
|
@@ -17,17 +24,21 @@ module GeoblacklightAdminHelper
|
|
17
24
|
end
|
18
25
|
end
|
19
26
|
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
27
|
+
# Generates a search path for the QA (questioning_authority) gem.
|
28
|
+
#
|
29
|
+
# @param vocab [String] the vocabulary to search
|
30
|
+
# @param subauthority [String, nil] the subauthority to search
|
31
|
+
# @return [String] the generated search path
|
23
32
|
def qa_search_vocab_path(vocab, subauthority = nil)
|
24
33
|
path = "/authorities/search/#{CGI.escape vocab}"
|
25
|
-
|
26
34
|
path += "/#{CGI.escape subauthority}" if subauthority
|
27
|
-
|
28
35
|
path
|
29
36
|
end
|
30
37
|
|
38
|
+
# Maps flash message levels to CSS classes.
|
39
|
+
#
|
40
|
+
# @param level [String] the flash message level
|
41
|
+
# @return [String] the corresponding CSS class
|
31
42
|
def flash_class(level)
|
32
43
|
alerts = {
|
33
44
|
"notice" => "alert alert-info",
|
@@ -38,6 +49,9 @@ module GeoblacklightAdminHelper
|
|
38
49
|
alerts[level]
|
39
50
|
end
|
40
51
|
|
52
|
+
# Provides a mapping of institution codes to institution names.
|
53
|
+
#
|
54
|
+
# @return [Hash] a hash mapping institution codes to names
|
41
55
|
def b1g_institution_codes
|
42
56
|
{
|
43
57
|
"01" => "Indiana University",
|
@@ -58,11 +72,17 @@ module GeoblacklightAdminHelper
|
|
58
72
|
}
|
59
73
|
end
|
60
74
|
|
75
|
+
# Generates an HTML badge for bookmarks.
|
76
|
+
#
|
77
|
+
# @return [String] the HTML string for the bookmarks badge
|
61
78
|
def bookmarks_badge
|
62
79
|
bookmarks_classes = ["badge", "badge-dark"]
|
63
80
|
"<span class='#{bookmarks_classes.join(" ")}' id='bookmarks-count'>#{current_user.bookmarks.size}</span>"
|
64
81
|
end
|
65
82
|
|
83
|
+
# Generates an HTML badge for notifications.
|
84
|
+
#
|
85
|
+
# @return [String] the HTML string for the notifications badge
|
66
86
|
def notifications_badge
|
67
87
|
notifications_classes = ["badge"]
|
68
88
|
notifications_classes << "badge-dark" if current_user.notifications.unread.empty?
|
@@ -70,7 +90,10 @@ module GeoblacklightAdminHelper
|
|
70
90
|
"<span class='#{notifications_classes.join(" ")}' id='notification-count'>#{current_user.notifications.unread.size}</span>"
|
71
91
|
end
|
72
92
|
|
73
|
-
#
|
93
|
+
# Converts parameters into hidden form fields.
|
94
|
+
#
|
95
|
+
# @param params [Hash] the parameters to convert
|
96
|
+
# @return [String] the HTML string of hidden fields
|
74
97
|
def params_as_hidden_fields(params)
|
75
98
|
hidden_fields = []
|
76
99
|
flatten_hash(params).each do |name, value|
|
@@ -83,6 +106,12 @@ module GeoblacklightAdminHelper
|
|
83
106
|
safe_join(hidden_fields, "\n")
|
84
107
|
end
|
85
108
|
|
109
|
+
# Flattens a nested hash into a single-level hash with keys representing the
|
110
|
+
# nested structure.
|
111
|
+
#
|
112
|
+
# @param hash [Hash] the hash to flatten
|
113
|
+
# @param ancestor_names [Array] the ancestor keys for nested hashes
|
114
|
+
# @return [Hash] the flattened hash
|
86
115
|
def flatten_hash(hash, ancestor_names = [])
|
87
116
|
flat_hash = {}
|
88
117
|
hash.each do |k, v|
|
@@ -100,6 +129,10 @@ module GeoblacklightAdminHelper
|
|
100
129
|
flat_hash
|
101
130
|
end
|
102
131
|
|
132
|
+
# Generates a key for a flattened hash from an array of names.
|
133
|
+
#
|
134
|
+
# @param names [Array] the array of names
|
135
|
+
# @return [String] the generated key
|
103
136
|
def flat_hash_key(names)
|
104
137
|
names = Array.new(names)
|
105
138
|
name = names.shift.to_s.dup
|
@@ -109,6 +142,10 @@ module GeoblacklightAdminHelper
|
|
109
142
|
name
|
110
143
|
end
|
111
144
|
|
145
|
+
# Maps a character to a CSS class for diff highlighting.
|
146
|
+
#
|
147
|
+
# @param char [String] the character representing a diff operation
|
148
|
+
# @return [String] the corresponding CSS class
|
112
149
|
def diff_class(char)
|
113
150
|
case char
|
114
151
|
when "~"
|
@@ -122,6 +159,10 @@ module GeoblacklightAdminHelper
|
|
122
159
|
end
|
123
160
|
end
|
124
161
|
|
162
|
+
# Generates a link to the admin import page for a given import.
|
163
|
+
#
|
164
|
+
# @param import [Object] the import object
|
165
|
+
# @return [String] the HTML link to the admin import page
|
125
166
|
def link_to_admin_import(import)
|
126
167
|
path = admin_documents_path(
|
127
168
|
{
|
@@ -132,6 +173,12 @@ module GeoblacklightAdminHelper
|
|
132
173
|
link_to import.name, path
|
133
174
|
end
|
134
175
|
|
176
|
+
# Generates a link to the GeoBlacklight import page with optional state.
|
177
|
+
#
|
178
|
+
# @param label [String] the link label
|
179
|
+
# @param import [Object] the import object
|
180
|
+
# @param state [Boolean] the publication state
|
181
|
+
# @return [String] the HTML link to the GeoBlacklight import page
|
135
182
|
def link_to_gbl_import(label, import, state = false)
|
136
183
|
path = if state
|
137
184
|
blacklight_path(
|
@@ -152,7 +199,40 @@ module GeoblacklightAdminHelper
|
|
152
199
|
link_to(label, path)
|
153
200
|
end
|
154
201
|
|
202
|
+
# Generates options for asset DCT references.
|
203
|
+
#
|
204
|
+
# @return [String] the escaped JavaScript string of options
|
155
205
|
def assets_dct_references_options
|
156
|
-
escape_javascript(options_for_select(I18n.t("activemodel.
|
206
|
+
escape_javascript(options_for_select(I18n.t("activemodel.asset_enum_values.document/reference.category").invert.sort.insert(0, ["Choose Reference Type", nil]))).to_s
|
207
|
+
end
|
208
|
+
|
209
|
+
# Determines if a document's thumbnail should be rendered.
|
210
|
+
#
|
211
|
+
# @param document [Object] the document object
|
212
|
+
# @return [Boolean] true if the thumbnail should be rendered, false otherwise
|
213
|
+
def thumb_to_render?(document)
|
214
|
+
if document&.thumbnail&.file_url&.present? && document&.thumbnail&.file_derivatives&.present?
|
215
|
+
true
|
216
|
+
elsif document&.document_assets&.any?
|
217
|
+
document.document_assets.any? do |asset|
|
218
|
+
asset.file_derivatives&.key?(:thumb_standard_2X)
|
219
|
+
end
|
220
|
+
else
|
221
|
+
false
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Returns the URL of the thumbnail to render for a document.
|
226
|
+
#
|
227
|
+
# @param document [Object] the document object
|
228
|
+
# @return [String] the URL of the thumbnail to render
|
229
|
+
def thumbnail_to_render(document)
|
230
|
+
if document&.thumbnail&.file_url&.present? && document&.thumbnail&.file_derivatives&.present?
|
231
|
+
document.thumbnail.file_url(:thumb_standard_2X)
|
232
|
+
elsif document&.document_assets&.any?
|
233
|
+
document.document_assets.find do |asset|
|
234
|
+
asset.file_derivatives&.key?(:thumb_standard_2X)
|
235
|
+
end&.file_url(:thumb_standard_2X)
|
236
|
+
end
|
157
237
|
end
|
158
238
|
end
|
@@ -1,7 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# MappingsHelper
|
4
|
+
#
|
5
|
+
# This module provides helper methods for handling mappings in the application.
|
6
|
+
# It includes methods to generate attribute collections and provide mapping
|
7
|
+
# and delimiter suggestions based on import configurations.
|
4
8
|
module MappingsHelper
|
9
|
+
# Returns a collection of attributes that can be used for mapping.
|
10
|
+
#
|
11
|
+
# The collection is generated from importable elements, sorted, and includes
|
12
|
+
# additional options for an empty string and "Discard".
|
13
|
+
#
|
14
|
+
# @return [Array<String>] a sorted array of attribute names with additional options.
|
5
15
|
def attribute_collection
|
6
16
|
attrs = Element.importable.map(&:solr_field).sort
|
7
17
|
attrs.prepend("")
|
@@ -9,6 +19,14 @@ module MappingsHelper
|
|
9
19
|
attrs
|
10
20
|
end
|
11
21
|
|
22
|
+
# Provides a mapping suggestion for a given header based on the import configuration.
|
23
|
+
#
|
24
|
+
# Checks if the header is included in the import's mapping configuration and returns
|
25
|
+
# the destination if available.
|
26
|
+
#
|
27
|
+
# @param import [Object] the import object containing mapping configurations.
|
28
|
+
# @param header [String] the header for which the mapping suggestion is needed.
|
29
|
+
# @return [String, false] the destination mapping if available, otherwise false.
|
12
30
|
def mapping_suggestion(import, header)
|
13
31
|
if import.mapping_configuration.include?(header.to_sym)
|
14
32
|
import.mapping_configuration[header.to_sym][:destination]
|
@@ -17,6 +35,14 @@ module MappingsHelper
|
|
17
35
|
end
|
18
36
|
end
|
19
37
|
|
38
|
+
# Provides a delimiter suggestion for a given header based on the import configuration.
|
39
|
+
#
|
40
|
+
# Checks if the header is included in the import's mapping configuration and returns
|
41
|
+
# the delimiter if available.
|
42
|
+
#
|
43
|
+
# @param import [Object] the import object containing mapping configurations.
|
44
|
+
# @param header [String] the header for which the delimiter suggestion is needed.
|
45
|
+
# @return [String, false] the delimiter if available, otherwise false.
|
20
46
|
def delimiter_suggestion(import, header)
|
21
47
|
if import.mapping_configuration.include?(header.to_sym)
|
22
48
|
import.mapping_configuration[header.to_sym][:delimited]
|
@@ -1,6 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Solr indexing for our document class. Still a work in progress.
|
4
|
+
#
|
5
|
+
# The DocumentIndexer class is responsible for configuring how documents
|
6
|
+
# are indexed into Solr. It uses the Kithe::Indexer framework to map
|
7
|
+
# document attributes to Solr fields.
|
8
|
+
#
|
9
|
+
# The configuration block defines various fields that are indexed, including
|
10
|
+
# fields specific to GeoBlacklight and custom fields defined via the Element model.
|
11
|
+
#
|
12
|
+
# Fields:
|
13
|
+
# - model_pk_ssi: The primary key of the model, extracted from the object's ID.
|
14
|
+
# - gbl_mdVersion_s: A static version string for GeoBlacklight.
|
15
|
+
# - gbl_mdModified_dt: The modification date of the metadata.
|
16
|
+
# - date_created_dtsi: The creation date of the record, in UTC ISO8601 format.
|
17
|
+
# - date_modified_dtsi: The last modification date of the record, in UTC ISO8601 format.
|
18
|
+
# - b1g_geom_import_id_ssi: The import ID for GeoBlacklight administration.
|
19
|
+
#
|
20
|
+
# If the "elements" table exists, additional fields are indexed based on
|
21
|
+
# the Element model's configuration.
|
4
22
|
class DocumentIndexer < Kithe::Indexer
|
5
23
|
configure do
|
6
24
|
# Kithe
|
@@ -12,8 +30,10 @@ class DocumentIndexer < Kithe::Indexer
|
|
12
30
|
# to_field 'geomg_id_s', obj_extract('friendlier_id') # the actual db pk, a UUID
|
13
31
|
|
14
32
|
# Define `to_field`(s) via Element
|
15
|
-
|
16
|
-
|
33
|
+
if ActiveRecord::Base.connection.table_exists?("elements")
|
34
|
+
Element.indexable.each do |elm|
|
35
|
+
to_field elm.solr_field, obj_extract(elm.index_value)
|
36
|
+
end
|
17
37
|
end
|
18
38
|
|
19
39
|
to_field "gbl_mdModified_dt", obj_extract("gbl_mdModified_dt")
|
@@ -0,0 +1,6 @@
|
|
1
|
+
// Action Cable provides the framework to deal with WebSockets in Rails.
|
2
|
+
// You can generate new channels where WebSocket features live using the `bin/rails generate channel` command.
|
3
|
+
|
4
|
+
import { createConsumer } from "@rails/actioncable"
|
5
|
+
|
6
|
+
export default createConsumer()
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import consumer from "./consumer"
|
2
|
+
|
3
|
+
consumer.subscriptions.create({ channel: "ExportChannel" }, {
|
4
|
+
connected() {
|
5
|
+
// Called when the subscription is ready for use on the server
|
6
|
+
console.log("GBL Admin - ExportChannel connected");
|
7
|
+
},
|
8
|
+
|
9
|
+
disconnected() {
|
10
|
+
// Called when the subscription has been terminated by the server
|
11
|
+
console.log("GBL Admin - ExportChannel disconnected");
|
12
|
+
},
|
13
|
+
|
14
|
+
received(data) {
|
15
|
+
console.log('GBL Admin - ExportChannel received!');
|
16
|
+
console.log(data);
|
17
|
+
|
18
|
+
if (data['progress']) {
|
19
|
+
console.log(data['progress']);
|
20
|
+
}
|
21
|
+
|
22
|
+
if (data['actions']) {
|
23
|
+
for (let index = 0; index < data.actions.length; ++index) {
|
24
|
+
var fnstring = data.actions[index].method;
|
25
|
+
var fn = window["GBLADMIN"][fnstring];
|
26
|
+
if (typeof fn === "function") fn(data.actions[index].payload);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
});
|
@@ -12,6 +12,7 @@ import { Controller } from "stimulus"
|
|
12
12
|
export default class extends Controller {
|
13
13
|
|
14
14
|
connect() {
|
15
|
+
console.log("GBL Admin - ResultsController connected");
|
15
16
|
}
|
16
17
|
|
17
18
|
checkedState(checked, selector='input[type=checkbox]') {
|
@@ -228,6 +229,7 @@ export default class extends Controller {
|
|
228
229
|
}
|
229
230
|
|
230
231
|
exportCsvDocumentDownloads() {
|
232
|
+
console.log('Export - CsvDocumentDownloads')
|
231
233
|
var scope = this.checkSelectionScope();
|
232
234
|
var el = document.querySelector('#result-selected-options');
|
233
235
|
if(scope === 'pageset') {
|
@@ -238,6 +240,7 @@ export default class extends Controller {
|
|
238
240
|
}
|
239
241
|
|
240
242
|
exportCsvDocumentAccessLinks() {
|
243
|
+
console.log('Export - CsvDocumentAccessLinks')
|
241
244
|
var scope = this.checkSelectionScope();
|
242
245
|
var el = document.querySelector('#result-selected-options');
|
243
246
|
if(scope === 'pageset') {
|
@@ -247,6 +250,17 @@ export default class extends Controller {
|
|
247
250
|
}
|
248
251
|
}
|
249
252
|
|
253
|
+
exportCsvDocumentDistributions() {
|
254
|
+
console.log('Export - CsvDocumentDistributions')
|
255
|
+
var scope = this.checkSelectionScope();
|
256
|
+
var el = document.querySelector('#result-selected-options');
|
257
|
+
if(scope === 'pageset') {
|
258
|
+
window.location = el.dataset.pageset + "&format=csv_document_distributions"
|
259
|
+
} else {
|
260
|
+
window.location = el.dataset.resultset + "&format=csv_document_distributions"
|
261
|
+
}
|
262
|
+
}
|
263
|
+
|
250
264
|
bulkAction() {
|
251
265
|
var scope = this.checkSelectionScope();
|
252
266
|
var el = document.querySelector('#result-selected-options');
|
data/app/javascript/index.js
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
console.log('Vite ⚡️ Rails - GBL Admin')
|
2
2
|
|
3
|
-
// Stimulus
|
3
|
+
// Import Stimulus and controllers
|
4
4
|
import { Application } from '@hotwired/stimulus'
|
5
5
|
import ResultsController from "./controllers/results_controller"
|
6
6
|
|
7
|
+
// Initialize Stimulus
|
7
8
|
window.Stimulus = Application.start()
|
8
|
-
|
9
|
+
|
10
|
+
// Register controllers
|
11
|
+
Stimulus.register("results", ResultsController)
|
12
|
+
|
13
|
+
// Import channels
|
14
|
+
import '../channels';
|
data/app/jobs/export_job.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "csv"
|
4
|
+
require "zip"
|
4
5
|
|
5
6
|
# ExportJob
|
6
7
|
class ExportJob < ApplicationJob
|
@@ -23,27 +24,53 @@ class ExportJob < ApplicationJob
|
|
23
24
|
logger.debug("Document Ids: #{document_ids}")
|
24
25
|
|
25
26
|
# Send progress
|
26
|
-
|
27
|
+
file_content_documents = export_service.call(document_ids)
|
28
|
+
file_content_document_distributions = ExportCsvDocumentDistributionsService.call(document_ids)
|
27
29
|
|
28
|
-
# Write into tempfile
|
29
|
-
@
|
30
|
+
# Write Documents into tempfile
|
31
|
+
@tempfile_documents = Tempfile.new(["documents-#{Time.zone.today}", ".csv"]).tap do |file|
|
30
32
|
CSV.open(file, "wb") do |csv|
|
31
|
-
|
33
|
+
file_content_documents.each do |row|
|
32
34
|
csv << row
|
33
35
|
end
|
34
36
|
end
|
37
|
+
logger.debug("Tempfile Documents Path: #{file.path}")
|
38
|
+
logger.debug("Tempfile Documents Size: #{File.size(file.path)} bytes")
|
35
39
|
end
|
36
40
|
|
41
|
+
# Write DocumentDistributions into tempfile
|
42
|
+
@tempfile_document_distributions = Tempfile.new(["document-distributions-#{Time.zone.today}", ".csv"]).tap do |file|
|
43
|
+
CSV.open(file, "wb") do |csv|
|
44
|
+
file_content_document_distributions.each do |row|
|
45
|
+
csv << row
|
46
|
+
end
|
47
|
+
end
|
48
|
+
logger.debug("Tempfile Document Distributions Path: #{file.path}")
|
49
|
+
logger.debug("Tempfile Document Distributions Size: #{File.size(file.path)} bytes")
|
50
|
+
end
|
51
|
+
|
52
|
+
# Create a zip file containing both tempfiles
|
53
|
+
zipfile_name = "export-#{Time.zone.today}.zip"
|
54
|
+
tmp_dir = Rails.root.join("tmp")
|
55
|
+
@tempfile_zip = Tempfile.new([zipfile_name, ".zip"], tmp_dir)
|
56
|
+
|
57
|
+
Zip::File.open(@tempfile_zip.path, Zip::File::CREATE) do |zipfile|
|
58
|
+
zipfile.add("documents.csv", @tempfile_documents.path)
|
59
|
+
zipfile.add("document-distributions.csv", @tempfile_document_distributions.path)
|
60
|
+
end
|
61
|
+
logger.debug("Zipfile Path: #{@tempfile_zip.path}")
|
62
|
+
logger.debug("Zipfile Size: #{File.size(@tempfile_zip.path)} bytes")
|
63
|
+
|
37
64
|
# Create notification
|
38
65
|
# Message: "Download Type|Row Count|Button Label"
|
39
|
-
notification = ExportNotification.with(message: "
|
66
|
+
notification = ExportNotification.with(message: "ZIP (#{export_service.short_name})|#{ActionController::Base.helpers.number_with_delimiter(file_content_documents.size - 1)} rows|ZIP")
|
40
67
|
|
41
68
|
# Deliver notification
|
42
69
|
notification.deliver(current_user)
|
43
70
|
|
44
|
-
# Attach
|
45
|
-
notification.record.file.attach(io: @
|
46
|
-
content_type: "
|
71
|
+
# Attach ZIP file (can only attach after persisted)
|
72
|
+
notification.record.file.attach(io: File.open(@tempfile_zip), filename: zipfile_name,
|
73
|
+
content_type: "application/zip")
|
47
74
|
|
48
75
|
# Update UI
|
49
76
|
ActionCable.server.broadcast("export_channel", {
|
@@ -1,11 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
##
|
4
|
+
# Module for GeoblacklightAdmin related jobs.
|
3
5
|
module GeoblacklightAdmin
|
6
|
+
##
|
7
|
+
# A job to delete the thumbnail associated with a Solr document.
|
8
|
+
#
|
9
|
+
# This job is enqueued with a priority queue and can optionally handle
|
10
|
+
# a bad document ID to transition its state.
|
4
11
|
class DeleteThumbnailJob < ApplicationJob
|
12
|
+
##
|
13
|
+
# Determines the queue to use based on the last argument.
|
5
14
|
queue_as do
|
6
15
|
arguments.last
|
7
16
|
end
|
8
17
|
|
18
|
+
##
|
19
|
+
# Performs the job to delete a thumbnail.
|
20
|
+
#
|
21
|
+
# @param solr_document_id [String] the ID of the Solr document
|
22
|
+
# @param bad_id [String, nil] optional ID of a bad document to transition
|
23
|
+
# @param queue [Symbol] the queue to use, defaults to :priority
|
24
|
+
#
|
25
|
+
# If the document has a thumbnail, it will be destroyed.
|
26
|
+
# If a bad_id is provided, it will transition the state of the
|
27
|
+
# BulkActionDocument to :success.
|
9
28
|
def perform(solr_document_id, bad_id = nil, queue = :priority)
|
10
29
|
document = Document.find_by_friendlier_id(solr_document_id)
|
11
30
|
if document.thumbnail.present?
|
@@ -1,9 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
##
|
4
|
+
# Module for GeoblacklightAdmin jobs.
|
3
5
|
module GeoblacklightAdmin
|
6
|
+
##
|
7
|
+
# Job to remove a specific DCT references URI from the parent asset.
|
8
|
+
#
|
9
|
+
# This job is queued with a priority level and is responsible for
|
10
|
+
# removing a DCT references URI from the parent of a given asset.
|
4
11
|
class RemoveParentDctReferencesUriJob < ApplicationJob
|
5
12
|
queue_as :priority
|
6
13
|
|
14
|
+
##
|
15
|
+
# Performs the job of removing the DCT references URI from the parent asset.
|
16
|
+
#
|
17
|
+
# This method checks if the asset has a `dct_references_uri_key` present.
|
18
|
+
# If present, it deletes the URI from the parent's `dct_references_s` array
|
19
|
+
# if it matches the asset's full file URL, and then saves the parent asset.
|
20
|
+
#
|
21
|
+
# @param asset [Object] The asset whose parent's DCT references URI is to be removed.
|
22
|
+
# @raise [StandardError] Logs an error if an exception occurs during the process.
|
7
23
|
def perform(asset)
|
8
24
|
if asset.dct_references_uri_key.present?
|
9
25
|
asset.parent.dct_references_s.delete_if { |i| i.value == asset.full_file_url }
|
@@ -1,9 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
##
|
4
|
+
# Module for GeoblacklightAdmin jobs.
|
3
5
|
module GeoblacklightAdmin
|
6
|
+
##
|
7
|
+
# Job to set the parent DCT references URI for a given asset.
|
8
|
+
#
|
9
|
+
# This job is responsible for creating a new reference for the asset's
|
10
|
+
# DCT references URI key and appending it to the parent asset's
|
11
|
+
# `dct_references_s` array. It then saves the parent asset.
|
12
|
+
#
|
13
|
+
# If an error occurs during the process, it logs the error message.
|
4
14
|
class SetParentDctReferencesUriJob < ApplicationJob
|
15
|
+
# Sets the queue for this job to :priority
|
5
16
|
queue_as :priority
|
6
17
|
|
18
|
+
##
|
19
|
+
# Performs the job to set the parent DCT references URI.
|
20
|
+
#
|
21
|
+
# @param asset [Object] The asset for which the DCT references URI is to be set.
|
22
|
+
#
|
23
|
+
# @return [void]
|
7
24
|
def perform(asset)
|
8
25
|
if asset.dct_references_uri_key.present?
|
9
26
|
reference = Document::Reference.new
|
@@ -1,11 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
##
|
4
|
+
# Module for GeoblacklightAdmin jobs.
|
3
5
|
module GeoblacklightAdmin
|
6
|
+
##
|
7
|
+
# StoreImageJob is responsible for handling the storage of images
|
8
|
+
# associated with a Solr document. It manages the lifecycle of the
|
9
|
+
# image storage process, including state transitions and error handling.
|
4
10
|
class StoreImageJob < ApplicationJob
|
11
|
+
##
|
12
|
+
# Sets the queue for the job based on the last argument provided.
|
5
13
|
queue_as do
|
6
14
|
arguments.last
|
7
15
|
end
|
8
16
|
|
17
|
+
##
|
18
|
+
# Performs the job to store an image for a given Solr document.
|
19
|
+
#
|
20
|
+
# @param solr_document_id [String] the ID of the Solr document
|
21
|
+
# @param bad_id [String, nil] optional ID for a BulkActionDocument
|
22
|
+
# @param queue [Symbol] the queue to use for the job, defaults to :default
|
23
|
+
#
|
24
|
+
# This method:
|
25
|
+
# - Finds the document by its friendlier ID.
|
26
|
+
# - Deletes any existing thumbnail.
|
27
|
+
# - Transitions the document's thumbnail state to 'queued'.
|
28
|
+
# - Waits for a random period to ensure polite crawling.
|
29
|
+
# - Stores the image using the ImageService.
|
30
|
+
# - Transitions the BulkActionDocument state to 'success' if a bad_id is provided.
|
9
31
|
def perform(solr_document_id, bad_id = nil, queue = :default)
|
10
32
|
# Find the document
|
11
33
|
document = Document.find_by_friendlier_id(solr_document_id)
|