hyrax 3.0.0.pre.rc3 → 3.0.0.pre.rc4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dassie/config/locales/hyrax.es.yml +3 -1
- data/.dassie/config/tinymce.yml +3 -2
- data/.regen +1 -1
- data/CONTAINERS.md +12 -0
- data/Dockerfile +10 -8
- data/README.md +5 -0
- data/app/assets/javascripts/hyrax.js +1 -0
- data/app/assets/javascripts/hyrax/app.js.erb +0 -10
- data/app/assets/stylesheets/hyrax/_catalog.scss +1 -0
- data/app/assets/stylesheets/hyrax/_representative-media.scss +2 -2
- data/app/controllers/hyrax/file_sets_controller.rb +55 -4
- data/app/controllers/hyrax/my_controller.rb +1 -1
- data/app/forms/hyrax/forms/file_manager_form.rb +9 -2
- data/app/helpers/hyrax/ability_helper.rb +2 -1
- data/app/helpers/hyrax/contact_form_helper.rb +1 -2
- data/app/helpers/hyrax/hyrax_helper_behavior.rb +16 -1
- data/app/helpers/hyrax/workflows_helper.rb +42 -0
- data/app/models/concerns/hyrax/ability.rb +2 -2
- data/app/models/hyrax/contact_form.rb +1 -7
- data/app/models/hyrax/permission_template.rb +14 -0
- data/app/models/proxy_deposit_request.rb +8 -4
- data/app/presenters/hyrax/file_set_presenter.rb +10 -0
- data/app/presenters/hyrax/google_scholar_presenter.rb +86 -0
- data/app/presenters/hyrax/work_show_presenter.rb +1 -1
- data/app/search_builders/hyrax/dashboard/nested_collections_search_builder.rb +0 -24
- data/app/services/hyrax/admin_set_member_service.rb +1 -1
- data/app/services/hyrax/change_content_depositor_service.rb +29 -7
- data/app/services/hyrax/collections/permissions_service.rb +5 -6
- data/app/services/hyrax/custom_queries/find_ids_by_model.rb +37 -0
- data/app/services/hyrax/edit_permissions_service.rb +201 -0
- data/app/services/hyrax/identifier/dispatcher.rb +1 -1
- data/app/services/hyrax/listeners/active_fedora_acl_index_listener.rb +26 -0
- data/app/services/hyrax/listeners/metadata_index_listener.rb +5 -1
- data/app/views/catalog/_index_header_list_collection.html.erb +1 -1
- data/app/views/catalog/_index_header_list_default.html.erb +1 -1
- data/app/views/catalog/_index_list_default.html.erb +1 -1
- data/app/views/hyrax/admin/admin_sets/_show_document_list_row.html.erb +1 -1
- data/app/views/hyrax/base/_currently_shared.html.erb +46 -0
- data/app/views/hyrax/base/_form_share.html.erb +5 -33
- data/app/views/hyrax/base/_show_actions.html.erb +16 -14
- data/app/views/hyrax/base/unavailable.html.erb +1 -1
- data/app/views/hyrax/batch_edits/_currently_shared.html.erb +8 -0
- data/app/views/hyrax/collections/_show_document_list_row.html.erb +1 -1
- data/app/views/hyrax/collections/_sort_and_per_page.html.erb +1 -1
- data/app/views/hyrax/contact_form/new.html.erb +3 -1
- data/app/views/hyrax/dashboard/collections/_show_document_list_menu.html.erb +1 -1
- data/app/views/hyrax/dashboard/collections/_show_document_list_row.html.erb +1 -1
- data/app/views/hyrax/dashboard/collections/_sort_and_per_page.html.erb +1 -1
- data/app/views/hyrax/file_sets/_actions.html.erb +1 -2
- data/app/views/hyrax/file_sets/_permission_form.html.erb +2 -31
- data/app/views/hyrax/file_sets/_show_actions.html.erb +1 -1
- data/app/views/hyrax/file_sets/media_display/_audio.html.erb +2 -2
- data/app/views/hyrax/file_sets/media_display/_default.html.erb +1 -1
- data/app/views/hyrax/file_sets/media_display/_image.html.erb +1 -1
- data/app/views/hyrax/file_sets/media_display/_office_document.html.erb +1 -1
- data/app/views/hyrax/file_sets/media_display/_pdf.html.erb +2 -2
- data/app/views/hyrax/file_sets/media_display/_video.html.erb +2 -2
- data/app/views/hyrax/my/_sort_and_per_page.html.erb +1 -1
- data/app/views/layouts/_head_tag_content.html.erb +1 -1
- data/app/views/shared/_citations.html.erb +16 -12
- data/bin/hyrax-entrypoint.sh +9 -2
- data/chart/hyrax/Chart.yaml +2 -2
- data/config/initializers/listeners.rb +1 -0
- data/config/locales/hyrax.de.yml +71 -13
- data/config/locales/hyrax.en.yml +31 -19
- data/config/locales/hyrax.es.yml +60 -2
- data/config/locales/hyrax.fr.yml +60 -2
- data/config/locales/hyrax.it.yml +60 -2
- data/config/locales/hyrax.pt-BR.yml +60 -2
- data/config/locales/hyrax.zh.yml +60 -2
- data/documentation/developing-your-hyrax-based-app.md +2 -2
- data/hyrax.gemspec +2 -2
- data/lib/generators/hyrax/templates/catalog_controller.rb +4 -0
- data/lib/generators/hyrax/templates/config/tinymce.yml +3 -2
- data/lib/generators/hyrax/work_resource/work_resource_generator.rb +11 -0
- data/lib/hyrax/configuration.rb +14 -0
- data/lib/hyrax/engine.rb +1 -0
- data/lib/hyrax/version.rb +1 -1
- data/lib/wings.rb +1 -0
- data/lib/wings/services/custom_queries/find_ids_by_model.rb +46 -0
- data/lib/wings/setup.rb +1 -0
- data/template.rb +1 -1
- metadata +21 -7
@@ -367,8 +367,8 @@ module Hyrax
|
|
367
367
|
.select(:source_id)
|
368
368
|
.distinct
|
369
369
|
.pluck(:source_id)
|
370
|
-
|
371
|
-
Hyrax
|
370
|
+
|
371
|
+
Hyrax.custom_queries.find_ids_by_model(model: Hyrax::AdministrativeSet, ids: ids).any?
|
372
372
|
end
|
373
373
|
|
374
374
|
def registered_user?
|
@@ -24,13 +24,7 @@ module Hyrax
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.issue_types_for_locale
|
27
|
-
|
28
|
-
I18n.t('hyrax.contact_form.issue_types.depositing'),
|
29
|
-
I18n.t('hyrax.contact_form.issue_types.changing'),
|
30
|
-
I18n.t('hyrax.contact_form.issue_types.browsing'),
|
31
|
-
I18n.t('hyrax.contact_form.issue_types.reporting'),
|
32
|
-
I18n.t('hyrax.contact_form.issue_types.general')
|
33
|
-
]
|
27
|
+
I18n.t('hyrax.contact_form.issue_types').values.select(&:present?)
|
34
28
|
end
|
35
29
|
end
|
36
30
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Hyrax
|
3
|
+
##
|
3
4
|
# Defines behavior that is applied to objects added as members of an AdminSet
|
4
5
|
#
|
5
6
|
# * access rights to stamp on each object
|
@@ -14,10 +15,14 @@ module Hyrax
|
|
14
15
|
has_many :access_grants, class_name: 'Hyrax::PermissionTemplateAccess', dependent: :destroy
|
15
16
|
accepts_nested_attributes_for :access_grants, reject_if: :all_blank
|
16
17
|
|
18
|
+
##
|
17
19
|
# @api public
|
20
|
+
#
|
18
21
|
# Retrieve the agent_ids associated with the given agent_type and access
|
22
|
+
#
|
19
23
|
# @param [String] agent_type
|
20
24
|
# @param [String] access
|
25
|
+
#
|
21
26
|
# @return [Array<String>] of agent_ids that match the given parameters
|
22
27
|
def agent_ids_for(agent_type:, access:)
|
23
28
|
access_grants.where(agent_type: agent_type, access: access).pluck(:agent_id)
|
@@ -29,6 +34,15 @@ module Hyrax
|
|
29
34
|
# In a perfect world, there would be a join table that enforced uniqueness on the ID.
|
30
35
|
has_one :active_workflow, -> { where(active: true) }, class_name: 'Sipity::Workflow', foreign_key: :permission_template_id
|
31
36
|
|
37
|
+
##
|
38
|
+
# @note this is a convenience method for +Hyrax.query_service.find_by(id: template.source_id)+
|
39
|
+
#
|
40
|
+
# @return [Hyrax::Resource] the collection this template is associated with
|
41
|
+
def source
|
42
|
+
Hyrax.query_service.find_by(id: source_id)
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
32
46
|
# A bit of an analogue for a `belongs_to :source_model` as it crosses from Fedora to the DB
|
33
47
|
# @return [AdminSet, ::Collection]
|
34
48
|
# @raise [Hyrax::ObjectNotFoundError] when neither an AdminSet or Collection is found
|
@@ -42,7 +42,7 @@ class ProxyDepositRequest < ActiveRecord::Base
|
|
42
42
|
|
43
43
|
validates :sending_user, :work_id, presence: true
|
44
44
|
validate :transfer_to_should_be_a_valid_username
|
45
|
-
validate :sending_user_should_not_be_receiving_user
|
45
|
+
validate :sending_user_should_not_be_receiving_user, unless: :sender_is_admin?
|
46
46
|
validate :should_not_be_already_part_of_a_transfer
|
47
47
|
|
48
48
|
after_save :send_request_transfer_message
|
@@ -63,16 +63,20 @@ class ProxyDepositRequest < ActiveRecord::Base
|
|
63
63
|
private
|
64
64
|
|
65
65
|
def transfer_to_should_be_a_valid_username
|
66
|
-
errors.add(:transfer_to,
|
66
|
+
errors.add(:transfer_to, I18n.t('hyrax.notifications.proxy_deposit_request.validation.valid_username')) unless receiving_user
|
67
67
|
end
|
68
68
|
|
69
69
|
def sending_user_should_not_be_receiving_user
|
70
|
-
errors.add(:transfer_to, '
|
70
|
+
errors.add(:transfer_to, I18n.t('hyrax.notifications.proxy_deposit_request.validation.sender_is_not_receiver')) if receiving_user && receiving_user.user_key == sending_user.user_key
|
71
71
|
end
|
72
72
|
|
73
73
|
def should_not_be_already_part_of_a_transfer
|
74
74
|
transfers = ProxyDepositRequest.where(work_id: work_id, status: PENDING)
|
75
|
-
errors.add(:open_transfer, '
|
75
|
+
errors.add(:open_transfer, I18n.t('hyrax.notifications.proxy_deposit_request.validation.open_transfer')) unless transfers.blank? || (transfers.count == 1 && transfers[0].id == id)
|
76
|
+
end
|
77
|
+
|
78
|
+
def sender_is_admin?
|
79
|
+
sending_user.ability.admin?
|
76
80
|
end
|
77
81
|
|
78
82
|
public
|
@@ -34,6 +34,10 @@ module Hyrax
|
|
34
34
|
:original_file_id,
|
35
35
|
to: :solr_document
|
36
36
|
|
37
|
+
def workflow
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
37
41
|
def single_use_links
|
38
42
|
@single_use_links ||= SingleUseLink.where(item_id: id).map { |link| link_presenter_class.new(link) }
|
39
43
|
end
|
@@ -90,6 +94,7 @@ module Hyrax
|
|
90
94
|
end
|
91
95
|
|
92
96
|
def user_can_perform_any_action?
|
97
|
+
Deprecation.warn("We're removing Hyrax::FileSetPresenter.user_can_perform_any_action? in Hyrax 4.0.0; Instead use can? in view contexts.")
|
93
98
|
current_ability.can?(:edit, id) || current_ability.can?(:destroy, id) || current_ability.can?(:download, id)
|
94
99
|
end
|
95
100
|
|
@@ -103,6 +108,11 @@ module Hyrax
|
|
103
108
|
ids = Hyrax::SolrService.query("{!field f=member_ids_ssim}#{id}", fl: Hyrax.config.id_field)
|
104
109
|
.map { |x| x.fetch(Hyrax.config.id_field) }
|
105
110
|
Hyrax.logger.warn("Couldn't find a parent work for FileSet: #{id}.") if ids.empty?
|
111
|
+
ids.each do |id|
|
112
|
+
doc = ::SolrDocument.find(id)
|
113
|
+
next if current_ability.can?(:edit, doc)
|
114
|
+
raise WorkflowAuthorizationException if doc.suppressed? && current_ability.can?(:read, doc)
|
115
|
+
end
|
106
116
|
Hyrax::PresenterFactory.build_for(ids: ids,
|
107
117
|
presenter_class: WorkShowPresenter,
|
108
118
|
presenter_args: current_ability).first
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hyrax
|
4
|
+
##
|
5
|
+
# Handles presentation for google scholar meta tags.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# my_book = Monograph.new(title: ['On Moomins'], creator: ['Tove', 'Lars'])
|
9
|
+
# scholar = GoogleScholarPresenter.new(my_book)
|
10
|
+
#
|
11
|
+
# scholar.title => 'On Moomins'
|
12
|
+
#
|
13
|
+
# @see https://scholar.google.com/intl/en/scholar/inclusion.html#overview
|
14
|
+
class GoogleScholarPresenter < Draper::Decorator
|
15
|
+
##
|
16
|
+
# @note Scholar content inclusion docs indicate we should embed metadata
|
17
|
+
# for "scholarly articles - journal papers, conference papers,
|
18
|
+
# technical reports, or their drafts, dissertations, pre-prints,
|
19
|
+
# post-prints, or abstracts." Implementations should try to return
|
20
|
+
# `false` for other content.
|
21
|
+
#
|
22
|
+
# @return [Boolean] whether this content is "scholarly" for Google Scholar's
|
23
|
+
# purposes. delegates to decorated object if possible.
|
24
|
+
#
|
25
|
+
# @see https://scholar.google.com/intl/en/scholar/inclusion.html#content
|
26
|
+
def scholarly?
|
27
|
+
return object.scholarly? if object.respond_to?(:scholarly?)
|
28
|
+
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# @note Google Scholar cares about author order. when possible, this should
|
34
|
+
# return the othors in order. delegates to `#ordered_authors` when
|
35
|
+
# available.
|
36
|
+
#
|
37
|
+
# @return [Array<String>] an ordered array of author names
|
38
|
+
def authors
|
39
|
+
return object.ordered_authors if object.respond_to?(:ordered_authors)
|
40
|
+
|
41
|
+
Array(object.creator)
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# @note falls back on {#title} if no description can be found. this
|
46
|
+
# probably isn't great.
|
47
|
+
#
|
48
|
+
# @return [String] a description
|
49
|
+
def description
|
50
|
+
(Array(object.try(:description)).first || title).truncate(200)
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# @return [String] the keywords
|
55
|
+
def keywords
|
56
|
+
Array(object.try(:keyword)).join('; ')
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# @todo this should probably only return a present value if a PDF is
|
61
|
+
# available!
|
62
|
+
#
|
63
|
+
# @return [#to_s]
|
64
|
+
def pdf_url
|
65
|
+
object.try(:download_url)
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# @return [String] the publication date
|
70
|
+
def publication_date
|
71
|
+
Array(object.try(:date_created)).first || ''
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# @return [String] a string representing the publisher
|
76
|
+
def publisher
|
77
|
+
Array(object.try(:publisher)).join('; ')
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# @return [String] exactly one title; the same one every time
|
82
|
+
def title
|
83
|
+
Array(object.try(:title)).sort.first || ""
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -33,32 +33,8 @@ module Hyrax
|
|
33
33
|
solr_parameters[:fq] += limit_clause if limit_clause # add limits to prevent illegal nesting arrangements
|
34
34
|
end
|
35
35
|
|
36
|
-
# If :deposit access is requested, check to see which collections the user has
|
37
|
-
# deposit or manage access to.
|
38
|
-
# @return [Array<String>] a list of filters to apply to the solr query
|
39
|
-
def gated_discovery_filters(permission_types = discovery_permissions, ability = current_ability)
|
40
|
-
return super unless permission_types.include?("deposit")
|
41
|
-
["{!terms f=id}#{collection_ids_for_deposit.join(',')}"]
|
42
|
-
end
|
43
|
-
|
44
36
|
private
|
45
37
|
|
46
|
-
def collection_ids_for_deposit
|
47
|
-
Hyrax::Collections::PermissionsService.collection_ids_for_deposit(ability: current_ability)
|
48
|
-
end
|
49
|
-
|
50
|
-
# My intention in this implementation is that if I need at least edit access on the queried document,
|
51
|
-
# then I must have one of the following access-levels
|
52
|
-
ACCESS_LEVELS_FOR_LEVEL = ActiveSupport::HashWithIndifferentAccess.new(
|
53
|
-
edit: ["edit"],
|
54
|
-
deposit: ["deposit"],
|
55
|
-
read: ["edit", "read"],
|
56
|
-
discover: ["edit", "discover", "read"]
|
57
|
-
).freeze
|
58
|
-
def extract_discovery_permissions(access)
|
59
|
-
ACCESS_LEVELS_FOR_LEVEL.fetch(access)
|
60
|
-
end
|
61
|
-
|
62
38
|
def limit_ids
|
63
39
|
# exclude current collection from returned list
|
64
40
|
limit_ids = [@collection.id]
|
@@ -18,7 +18,7 @@ module Hyrax
|
|
18
18
|
delegate :repository, to: :scope
|
19
19
|
|
20
20
|
##
|
21
|
-
# @param [#repository] scope Typically
|
21
|
+
# @param [#repository] scope Typically a controller object which responds to +#repository+
|
22
22
|
# @param [::Collection] collection an collection of type admin set
|
23
23
|
# @param [ActionController::Parameters] params query params
|
24
24
|
def initialize(scope:, collection:, params:)
|
@@ -1,9 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Hyrax
|
3
3
|
class ChangeContentDepositorService
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
4
|
+
# Set the given `user` as the depositor of the given `work`; If
|
5
|
+
# `reset` is true, first remove all previous permissions.
|
6
|
+
#
|
7
|
+
# @param work [ActiveFedora::Base, Valkyrie::Resource] the work
|
8
|
+
# that is receiving a change of depositor
|
9
|
+
# @param user [User] the user that will "become" the depositor of
|
10
|
+
# the given work
|
11
|
+
# @param reset [TrueClass, FalseClass] when true, first clear
|
12
|
+
# permissions for the given work and contained file
|
13
|
+
# sets; regardless of true/false make the given user
|
14
|
+
# the depositor of the given work
|
7
15
|
def self.call(work, user, reset)
|
8
16
|
case work
|
9
17
|
when ActiveFedora::Base
|
@@ -18,6 +26,7 @@ module Hyrax
|
|
18
26
|
work.permissions = [] if reset
|
19
27
|
work.apply_depositor_metadata(user)
|
20
28
|
work.file_sets.each do |f|
|
29
|
+
f.permissions = [] if reset
|
21
30
|
f.apply_depositor_metadata(user)
|
22
31
|
f.save!
|
23
32
|
end
|
@@ -26,6 +35,8 @@ module Hyrax
|
|
26
35
|
end
|
27
36
|
private_class_method :call_af
|
28
37
|
|
38
|
+
# @todo Should this include some dependency injection regarding
|
39
|
+
# the Hyrax.persister and Hyrax.custom_queries?
|
29
40
|
def self.call_valkyrie(work, user, reset)
|
30
41
|
if reset
|
31
42
|
work.permission_manager.acl.permissions = []
|
@@ -35,19 +46,30 @@ module Hyrax
|
|
35
46
|
work.proxy_depositor = work.depositor
|
36
47
|
apply_depositor_metadata(work, user)
|
37
48
|
|
38
|
-
|
39
|
-
apply_depositor_metadata(f, user)
|
40
|
-
end
|
49
|
+
apply_valkyrie_changes_to_file_sets(work: work, user: user, reset: reset)
|
41
50
|
|
51
|
+
Hyrax.persister.save(resource: work)
|
42
52
|
work
|
43
53
|
end
|
44
54
|
private_class_method :call_valkyrie
|
45
55
|
|
46
56
|
def self.apply_depositor_metadata(resource, depositor)
|
47
57
|
depositor_id = depositor.respond_to?(:user_key) ? depositor.user_key : depositor
|
48
|
-
resource.depositor = depositor_id if resource.respond_to? :depositor
|
58
|
+
resource.depositor = depositor_id if resource.respond_to? :depositor=
|
49
59
|
Hyrax::AccessControlList.new(resource: resource).grant(:edit).to(::User.find_by_user_key(depositor_id)).save
|
50
60
|
end
|
51
61
|
private_class_method :apply_depositor_metadata
|
62
|
+
|
63
|
+
def self.apply_valkyrie_changes_to_file_sets(work:, user:, reset:)
|
64
|
+
Hyrax.custom_queries.find_child_filesets(resource: work).each do |f|
|
65
|
+
if reset
|
66
|
+
f.permission_manager.acl.permissions = []
|
67
|
+
f.permission_manager.acl.save
|
68
|
+
end
|
69
|
+
apply_depositor_metadata(f, user)
|
70
|
+
Hyrax.persister.save(resource: f)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
private_class_method :apply_valkyrie_changes_to_file_sets
|
52
74
|
end
|
53
75
|
end
|
@@ -22,15 +22,14 @@ module Hyrax
|
|
22
22
|
|
23
23
|
def self.filter_source(source_type:, ids:)
|
24
24
|
return [] if ids.empty?
|
25
|
-
|
26
|
-
query = case source_type
|
25
|
+
model = case source_type
|
27
26
|
when 'admin_set'
|
28
|
-
|
27
|
+
Hyrax::AdministrativeSet
|
29
28
|
when 'collection'
|
30
|
-
|
29
|
+
Hyrax::PcdmCollection
|
31
30
|
end
|
32
|
-
|
33
|
-
Hyrax
|
31
|
+
|
32
|
+
Hyrax.custom_queries.find_ids_by_model(model: model, ids: ids).to_a
|
34
33
|
end
|
35
34
|
private_class_method :filter_source
|
36
35
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
module CustomQueries
|
4
|
+
##
|
5
|
+
# @see https://github.com/samvera/valkyrie/wiki/Queries#custom-queries
|
6
|
+
class FindIdsByModel
|
7
|
+
def self.queries
|
8
|
+
[:find_ids_by_model]
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(query_service:)
|
12
|
+
@query_service = query_service
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :query_service
|
16
|
+
delegate :resource_factory, to: :query_service
|
17
|
+
|
18
|
+
##
|
19
|
+
# @note this is an unoptimized default implementation of this custom
|
20
|
+
# query. it's Hyrax's policy to provide such implementations of custom
|
21
|
+
# queries in use for cross-compatibility of Valkyrie query services.
|
22
|
+
# it's advisable to provide an optimized query for the specific adapter.
|
23
|
+
#
|
24
|
+
# @param model [Class]
|
25
|
+
# @param ids [Enumerable<#to_s>, Symbol]
|
26
|
+
#
|
27
|
+
# @return [Enumerable<Valkyrie::ID>]
|
28
|
+
def find_ids_by_model(model:, ids: :all)
|
29
|
+
return query_service.find_all_of_model(model: model).map(&:id) if ids == :all
|
30
|
+
|
31
|
+
query_service.find_many_by_ids(ids: ids).select do |resource|
|
32
|
+
resource.is_a?(model)
|
33
|
+
end.map(&:id)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Hyrax
|
3
|
+
# Encapsulates the logic to determine which object permissions may be edited by a given user
|
4
|
+
# - user is permitted to update any work permissions coming ONLY from collections they manage
|
5
|
+
# - user is not permitted to update a work permission if it comes from a collection they do not manage, even if also from a managed collection
|
6
|
+
# - user is permitted to update only non-manager permissions from any Collections
|
7
|
+
# - user is permitted to update any non-collection permissions
|
8
|
+
class EditPermissionsService
|
9
|
+
# @api public
|
10
|
+
# @since v3.0.0
|
11
|
+
#
|
12
|
+
# @param form [SimpleForm::FormBuilder]
|
13
|
+
# @param current_ability [Ability]
|
14
|
+
# @return [Hyrax::EditPermissionService]
|
15
|
+
#
|
16
|
+
# @note
|
17
|
+
# form object.class = SimpleForm::FormBuilder
|
18
|
+
# For works (i.e. GenericWork):
|
19
|
+
# - form object.object = Hyrax::GenericWorkForm
|
20
|
+
# - form object.object.model = GenericWork
|
21
|
+
# - use the work itself
|
22
|
+
# For file_sets:
|
23
|
+
# - form object.object.class = FileSet
|
24
|
+
# - use work the file_set is in
|
25
|
+
# No other object types are supported by this view. %>
|
26
|
+
def self.build_service_object_from(form:, ability:)
|
27
|
+
if form.object.respond_to?(:model) && form.object.model.work?
|
28
|
+
new(object: form.object, ability: ability)
|
29
|
+
elsif form.object.file_set?
|
30
|
+
new(object: form.object.in_works.first, ability: ability)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :depositor, :unauthorized_collection_managers
|
35
|
+
|
36
|
+
# @param object [#depositor, #admin_set_id, #member_of_collection_ids] GenericWorkForm (if called for object) or GenericWork (if called for file set)
|
37
|
+
# @param ability [Ability] user's current_ability
|
38
|
+
def initialize(object:, ability:)
|
39
|
+
@object = object
|
40
|
+
@ability = ability
|
41
|
+
@depositor = object.depositor
|
42
|
+
unauthorized = manager_permissions_to_block
|
43
|
+
@unauthorized_managers = unauthorized.unauthorized_managers
|
44
|
+
@unauthorized_collection_managers = unauthorized.unauthorized_collection_managers
|
45
|
+
end
|
46
|
+
|
47
|
+
# @api private
|
48
|
+
# @todo refactor this code to use "can_edit?"; Thinking in negations can be challenging.
|
49
|
+
#
|
50
|
+
# @param permission_hash [Hash] one set of permission fields for object {:name, :access}
|
51
|
+
# @return [Boolean] true if user cannot edit the given permissions
|
52
|
+
def cannot_edit_permissions?(permission_hash)
|
53
|
+
permission_hash.fetch(:access) == "edit" && @unauthorized_managers.include?(permission_hash.fetch(:name))
|
54
|
+
end
|
55
|
+
|
56
|
+
# @api private
|
57
|
+
#
|
58
|
+
# @param permission_hash [Hash] one set of permission fields for object {:name, :access}
|
59
|
+
# @return [Boolean] true if given permissions are one of fixed exclusions
|
60
|
+
def excluded_permission?(permission_hash)
|
61
|
+
exclude_from_display.include? permission_hash.fetch(:name).downcase
|
62
|
+
end
|
63
|
+
|
64
|
+
# @api public
|
65
|
+
#
|
66
|
+
# This method either:
|
67
|
+
#
|
68
|
+
# * returns false if the given permission_hash is part of the fixed exclusions.
|
69
|
+
# * yields a PermissionPresenter to provide additional logic and text for rendering
|
70
|
+
#
|
71
|
+
# @param permission_hash [Hash<:name, :access>]
|
72
|
+
# @return false if the given permission_hash is a fixed exclusion
|
73
|
+
# @yield PermissionPresenter
|
74
|
+
#
|
75
|
+
# @see #excluded_permission?
|
76
|
+
def with_applicable_permission(permission_hash:)
|
77
|
+
return false if excluded_permission?(permission_hash)
|
78
|
+
yield(PermissionPresenter.new(service: self, permission_hash: permission_hash))
|
79
|
+
end
|
80
|
+
|
81
|
+
# @api private
|
82
|
+
#
|
83
|
+
# A helper class to contain specific presentation logic related to
|
84
|
+
# the EditPermissionsService
|
85
|
+
class PermissionPresenter
|
86
|
+
# @param service [Hyrax::EditPermissionsService]
|
87
|
+
# @param permission_hash [Hash]
|
88
|
+
def initialize(service:, permission_hash:)
|
89
|
+
@service = service
|
90
|
+
@permission_hash = permission_hash
|
91
|
+
end
|
92
|
+
|
93
|
+
# A hint at how permissions are granted.
|
94
|
+
#
|
95
|
+
# @return String
|
96
|
+
# rubocop:disable Rails/OutputSafety
|
97
|
+
def granted_by_html_hint
|
98
|
+
html = ""
|
99
|
+
@service.unauthorized_collection_managers.each do |managers|
|
100
|
+
next unless name == managers.fetch(:name)
|
101
|
+
html += "<br />Access granted via collection #{managers.fetch(:id)}"
|
102
|
+
end
|
103
|
+
html.html_safe
|
104
|
+
end
|
105
|
+
# rubocop:enable Rails/OutputSafety
|
106
|
+
|
107
|
+
# @return String
|
108
|
+
def name
|
109
|
+
@permission_hash.fetch(:name)
|
110
|
+
end
|
111
|
+
|
112
|
+
# @return String
|
113
|
+
def access
|
114
|
+
@permission_hash.fetch(:access)
|
115
|
+
end
|
116
|
+
|
117
|
+
# @return Boolean
|
118
|
+
# @see EditPermissionsService#cannot_edit_permissions?
|
119
|
+
def can_edit?
|
120
|
+
!@service.cannot_edit_permissions?(@permission_hash)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
# Fixed set of users & groups to exclude from "editable" section of display
|
127
|
+
def exclude_from_display
|
128
|
+
[::Ability.public_group_name, ::Ability.registered_group_name, ::Ability.admin_group_name, @depositor]
|
129
|
+
end
|
130
|
+
|
131
|
+
BlockedPermissions = Struct.new(:unauthorized_managers, :unauthorized_collection_managers)
|
132
|
+
|
133
|
+
# find all of the other managers of collections which a user cannot manage
|
134
|
+
#
|
135
|
+
# Process used:
|
136
|
+
# - find all of the work's collections which a user can manage
|
137
|
+
# - find all of the work's collections (of a type which shares permissions) that a user cannot manage
|
138
|
+
# - find all of the managers of these collections the user cannot manage
|
139
|
+
# This gives us the manager permissions the user is not authorized to update.
|
140
|
+
#
|
141
|
+
# @return [Struct] BlockedPermissions
|
142
|
+
# - unauthorized_managers [Array] ids of managers of all collections
|
143
|
+
# - unauthorized_collection_managers [Array hashes] manager ids & collection_ids [{:name, :id}]
|
144
|
+
def manager_permissions_to_block
|
145
|
+
unauthorized_managers = []
|
146
|
+
unauthorized_collection_managers = []
|
147
|
+
object_unauthorized_collection_ids.each do |id|
|
148
|
+
Hyrax::PermissionTemplate.find_by(source_id: id).access_grants.each do |grant|
|
149
|
+
if grant.access == "manage"
|
150
|
+
unauthorized_managers << grant.agent_id
|
151
|
+
unauthorized_collection_managers += Array.wrap({ name: grant.agent_id }.merge(id: id))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
BlockedPermissions.new(unauthorized_managers, unauthorized_collection_managers)
|
156
|
+
end
|
157
|
+
|
158
|
+
# find all of the work's collections a user can manage
|
159
|
+
# @return [Array] of collection ids
|
160
|
+
def object_managed_collection_ids
|
161
|
+
@object_managed_collection_ids ||= object_member_of & managed_collection_ids
|
162
|
+
end
|
163
|
+
|
164
|
+
# find all of the work's collections a user cannot manage
|
165
|
+
# note: if the collection type doesn't include "sharing_applies_to_new_works", we don't limit access
|
166
|
+
# @return [Array] of collection ids with limited access
|
167
|
+
def object_unauthorized_collection_ids
|
168
|
+
@object_unauthorized_collection_ids ||= begin
|
169
|
+
limited_access = []
|
170
|
+
unauthorized_collection_ids = object_member_of - object_managed_collection_ids
|
171
|
+
if unauthorized_collection_ids.any?
|
172
|
+
unauthorized_collection_ids.each do |id|
|
173
|
+
# TODO: Can we instead use a SOLR query? This seems to be somewhat expensive. However, as this is
|
174
|
+
# used in administration instead of user front-end displays, I'm not as concerned.
|
175
|
+
collection = ActiveFedora::Base.find(id)
|
176
|
+
limited_access << id if (collection.instance_of? AdminSet) || collection.share_applies_to_new_works?
|
177
|
+
end
|
178
|
+
end
|
179
|
+
limited_access
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# find all of the collection ids an object is a member of
|
184
|
+
# @return [Array] array of collection ids
|
185
|
+
def object_member_of
|
186
|
+
@object_member_of ||= begin
|
187
|
+
belongs_to = []
|
188
|
+
# get all of work's collection ids from the form
|
189
|
+
belongs_to += @object.member_of_collection_ids
|
190
|
+
belongs_to << @object.admin_set_id if @object.admin_set_id.present?
|
191
|
+
belongs_to
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# The list of all collections this user has manage rights on
|
196
|
+
# @return [Array] array of all collection ids that user can manage
|
197
|
+
def managed_collection_ids
|
198
|
+
Hyrax::Collections::PermissionsService.source_ids_for_manage(ability: @ability)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|