Skip to content

Commit

Permalink
Adds access to access settings.
Browse files Browse the repository at this point in the history
closes #463
  • Loading branch information
justinlittman committed Jan 10, 2025
1 parent 0b4fe5b commit 7787e36
Show file tree
Hide file tree
Showing 21 changed files with 229 additions and 39 deletions.
8 changes: 8 additions & 0 deletions app/components/works/edit/access_settings_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@
</p>

<%= render Elements::HorizontalRuleComponent.new(classes: 'my-3') %>

<%= render Works::Edit::SectionHeaderComponent.new(title: 'Download access') %>
<% if depositor_selects_access? %>
<%= render Elements::Forms::SelectFieldComponent.new(form:, field_name: :access, options: access_options, width: 250, label: 'Who can download the files?') %>
<% else %>
<%= collection_selects_access_message %>
<%= form.hidden_field :access %>
<% end %>
19 changes: 18 additions & 1 deletion app/components/works/edit/access_settings_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,27 @@ module Works
module Edit
# Component for rendering the access settings pane.
class AccessSettingsComponent < ApplicationComponent
def initialize(form:)
def initialize(form:, collection:)
@form = form
@collection = collection
super()
end

attr_reader :form

delegate :depositor_selects_access?, to: :@collection

def access_options
[
[I18n.t('access.stanford'), 'stanford'],
[I18n.t('access.world'), 'world']
]
end

def collection_selects_access_message
access_text = form.object.access == 'stanford' ? 'the Stanford Community' : 'anyone'
"The files in your deposit will be downloadable by #{access_text}."
end
end
end
end
30 changes: 19 additions & 11 deletions app/controllers/works_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@ def show
end

def new
collection = Collection.find_by!(druid: params.expect(:collection_druid))
@collection = Collection.find_by!(druid: params.expect(:collection_druid))

authorize! collection, with: WorkPolicy
authorize! @collection, with: WorkPolicy

@collection_title = collection.title
@content = Content.create!(user: current_user)
@work_form = WorkForm.new(collection_druid: collection.druid, content_id: @content.id, license: collection.license)
@license_presenter = LicensePresenter.new(work_form: @work_form, collection:)
@work_form = new_work_form
@license_presenter = LicensePresenter.new(work_form: @work_form, collection: @collection)

render :form
end
Expand All @@ -40,29 +39,28 @@ def edit

# This updates the Work with the latest metadata from the Cocina object.
ModelSync::Work.call(work: @work, cocina_object: @cocina_object)
@collection_title = @work.collection.title
@collection = @work.collection
@license_presenter = LicensePresenter.new(work_form: @work_form, collection: @work.collection)

render :form
end

def create # rubocop:disable Metrics/AbcSize
@work_form = WorkForm.new(**work_params)
collection = Collection.find_by!(druid: @work_form.collection_druid)
@collection_title = collection.title
authorize! collection, with: WorkPolicy
@collection = Collection.find_by!(druid: @work_form.collection_druid)
authorize! @collection, with: WorkPolicy

# The validation_context param determines whether extra validations are applied, e.g., for deposits.
if @work_form.valid?(validation_context)
# Setting the deposit_job_started_at to the current time to indicate that the deposit job has started and user
# should be "waiting".
work = Work.create!(title: @work_form.title, user: current_user, deposit_job_started_at: Time.zone.now,
collection:)
collection: @collection)
DepositWorkJob.perform_later(work:, work_form: @work_form, deposit: deposit?)
redirect_to wait_works_path(work.id)
else
@content = Content.find(@work_form.content_id)
@license_presenter = LicensePresenter.new(work_form: @work_form, collection:)
@license_presenter = LicensePresenter.new(work_form: @work_form, collection: @collection)
render :form, status: :unprocessable_entity
end
end
Expand All @@ -80,6 +78,7 @@ def update # rubocop:disable Metrics/AbcSize
redirect_to wait_works_path(@work.id)
else
@content = Content.find(@work_form.content_id)
@collection = @work.collection
@license_presenter = LicensePresenter.new(work_form: @work_form, collection: @work.collection)
render :form, status: :unprocessable_entity
end
Expand Down Expand Up @@ -153,4 +152,13 @@ def editable?
def set_presenter
@work_presenter = WorkPresenter.new(work: @work, work_form: @work_form, version_status: @version_status)
end

def new_work_form
WorkForm.new(
collection_druid: @collection.druid,
content_id: @content.id,
license: @collection.license,
access: @collection.stanford_access? ? 'stanford' : 'world'
)
end
end
3 changes: 3 additions & 0 deletions app/forms/work_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ def persisted?
# Other requires a work subtype string
validates :other_work_subtype, presence: true, if: -> { work_type == WorkType::OTHER }

attribute :access, :string, default: 'world'
validates :access, inclusion: { in: %w[world stanford] }

def content_file_presence
return if content_id.nil? # This makes test configuration easier.
return if Content.find(content_id).content_files.exists?
Expand Down
4 changes: 4 additions & 0 deletions app/presenters/work_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ def publication_date
super.to_s
end

def access_label
I18n.t("access.#{access}")
end

private

delegate :collection, :created_at, :user, to: :work
Expand Down
8 changes: 8 additions & 0 deletions app/services/cocina_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,12 @@ def self.event_date_for(cocina_object:, type:) # rubocop:disable Metrics/AbcSize
def self.orcid_for(contributor:)
contributor.identifier&.find { |id| id.type == 'ORCID' }&.value&.presence
end

def self.access_for(cocina_object:)
cocina_object.access.view
end

def self.license_for(cocina_object:)
cocina_object.access.license
end
end
33 changes: 33 additions & 0 deletions app/services/to_cocina/work/access_mapper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

module ToCocina
module Work
# Maps WorkForm to Cocina access parameters
class AccessMapper
def self.call(...)
new(...).call
end

# @param [WorkForm] work_form
def initialize(work_form:)
@work_form = work_form
end

# @return [Hash] the Cocina access parameters
def call
{
view: access,
download: access,
license: license.presence,
useAndReproductionStatement: I18n.t('license.terms_of_use')
}.compact
end

private

attr_reader :work_form

delegate :access, :license, to: :work_form
end
end
end
7 changes: 1 addition & 6 deletions app/services/to_cocina/work/mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,13 @@ def params
label: work_form.title,
description: DescriptionMapper.call(work_form:),
version: work_form.version,
access:,
access: AccessMapper.call(work_form:),
identification: { sourceId: source_id },
administrative: { hasAdminPolicy: Settings.apo },
structural: StructuralMapper.call(work_form:, content:)
}.compact
end

def access
{ view: 'world', download: 'world', license: work_form.license.presence,
useAndReproductionStatement: I18n.t('license.terms_of_use') }.compact
end

def request_params
params.tap do |params_hash|
params_hash[:administrative][:partOfProject] = Settings.project_tag
Expand Down
4 changes: 3 additions & 1 deletion app/services/to_cocina/work/structural_mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ def call

attr_reader :work_form, :content

delegate :access, to: :work_form

def params
{
contains: content.content_files.map { |content_file| fileset_params_for(content_file) },
Expand All @@ -55,7 +57,7 @@ def file_params_for(content_file)
version: work_form.version,
label: content_file.label,
filename: content_file.filepath,
access: { view: 'world', download: 'world' },
access: { view: access, download: access },
administrative: { publish: !content_file.hidden?, sdrPreserve: true, shelve: !content_file.hidden? },
hasMimeType: content_file.mime_type,
hasMessageDigests: message_digests_for(content_file),
Expand Down
3 changes: 3 additions & 0 deletions app/services/to_collection_form/roundtrip_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def call
Rails.logger.info("Roundtripped: #{pretty_roundtripped}")
false
end
rescue Cocina::Models::ValidationError
# Generating the roundtripped cocina object may create an invalid object
false
end

private
Expand Down
3 changes: 2 additions & 1 deletion app/services/to_work_form/mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ def params # rubocop:disable Metrics/AbcSize
related_works_attributes: CocinaSupport.related_works_for(cocina_object:),
related_links_attributes: CocinaSupport.related_links_for(cocina_object:),
keywords_attributes: CocinaSupport.keywords_for(cocina_object:),
license: cocina_object.access.license,
license: CocinaSupport.license_for(cocina_object:),
access: CocinaSupport.access_for(cocina_object:),
version: cocina_object.version,
collection_druid: CocinaSupport.collection_druid_for(cocina_object:),
publication_date_attributes: CocinaSupport.event_date_for(cocina_object:, type: 'publication')
Expand Down
3 changes: 3 additions & 0 deletions app/services/to_work_form/roundtrip_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ def call
Rails.logger.info("Roundtripped: #{pretty_roundtripped}")
false
end
rescue Cocina::Models::ValidationError
# Generating the roundtripped cocina object may create an invalid object
false
end

private
Expand Down
4 changes: 2 additions & 2 deletions app/views/works/form.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<% content_for :breadcrumbs do %>
<%= render Elements::BreadcrumbNavComponent.new(dashboard: true, admin: false) do |component| %>
<% component.with_breadcrumb(text: @collection_title, link: collection_path(druid: @work_form.collection_druid)) %>
<% component.with_breadcrumb(text: @collection.title, link: collection_path(druid: @work_form.collection_druid)) %>
<% if (current_page?(action: :new) || (controller.action_name == 'create')) %>
<% component.with_breadcrumb(text: I18n.t('works.edit.no_title')) %>
<% else %>
Expand Down Expand Up @@ -74,7 +74,7 @@
<% end %>

<% component.with_pane(tab_name: :access, label: t('works.edit.panes.access.label'), form_id:) do %>
<%= render Works::Edit::AccessSettingsComponent.new(form:) %>
<%= render Works::Edit::AccessSettingsComponent.new(form:, collection: @collection) %>
<% end %>

<% component.with_pane(tab_name: :license, label: t('works.edit.panes.license.label'), form_id:) do %>
Expand Down
4 changes: 4 additions & 0 deletions app/views/works/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
<% component.with_row(label: 'Related links', values: @work_presenter.related_links) %>
<% end %>

<%= render Elements::Tables::TableComponent.new(id: 'access-table', classes: 'mb-5', label: 'Access settings') do |component| %>
<% component.with_row(label: 'Access', values: [@work_presenter.access_label]) %>
<% end %>

<%= render Elements::Tables::TableComponent.new(id: 'license-table', classes: 'mb-5', label: 'License and additional terms of use') do |component| %>
<% component.with_row(label: 'License', values: [@work_presenter.license_label]) %>
<% component.with_row(label: 'Terms of use', values: [t('license.terms_of_use')]) %>
Expand Down
3 changes: 3 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ en:
validation: >
Required fields have not been filled out. Click on the section(s) highlighted in red on
the left to correct the error(s) and then try depositing again.
access:
stanford: 'Stanford Community'
world: 'Everyone'
publication_date:
edit:
legend: 'Previous publication date'
Expand Down
30 changes: 30 additions & 0 deletions spec/components/works/edit/access_settings_component_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe Works::Edit::AccessSettingsComponent, type: :component do
let(:form) { ActionView::Helpers::FormBuilder.new(nil, work_form, vc_test_controller.view_context, {}) }
let(:work_form) { WorkForm.new(access: 'stanford') }
let(:collection) { instance_double(Collection, depositor_selects_access?: depositor_selects) }

context 'when the depositor selects access' do
let(:depositor_selects) { true }

it 'renders the select' do
render_inline(described_class.new(form:, collection:))

expect(page).to have_select('Who can download the files?', options: ['Stanford Community', 'Everyone'])
end
end

context 'when the collection specifies access' do
let(:depositor_selects) { false }

it 'states the access and renders a hidden field' do
render_inline(described_class.new(form:, collection:))

expect(page).to have_text('The files in your deposit will be downloadable by the Stanford Community.')
expect(page).to have_field('access', type: 'hidden', with: 'stanford')
end
end
end
6 changes: 4 additions & 2 deletions spec/serializers/work_form_serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
'work_type' => work_type_fixture,
'work_subtypes' => work_subtypes_fixture,
'other_work_subtype' => nil,
'content_id' => 5
'content_id' => 5,
'access' => 'stanford'
}
end
let(:work_form) do
Expand All @@ -44,7 +45,8 @@
keywords_attributes: keywords_fixture,
work_type: work_type_fixture,
work_subtypes: work_subtypes_fixture,
content_id: 5)
content_id: 5,
access: 'stanford')
end

describe '.serialize?' do
Expand Down
56 changes: 56 additions & 0 deletions spec/services/to_cocina/work/access_mapper_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe ToCocina::Work::AccessMapper do
subject(:access) { described_class.call(work_form:) }

context 'when stanford access' do
let(:work_form) { WorkForm.new(access: 'stanford') }

it 'maps to cocina' do
expect(access).to match(
view: 'stanford',
download: 'stanford',
useAndReproductionStatement: String
)
end
end

context 'when world access' do
let(:work_form) { WorkForm.new }

it 'maps to cocina' do
expect(access).to match(
view: 'world',
download: 'world',
useAndReproductionStatement: String
)
end
end

context 'when license' do
let(:work_form) { WorkForm.new(license: 'http://creativecommons.org/licenses/by/3.0/us/') }

it 'maps to cocina' do
expect(access).to match(
view: 'world',
download: 'world',
license: 'http://creativecommons.org/licenses/by/3.0/us/',
useAndReproductionStatement: String
)
end
end

context 'when none license' do
let(:work_form) { WorkForm.new(license: '') }

it 'maps to cocina' do
expect(access).to match(
view: 'world',
download: 'world',
useAndReproductionStatement: String
)
end
end
end
Loading

0 comments on commit 7787e36

Please sign in to comment.