hyrax-doi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +65 -0
  3. data/Rakefile +23 -0
  4. data/app/actors/hyrax/actors/doi_actor.rb +50 -0
  5. data/app/assets/config/hyrax_doi_manifest.js +2 -0
  6. data/app/assets/javascripts/hyrax/doi/application.js +15 -0
  7. data/app/assets/stylesheets/hyrax/doi/application.css +15 -0
  8. data/app/controllers/hyrax/doi/application_controller.rb +15 -0
  9. data/app/controllers/hyrax/doi/hyrax_doi_controller.rb +91 -0
  10. data/app/forms/concerns/hyrax/doi/datacite_doi_form_behavior.rb +18 -0
  11. data/app/forms/concerns/hyrax/doi/doi_form_behavior.rb +18 -0
  12. data/app/helpers/hyrax/doi/helper_behavior.rb +9 -0
  13. data/app/helpers/hyrax/doi/work_form_helper.rb +15 -0
  14. data/app/helpers/hyrax/doi/work_show_helper.rb +12 -0
  15. data/app/jobs/hyrax/doi/application_job.rb +7 -0
  16. data/app/jobs/hyrax/doi/register_doi_job.rb +18 -0
  17. data/app/models/concerns/hyrax/doi/datacite_doi_behavior.rb +21 -0
  18. data/app/models/concerns/hyrax/doi/doi_behavior.rb +38 -0
  19. data/app/models/concerns/hyrax/doi/solr_document/datacite_doi_behavior.rb +14 -0
  20. data/app/models/concerns/hyrax/doi/solr_document/doi_behavior.rb +14 -0
  21. data/app/presenters/concerns/hyrax/doi/datacite_doi_presenter_behavior.rb +20 -0
  22. data/app/presenters/concerns/hyrax/doi/doi_presenter_behavior.rb +12 -0
  23. data/app/services/bolognese/readers/hyrax_work_reader.rb +99 -0
  24. data/app/services/bolognese/writers/hyrax_work_writer.rb +50 -0
  25. data/app/services/hyrax/doi/datacite_client.rb +138 -0
  26. data/app/services/hyrax/doi/datacite_registrar.rb +121 -0
  27. data/app/views/hyrax/base/_attribute_rows.html.erb +18 -0
  28. data/app/views/hyrax/base/_form_doi.html.erb +73 -0
  29. data/config/locales/hyrax_doi.en.yml +7 -0
  30. data/config/routes.rb +5 -0
  31. data/lib/generators/hyrax/doi/add_to_work_type_generator.rb +97 -0
  32. data/lib/generators/hyrax/doi/install_generator.rb +74 -0
  33. data/lib/generators/hyrax/doi/templates/config/initializers/hyrax-doi.rb +15 -0
  34. data/lib/hyrax/doi.rb +9 -0
  35. data/lib/hyrax/doi/engine.rb +28 -0
  36. data/lib/hyrax/doi/errors.rb +8 -0
  37. data/lib/hyrax/doi/spec/shared_specs.rb +9 -0
  38. data/lib/hyrax/doi/spec/shared_specs/datacite_doi_behavior.rb +40 -0
  39. data/lib/hyrax/doi/spec/shared_specs/datacite_doi_form_behavior.rb +17 -0
  40. data/lib/hyrax/doi/spec/shared_specs/datacite_doi_presenter_behavior.rb +39 -0
  41. data/lib/hyrax/doi/spec/shared_specs/doi_behavior.rb +63 -0
  42. data/lib/hyrax/doi/spec/shared_specs/doi_form_behavior.rb +17 -0
  43. data/lib/hyrax/doi/spec/shared_specs/doi_presenter_behavior.rb +19 -0
  44. data/lib/hyrax/doi/spec/shared_specs/solr_document/datacite_doi_behavior.rb +20 -0
  45. data/lib/hyrax/doi/spec/shared_specs/solr_document/doi_behavior.rb +20 -0
  46. data/lib/hyrax/doi/version.rb +6 -0
  47. data/lib/tasks/hyrax/doi_tasks.rake +5 -0
  48. metadata +319 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d0263fd4d11d35e30f48410437354a6604f0726ddfeb13791062faf174c85d81
4
+ data.tar.gz: 7db8943b4fa0b9960572ad3dba14f4f6bb069af024338033b5260ba74aac4450
5
+ SHA512:
6
+ metadata.gz: 69950241eb6d5511544d937cd87cdb94351ec171ebc94e7ad6f4dc91a16cd95be5263dea80d366ff431c6e79ba47bc28fd0b89e435078d8d199fc5caa8f7d2eb
7
+ data.tar.gz: 789ac23699e1a9476c215350d1abef88dcf0fd5c596e2c7e76974d442fc4065d44ea9f0d8e26a895401635d93a5eb06f5bdc5b084189dfdd14702dff247b1266
@@ -0,0 +1,65 @@
1
+ # Hyrax::DOI
2
+ Code: [![CircleCI](https://circleci.com/gh/ubiquitypress/hyrax-doi.svg?style=svg)](https://circleci.com/gh/ubiquitypress/hyrax-doi)
3
+ [![Code Climate](https://codeclimate.com/github/ubiquitypress/hyrax-doi/badges/gpa.svg)](https://codeclimate.com/github/ubiquitypress/hyrax-doi)
4
+
5
+
6
+ Docs: [![Contribution Guidelines](http://img.shields.io/badge/CONTRIBUTING-Guidelines-blue.svg)](./CONTRIBUTING.md)
7
+ [![Apache 2.0 License](http://img.shields.io/badge/APACHE2-license-blue.svg)](./LICENSE)
8
+
9
+ Jump in: [![Slack Status](http://slack.samvera.org/badge.svg)](http://slack.samvera.org/)
10
+
11
+ Hyrax-doi is a Hyrax plugin that provides tools for working with DOIs including model attributes, minting, and fetching descriptive metadata.
12
+
13
+ ## Compatibilty
14
+ Hyrax-doi is compatible with Hyrax 2.9+ and tested with a [Hyrax 2.9.0 test application](https://github.com/ubiquitypress/hyrax_test_app) that mirrors the generated app used by Hyrax internally for testing.
15
+
16
+ ## Installation
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'hyrax-doi'
21
+ ```
22
+
23
+ And then execute:
24
+ ```bash
25
+ $ bundle
26
+ ```
27
+
28
+ Or install it yourself as:
29
+ ```bash
30
+ $ gem install hyrax-doi
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### Setup work type for hyrax-doi
36
+ Run the generator to add DOI support to a given work type:
37
+ ```
38
+ rails g hyrax:doi:add_to_work_type MyWorkType
39
+ ```
40
+
41
+ ## Development
42
+
43
+ ### Running Rake Tasks and Generators
44
+ When working on this engine rake tasks from Hyku can be run by prepending the `app` namespace (e.g. `rake app:db:migrate`). Generators provided by rails or other gems/engines can be run like normal from this engine's root (e.g. `rails g job UbiquityExporter`).
45
+
46
+ ### Development Server
47
+
48
+ To run a development server locally outside of docker do the following with each line in its own shell from the root of the engine:
49
+ ```
50
+ solr_wrapper -v --config .solr_wrapper.yml
51
+ fcrepo_wrapper -v --config .fcrepo_wrapper.yml
52
+ bundle exec rails server -b 0.0.0.0
53
+ ```
54
+
55
+ ### Testing
56
+
57
+ Tests are run automatically on CircleCI with rubocop and codeclimate. These tests must pass before pull requests can be merged.
58
+
59
+ To run the tests locally outside of docker do the following with each line in its own shell from the root of the engine:
60
+ ```
61
+ solr_wrapper -v --config .solr_wrapper_test.yml
62
+ fcrepo_wrapper -v --config .fcrepo_wrapper_test.yml
63
+ bundle exec rspec
64
+ ```
65
+ You shouldn't need to run anything from inside `spec/internal_test_hyrax` unless explicitly told to do so.
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ require 'rdoc/task'
9
+
10
+ RDoc::Task.new(:rdoc) do |rdoc|
11
+ rdoc.rdoc_dir = 'rdoc'
12
+ rdoc.title = 'Hyrax::DOI'
13
+ rdoc.options << '--line-numbers'
14
+ rdoc.rdoc_files.include('README.md')
15
+ rdoc.rdoc_files.include('lib/**/*.rb')
16
+ end
17
+
18
+ APP_RAKEFILE = File.expand_path("spec/internal_test_hyrax/Rakefile", __dir__)
19
+ load 'rails/tasks/engine.rake'
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+ require 'bundler/gem_tasks'
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module Actors
4
+ ##
5
+ # An actor that registers a DOI using the configured registar
6
+ # This actor should come after the model actor which saves the work
7
+ #
8
+ # @example use in middleware
9
+ # stack = ActionDispatch::MiddlewareStack.new.tap do |middleware|
10
+ # # middleware.use OtherMiddleware
11
+ # middleware.use Hyrax::Actors::DOIActor
12
+ # # middleware.use MoreMiddleware
13
+ # end
14
+ #
15
+ # env = Hyrax::Actors::Environment.new(object, ability, attributes)
16
+ # last_actor = Hyrax::Actors::Terminator.new
17
+ # stack.build(last_actor).create(env)
18
+ class DOIActor < AbstractActor
19
+ ##
20
+ # @return [Boolean]
21
+ #
22
+ # @see Hyrax::Actors::AbstractActor
23
+ def create(env)
24
+ # Assume the model actor has already run and saved the work
25
+ create_or_update_doi(env.curation_concern) && next_actor.create(env)
26
+ end
27
+
28
+ ##
29
+ # @return [Boolean]
30
+ #
31
+ # @see Hyrax::Actors::AbstractActor
32
+ def update(env)
33
+ create_or_update_doi(env.curation_concern) && next_actor.update(env)
34
+ end
35
+
36
+ private
37
+
38
+ def create_or_update_doi(work)
39
+ return true unless doi_enabled_work_type?(work)
40
+
41
+ Hyrax::DOI::RegisterDOIJob.perform_later(work, registrar: work.doi_registrar.presence, registrar_opts: work.doi_registrar_opts)
42
+ end
43
+
44
+ # Check if work is DOI enabled
45
+ def doi_enabled_work_type?(work)
46
+ work.class.ancestors.include? Hyrax::DOI::DOIBehavior
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/hyrax/doi .js
2
+ //= link_directory ../stylesheets/hyrax/doi .css
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require rails-ujs
14
+ //= require activestorage
15
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DOI
4
+ class ApplicationController < ActionController::Base
5
+ protect_from_forgery with: :exception
6
+
7
+ def self.search_state_class=(*)
8
+ # no-op to make Hyrax::Controller happy
9
+ end
10
+
11
+ # Include after search_state_class is defined since Hyrax::Controller calls it
12
+ include Hyrax::Controller
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DOI
4
+ class HyraxDOIController < ApplicationController
5
+ before_action :check_authorization
6
+
7
+ def create_draft_doi
8
+ draft_doi = mint_draft_doi
9
+
10
+ respond_to do |format|
11
+ format.js { render js: autofill_field(doi_attribute_name, draft_doi), status: :created }
12
+ format.json { render_json_response(response_type: :created, options: { data: draft_doi }) }
13
+ end
14
+ rescue Hyrax::DOI::DataCiteClient::Error => e
15
+ respond_to do |format|
16
+ format.js { render plain: e.message, status: :internal_server_error }
17
+ format.json { render_json_response(response_type: :internal_error, message: e.full_message) }
18
+ end
19
+ end
20
+
21
+ def autofill
22
+ doi = params['doi']
23
+
24
+ respond_to do |format|
25
+ format.js { render js: autofill_js(doi), status: :ok }
26
+ end
27
+ rescue Hyrax::DOI::NotFoundError => e
28
+ respond_to do |format|
29
+ format.js { render plain: e.message, status: :internal_server_error }
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def check_authorization
36
+ raise Hydra::AccessDenied unless current_ability.can_create_any_work?
37
+ end
38
+
39
+ def mint_draft_doi
40
+ doi_registrar.mint_draft_doi
41
+ end
42
+
43
+ def doi_registrar
44
+ # TODO: generalize this
45
+ Hyrax::Identifier::Registrar.for(:datacite, {})
46
+ end
47
+
48
+ def field_selector(attribute_name)
49
+ ".#{params[:curation_concern]}_#{attribute_name}"
50
+ end
51
+
52
+ def doi_attribute_name
53
+ params[:attribute] || "doi"
54
+ end
55
+
56
+ def hyrax_work_from_doi(doi)
57
+ meta = Bolognese::Metadata.new(input: doi)
58
+ # Check that a record was actually loaded
59
+ raise Hyrax::DOI::NotFoundError, "DOI (#{doi}) could not be found." if meta.blank? || meta.doi.blank?
60
+ meta.hyrax_work
61
+ end
62
+
63
+ # TODO: Move this out to a partial that gets rendered?
64
+ def autofill_js(doi)
65
+ # TODO: Need to wipe old data or is this just supplemental?
66
+ js = hyrax_work_from_doi(doi).attributes.collect { |k, v| autofill_field(k, v) }.reject(&:blank?).join("\n")
67
+ js << "document.location = '#metadata';"
68
+ end
69
+
70
+ # TODO: Move this out to a partial that gets rendered?
71
+ def autofill_field(attribute_name, value)
72
+ js = []
73
+ # TODO: add error handling in the JS so an error doesn't leave the autofilling incomplete
74
+ Array(value).each_with_index do |v, index|
75
+ # Is this the right way to do this?
76
+ # Need to be smarter to see if all repeated fields are filled before trying to create a new one by clicking?
77
+ js << "document.querySelectorAll('#{field_selector(attribute_name)} button.add')[0].click();" unless index.zero?
78
+ js << "document.querySelectorAll('#{field_selector(attribute_name)} .form-control')[#{index}].value = '#{helpers.escape_javascript(v)}';"
79
+ end
80
+ js.reject(&:blank?).join("\n")
81
+ end
82
+
83
+ # Override of Hyrax method (See https://github.com/samvera/hyrax/pull/4495)
84
+ # render a json response for +response_type+
85
+ def render_json_response(response_type: :success, message: nil, options: {})
86
+ json_body = Hyrax::API.generate_response_body(response_type: response_type, message: message, options: options)
87
+ render json: json_body, status: Hyrax::API.default_responses[response_type][:code]
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DOI
4
+ module DataCiteDOIFormBehavior
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ self.terms += [:doi_status_when_public]
9
+
10
+ delegate :doi_status_when_public, to: :model
11
+ end
12
+
13
+ def secondary_terms
14
+ super - [:doi_status_when_public]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DOI
4
+ module DOIFormBehavior
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ self.terms += [:doi]
9
+
10
+ delegate :doi, to: :model
11
+ end
12
+
13
+ def secondary_terms
14
+ super - [:doi]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DOI
4
+ module HelperBehavior
5
+ include Hyrax::DOI::WorkFormHelper
6
+ include Hyrax::DOI::WorkShowHelper
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DOI
4
+ module WorkFormHelper
5
+ def form_tabs_for(form:)
6
+ if form.model_class.ancestors.include? Hyrax::DOI::DOIBehavior
7
+ # TODO: Add check for feature flipper?
8
+ super.prepend("doi")
9
+ else
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DOI
4
+ module WorkShowHelper
5
+ def render_doi?(presenter)
6
+ return false unless presenter.class.ancestors.include? Hyrax::DOI::DOIPresenterBehavior
7
+ return presenter.doi_status_when_public.in? [nil, 'registered', 'findable'] if presenter.class.ancestors.include? Hyrax::DOI::DataCiteDOIPresenterBehavior
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DOI
4
+ class ApplicationJob < ActiveJob::Base
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DOI
4
+ class RegisterDOIJob < ApplicationJob
5
+ queue_as Hyrax.config.ingest_queue_name
6
+
7
+ ##
8
+ # @param model [ActiveFedora::Base]
9
+ # @param registrar [String] Note this is a string and not a symbol because ActiveJob cannot serialize a symbol
10
+ # @param registrar_opts [Hash]
11
+ def perform(model, registrar: Hyrax.config.identifier_registrars.keys.first, registrar_opts: {})
12
+ Hyrax::Identifier::Dispatcher
13
+ .for(registrar.to_sym, **registrar_opts)
14
+ .assign_for!(object: model, attribute: :doi)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DOI
4
+ module DataCiteDOIBehavior
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ property :doi_status_when_public, predicate: ::RDF::URI('http://samvera.org/ns/hyrax/doi#doi_status_when_public'), multiple: false do |index|
9
+ index.as :stored_sortable
10
+ end
11
+
12
+ validates :doi_status_when_public, inclusion: { in: Hyrax::DOI::DataCiteRegistrar::STATES }, allow_nil: true
13
+ end
14
+
15
+ # Override
16
+ def doi_registrar
17
+ 'datacite'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module DOI
4
+ module DOIBehavior
5
+ extend ActiveSupport::Concern
6
+
7
+ DOI_REGEX = /\A10\.\d{4,}(\.\d+)*\/[-._;():\/A-Za-z\d]+\z/.freeze
8
+
9
+ included do
10
+ property :doi, predicate: ::RDF::Vocab::BIBO.doi, multiple: true do |index|
11
+ index.as :stored_sortable
12
+ end
13
+
14
+ validate :validate_doi
15
+ end
16
+
17
+ # Override this method
18
+ # Specify a registrar to use with this class
19
+ def doi_registrar
20
+ nil
21
+ end
22
+
23
+ # Override this method
24
+ # Specify options for the registrar to use with this class
25
+ def doi_registrar_opts
26
+ {}
27
+ end
28
+
29
+ private
30
+
31
+ def validate_doi
32
+ Array(doi).each do |doi|
33
+ errors.add(:doi, "DOI (#{doi}) is invalid.") unless doi.match? DOI_REGEX
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end