-
Notifications
You must be signed in to change notification settings - Fork 137
Expand file tree
/
Copy pathhyrax_helper_behavior.rb
More file actions
507 lines (451 loc) · 20.1 KB
/
Copy pathhyrax_helper_behavior.rb
File metadata and controls
507 lines (451 loc) · 20.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# coding: utf-8
# frozen_string_literal: true
module Hyrax
module HyraxHelperBehavior
include Hyrax::CitationsBehavior
include ERB::Util # provides html_escape
include Hyrax::ContactFormHelper
include Hyrax::TitleHelper
include Hyrax::FileSetHelper
include Hyrax::AbilityHelper
include Hyrax::EmbargoHelper
include Hyrax::LeaseHelper
include Hyrax::CollectionsHelper
include Hyrax::ChartsHelper
include Hyrax::DashboardHelperBehavior
include Hyrax::IiifHelper
include Hyrax::MembershipHelper
include Hyrax::PermissionLevelsHelper
include Hyrax::WorkFormHelper
include Hyrax::WorkflowsHelper
include Hyrax::FacetsHelper
include Hyrax::AttributesHelper
include Hyrax::CompoundFieldsHelper
include Hyrax::PermalinkHelper
include Hyrax::WorksHelper
include Hyrax::FileSetFormHelper
##
# @return [Array<String>] the list of admin sets available for creating works for this user
def available_admin_sets_for_creating_works(ability:)
return [] unless Flipflop.assign_admin_set?
return [] unless ability
source_ids = Hyrax::Collections::PermissionsService.source_ids_for_deposit(ability:, source_type: 'admin_set')
return [] if source_ids.blank?
# This helper only needs indexed display data. Use Solr first to avoid per-id repository fetches,
# but fall back for admin sets that have not been indexed yet.
admin_sets_list = Hyrax::SolrQueryService.new
.with_ids(ids: source_ids)
.with_field_pairs(
field_pairs: {
has_model_ssim: Hyrax::ModelRegistry.admin_set_rdf_representations.join(',')
},
type: 'terms'
)
.solr_documents(rows: source_ids.length, fl: 'id,title_tesim')
.map do |doc|
[Array(doc['title_tesim']).first.to_s, doc['id']]
end
indexed_ids = admin_sets_list.map(&:last)
missing_ids = source_ids.map(&:to_s) - indexed_ids
if missing_ids.present?
admin_sets_list.concat(
Hyrax.query_service.find_many_by_ids(ids: missing_ids).map do |source|
[source.title.first, source.id.to_s]
end
)
end
# Sorts the default admin set to be first, then the rest by title.
admin_sets_list.sort do |a, b|
if Hyrax::AdminSetCreateService.default_admin_set?(id: a[1])
-1
elsif Hyrax::AdminSetCreateService.default_admin_set?(id: b[1])
1
else
a[0] <=> b[0]
end
end
end
##
# @return [Array<String>] the list of all user groups
def available_user_groups(ability:)
return ::User.group_service.role_names if ability.admin?
ability.user_groups
end
# Which translations are available for the user to select
# @return [Hash{String => String}] locale abbreviations as keys and flags as values
def available_translations
{
'de' => 'Deutsch',
'en' => 'English',
'es' => 'Español',
'fr' => 'Français',
'it' => 'Italiano',
'pt-BR' => 'Português do Brasil',
'zh' => '中文'
}
end
delegate :name, :name_full, to: :institution, prefix: :institution
def banner_image
Hyrax.config.banner_image
end
def orcid_label(style_class = '')
safe_join([image_tag('orcid.png', alt: t('hyrax.user_profile.orcid.alt'), class: style_class),
t('hyrax.user_profile.orcid.label')], ' ')
end
def zotero_label(opts = {})
html_class = opts[:html_class] || ''
safe_join([image_tag('zotero.png', alt: t('hyrax.user_profile.zotero.alt'), class: html_class),
t('hyrax.user_profile.zotero.label')], ' ')
end
def zotero_profile_url(zotero_user_id)
"https://www.zotero.org/users/#{zotero_user_id}"
end
# @param [User] user
def render_notifications(user:)
mailbox = UserMailbox.new(user)
unread_notifications = mailbox.unread_count
link_to(hyrax.notifications_path,
'aria-description' => mailbox.label(params[:locale]),
class: 'notify-number nav-link') do
capture do
concat tag.span('', class: 'fa fa-bell', 'aria-label': t('hyrax.admin.sidebar.notifications'))
concat "\n"
concat tag.span(unread_notifications,
class: count_classes_for(unread_notifications))
end
end
end
# @param [ProxyDepositRequest] req
def show_transfer_request_title(req)
if req.deleted_work? || req.canceled?
req.to_s
else
link_to(req.to_s, [main_app, req.work])
end
end
# @param item [Object]
# @param field [String]
# @return [ActiveSupport::SafeBuffer] the html_safe link
def link_to_facet(item, field)
path = main_app.search_catalog_path(search_state.add_facet_params_and_redirect(field, item))
link_to(item, path)
end
# @param values [Array{String}] strings to display
# @param solr_field [String] name of the solr field to link to, without its suffix (:facetable)
# @param empty_message [String] message to display if no values are passed in
# @param separator [String] value to join with
# @return [ActiveSupport::SafeBuffer] the html_safe link
def link_to_facet_list(values, solr_field, empty_message = "No value entered", separator = ", ")
return empty_message if values.blank?
facet_field = solr_field.to_s + "_sim"
safe_join(values.map { |item| link_to_facet(item, facet_field) }, separator)
end
# @param name [String] field name
# @param value [String] field value
# @param label [String] defaults to value
# @param facet_hash [Hash] first argument to Blacklight::SearchState.new
# @return [ActiveSupport::SafeBuffer] the html_safe link
# @see Blacklight::SearchState#initialize
def link_to_field(name, value, label = nil, facet_hash = {})
label ||= value
params = {}
if name.present?
params[:search_field] = name
params[:q] = "\"#{value}\""
end
state = search_state_with_facets(params, facet_hash)
link_to(label, main_app.search_catalog_path(state))
end
## GROUP: helper_methods
# A Blacklight helper_method
# @param [Hash] options from blacklight invocation of helper_method
# @see #index_field_link params
# @return [Date]
def human_readable_date(options)
value = options[:value].first
Date.parse(value).to_formatted_s(:standard)
end
# A Blacklight helper_method
# @param options [Hash{Symbol=>Object}] Blacklight sends :document, :field, :config, :value and whatever else was in options
# @option options [Array{String}] :value
# @option options [Hash] :config including +{:field_name => "my_name"}+
# @option options [Hash] :document
# @option options [Array{String}] :value the strings you might otherwise have passed to this method singly
# @return [ActiveSupport::SafeBuffer] the html_safe link
def index_field_link(options)
raise ArgumentError unless options[:config] && options[:config][:field_name]
name = options[:config][:field_name]
links = options[:value].map { |item| link_to_field(name, item, item) }
safe_join(links, ", ")
end
# A Blacklight helper_method
#
# @example
# link_to_each_facet_field({ value: "Imaging > Object Photography", config: { helper_facet: :document_types_sim }})
# ```html
# <a href=\"/catalog?f%5Bdocument_types_sim%5D%5B%5D=Imaging\">Imaging</a> > <a href=\"/catalog?f%5Bdocument_types_sim%5D%5B%5D=Object+Photography\">Object Photography</a>
# ```
#
# @param options [Hash] from blacklight invocation of helper_method
# @option options [Array<#strip>] :value
# @option options [Hash>] :config Example: { separator: '>', helper_facet: :document_types_sim, output_separator: '::' }
# @return the html_safe facet links separated by the given separator (default: ' > ') to indicate facet hierarchy
#
# @raise KeyError when the options are note properly configured
def link_to_each_facet_field(options)
config = options.fetch(:config)
separator = config.fetch(:separator, ' > ')
output_separator = config.fetch(:output_separator, separator)
facet_search = config.fetch(:helper_facet)
facet_fields = Array.wrap(options.fetch(:value)).first.split(separator).map(&:strip)
facet_links = facet_fields.map do |type|
link_to(type, main_app.search_catalog_path(f: { facet_search => [type] }))
end
safe_join(facet_links, output_separator)
end
# Uses Rails auto_link to add links to fields
#
# @param field [String,Hash] string to format and escape, or a hash as per helper_method
# @option field [SolrDocument] :document
# @option field [String] :field name of the solr field
# @option field [Blacklight::Configuration::IndexField, Blacklight::Configuration::ShowField] :config
# @option field [Array] :value array of values for the field
# @param show_link [Boolean]
# @return [ActiveSupport::SafeBuffer]
def iconify_auto_link(field, show_link = true)
if field.is_a? Hash
options = field[:config].separator_options || {}
text = field[:value].to_sentence(options)
else
text = field
end
# this block is only executed when a link is inserted;
# if we pass text containing no links, it just returns text.
auto_link(html_escape(text)) do |value|
"<span class='fa fa-external-link'></span>#{(' ' + value) if show_link}"
end
end
# Blacklight index helper_method for properties stored as HTML markup
# (`form: { input_type: rich_text }`, `view: { render_as: html }`). Catalog
# search-result columns escape values by default, which dumps the raw tags;
# this renders a clean, truncated plain-text snippet instead. The full
# sanitized HTML still renders on the work show page via HtmlAttributeRenderer.
#
# Works the same in both metadata modes: in flexible mode FlexibleCatalogBehavior
# assigns this helper from the property's `render_as: html`; in non-flexible mode
# declare it on the field directly, e.g.
# config.add_index_field 'my_html_field_tesim', helper_method: :render_html_index_value
#
# Truncation length is author-declarable per property; default is 230.
# flexible: view: { render_as: html, search_results_truncate: 300 } # or false to disable
# non-flexible: config.add_index_field 'x_tesim',
# helper_method: :render_html_index_value, search_results_truncate: 300
#
# @param field [String, Hash{Symbol=>Array}] Blacklight passes a Hash with a
# :value array (and :config); a bare String is also accepted.
# @return [String] truncated plain-text snippet, no markup
def render_html_index_value(field)
raw = field.is_a?(Hash) ? Array(field[:value]).join(' ') : field.to_s
# Decode entities first (e.g. <em> -> <em>) so escaped markup is then
# removed by tag-stripping rather than resurfacing as literal tags; tags ->
# spaces so block boundaries don't glue words together; collapse whitespace.
decoded = CGI.unescapeHTML(raw)
text = strip_tags(decoded.gsub(/<[^>]+>/, ' ')).gsub(/\s+/, ' ').strip
limit = field.is_a?(Hash) ? field[:config].try(:search_results_truncate) : nil
return text if limit == false # `search_results_truncate: false` opts out of truncation
truncate(text, length: html_index_truncate_length(limit), separator: ' ')
end
# Coerce the declared `search_results_truncate` value into a positive integer
# length, falling back to the 230-character default for nil or malformed
# values (e.g. a stray string or non-positive number) rather than producing a
# near-empty snippet. `false` is handled by the caller as an explicit opt-out.
# @api private
DEFAULT_HTML_INDEX_TRUNCATE = 230
def html_index_truncate_length(limit)
length = Integer(limit, exception: false)
length&.positive? ? length : DEFAULT_HTML_INDEX_TRUNCATE
end
# *Sometimes* a Blacklight index field helper_method
# @param [String,User,Hash{Symbol=>Array}] args if a hash, the user_key must be under :value
# @return [ActiveSupport::SafeBuffer] the html_safe link
# rubocop:disable Metrics/CyclomaticComplexity
def link_to_profile(args)
user_or_key = args.is_a?(Hash) ? args[:value].first : args
user = case user_or_key
when User
user_or_key
when String
::User.find_by_user_key(user_or_key)
end
return user_or_key if user.nil?
text = user.respond_to?(:name) ? user.name : user_or_key
user_path_params = [user]
# Oh the antics, because in some cases (stares at
# jobs/content_deposit_event_job_spec.rb) the controller is nil,
# which means calling params will raise a NoMethodError. I also
# suppose it's possible that the controller won't have a set
# params. These precautions should help with that. I'd love to
# use params[:locale] in this case, but the & try syntax doesn't
# work with that. So you get this lovely bit of logic.
locale = controller&.params&.fetch(:locale, nil) || controller&.params&.fetch('locale', nil)
user_path_params << { locale: locale } if locale
link_to text, Hyrax::Engine.routes.url_helpers.user_path(*user_path_params)
end
# rubocop:enable Metrics/CyclomaticComplexity
# A Blacklight index field helper_method
# @param [Hash] options from blacklight helper_method invocation. Maps license URIs to links with labels.
# @return [ActiveSupport::SafeBuffer] license links, html_safe
def license_links(options)
service = Hyrax.config.license_service_class.new
to_sentence(options[:value].map do |right|
label = service.label(right) { right }
Hyrax::AuthorityRenderingHelper.linkable_uri?(right) ? link_to(label, right) : ERB::Util.h(label)
end)
end
# A Blacklight index field helper_method
# @param [Hash] options from blacklight helper_method invocation. Maps rights statement URIs to links with labels.
# @return [ActiveSupport::SafeBuffer] rights statement links, html_safe
def rights_statement_links(options)
service = Hyrax.config.rights_statement_service_class.new
to_sentence(options[:value].map do |right|
label = service.label(right) { right }
Hyrax::AuthorityRenderingHelper.linkable_uri?(right) ? link_to(label, right) : ERB::Util.h(label)
end)
end
# A Blacklight facet field helper_method
# @param [String] value from blacklight helper_method invocation.
# @return [String] the decoded meaning of the boolean field
def suppressed_to_status(value)
case value
when 'false'
t('hyrax.admin.workflows.index.tabs.published')
else
t('hyrax.admin.workflows.index.tabs.under_review')
end
end
def link_to_telephone(user)
return unless user
link_to user.telephone, "wtai://wp/mc;#{user.telephone}" if user.telephone
end
# Only display the current search parameters if the user is not in the dashboard.
# Otherwise, search defaults to the user's works and not all of Hyrax.
def current_search_parameters
return if on_the_dashboard?
params[:q]
end
# @return [String] the appropriate action url for our search form (depending on our current page)
def search_form_action
if on_the_dashboard?
search_action_for_dashboard
else
main_app.search_catalog_path
end
end
def user_display_name_and_key(user_key)
user = ::User.find_by_user_key(user_key)
return user_key if user.nil?
user.respond_to?(:name) ? "#{user.name} (#{user_key})" : user_key
end
# Used by the gallery view
def collection_thumbnail(_document, _image_options = {}, _url_options = {})
tag.span("", class: [Hyrax::ModelIcon.css_class_for(::Collection), "collection-icon-search"])
end
# Returns alt text for a thumbnail image.
# When the document has a custom thumbnail (thumbnail_alt_text indexed), delegates to
# alt_text_for_view to get the specific alt text. Otherwise returns a generic fallback
# (hyrax.sr.thumbnail) indicating a default representative image is shown.
# Apps can override this method to inject content-block lookups or other custom logic.
#
# @param document [SolrDocument]
# @param block_name [String] unused here; provided for API compatibility with overrides
# @return [String]
def thumbnail_alt_text_for(document, block_name: nil) # rubocop:disable Lint/UnusedMethodArgument
return document.alt_text_for_view if document.respond_to?(:thumbnail_alt_text) && document.thumbnail_alt_text.present?
t('hyrax.sr.thumbnail')
end
def collection_title_by_id(id)
solr_docs = controller.blacklight_config.repository.find(id).docs
return nil if solr_docs.empty?
solr_field = solr_docs.first["title_tesim"]
return nil if solr_field.nil?
solr_field.first
rescue Blacklight::Exceptions::RecordNotFound
nil
end
##
# @param [Object] an object that might have a thumbnail
#
# @return [String] a label for the object's thumbnail
def thumbnail_label_for(object:)
object.try(:thumbnail_title).presence ||
""
end
##
# @param value [Object] the thing we'll attempt to cast to a formatted date.
# @param format [String] the `DateTime.strftime` format string
# @return [String]
#
# @see https://github.com/samvera/hyrax/issues/5818
# @see https://ruby-doc.org/core-3.0.1/Time.html#method-i-strftime
def cast_to_date_time_format(value, format:)
return value.strftime(format).to_s if value.respond_to?(:strftime)
(Time.zone.parse(value)&.strftime(format) || value).to_s
rescue ArgumentError, TypeError
value.to_s
end
private
def user_agent
request.user_agent || ''
end
def count_classes_for(unread_count)
classes = unread_count.zero? ? 'invisible badge-secondary' : 'badge-danger'
"count badge #{classes}"
end
def search_action_for_dashboard
case params[:controller]
when "hyrax/my/collections"
hyrax.my_collections_path
when "hyrax/my/shares"
hyrax.dashboard_shares_path
when "hyrax/my/highlights"
hyrax.dashboard_highlights_path
when "hyrax/dashboard/works"
hyrax.dashboard_works_path
when "hyrax/dashboard/collections"
hyrax.dashboard_collections_path
else
# hyrax/my/works controller and default cases.
hyrax.my_works_path
end
end
# rubocop:enable Metrics/MethodLength
# @param [ActionController::Parameters] params first argument for Blacklight::SearchState.new
# @param [Hash] facet(s)
# @return [Hash]
#
# @note Assumes one facet is passed in. If a second facet is passed, then it must be the depositor
# facet used by the Profile page.
def search_state_with_facets(params, facet = {})
state = Blacklight::SearchState.new(params, CatalogController.blacklight_config)
return state.params if facet.none?
# facet should contain one or two values. If it has two values,
# the second is assumed to be the depositor facet.
facet_type = state.add_facet_params(facet.keys.first.to_s + "_sim", facet.values.first)
return facet_type if facet.length == 1
facet_depositor = state.add_facet_params(facet.keys[1].to_s + "_ssim", facet.values[1])
facet_all = Hash.new {}
facet_all["f"] = facet_type["f"].merge(facet_depositor["f"])
facet_all
end
def institution
Institution
end
##
# Returns the generator identification string for HTML meta tags.
# Override this method in your application to customize how your instance identifies itself.
# @return [String] The generator identification (e.g., "Samvera Hyrax 5.x.y")
def hyrax_generator_meta_tag
"Samvera Hyrax #{::Hyrax::VERSION}"
end
end
end