diff --git a/.eslintrc b/.eslintrc index 63003a4174..16856ac5b1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -70,7 +70,6 @@ rules: no-implied-eval: 2 no-invalid-this: 0 no-iterator: 2 - no-labels: 0 no-lone-blocks: 2 no-loop-func: 2 no-magic-number: 0 diff --git a/.rubocop.yml b/.rubocop.yml index db5391fd0e..56e43e9d3f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -49,6 +49,10 @@ Metrics/BlockLength: Metrics/MethodLength: Max: 13 +Naming/FileName: # https://github.com/bbatsov/rubocop/issues/2973 + Exclude: + - 'Gemfile' + Style/AsciiComments: Enabled: false @@ -60,10 +64,6 @@ Style/CollectionMethods: detect: 'find' find_all: 'select' -Style/FileName: # https://github.com/bbatsov/rubocop/issues/2973 - Exclude: - - 'Gemfile' - Style/MethodMissing: Exclude: - 'app/models/concerns/hyrax/file_set/characterization.rb' @@ -140,6 +140,9 @@ RSpec/DescribeClass: - 'spec/tasks/rake_spec.rb' - 'spec/views/**/*' +RSpec/ContextWording: + Enabled: false + # By default RSpec/MessageSpies has the following: # Prefer have_received for setting message expectations. Setup form as a spy using allow or instance_spy. # The default assumes EnforcedStyle is 'have_received'. Most of our specs are 'receive' diff --git a/.rubocop_fixme.yml b/.rubocop_fixme.yml index 6bca83eb0a..446516ad6a 100644 --- a/.rubocop_fixme.yml +++ b/.rubocop_fixme.yml @@ -45,14 +45,24 @@ Metrics/MethodLength: - 'lib/hyrax/rails/routes.rb' - 'spec/support/**/*' + +Naming/PredicateName: + Exclude: + - 'app/helpers/hyrax/collections_helper.rb' + + # TODO: remove when https://github.com/bbatsov/rubocop/issues/4539 is fixed Style/FormatStringToken: Exclude: - 'config/routes.rb' -Style/PredicateName: +Style/DateTime: + Enabled: false + +# TODO: remove when https://github.com/bbatsov/rubocop/issues/5161 is fixed +Style/CommentedKeyword: Exclude: - - 'app/helpers/hyrax/collections_helper.rb' + - 'app/models/vocab/fedora_resource_status.rb' Style/SpecialGlobalVars: Exclude: diff --git a/README.md b/README.md index 2f92b81593..4a492594d2 100644 --- a/README.md +++ b/README.md @@ -249,12 +249,20 @@ You may wish to [customize your work type](https://github.com/samvera/hyrax/wiki Hyrax 2 uses a WebSocket-based user notifications system, which requires Redis. To enable user notifications, make sure that you have configured ActionCable to use Redis as the adapter in your application's `config/cable.yml`. E.g., for the `development` Rails environment: -``` yaml +```yaml development: adapter: redis url: redis://localhost:6379 ``` +Using Rails up to version 5.1.4, ActionCable will not work with the 4.x series of the `redis` gem, so you will also need to pin your application to a 3.x release by adding this to your `Gemfile`: + +```ruby +gem 'redis', '~> 3.0' +``` + +And then run `bundle update redis`. + Note that the Hyrax Management Guide contains additional information on [how to configure ActionCable in production environments](https://github.com/samvera/hyrax/wiki/Hyrax-Management-Guide#notifications). # Managing a Hyrax-based app diff --git a/app/actors/hyrax/actors/abstract_actor.rb b/app/actors/hyrax/actors/abstract_actor.rb index d31200eb5a..421a893396 100644 --- a/app/actors/hyrax/actors/abstract_actor.rb +++ b/app/actors/hyrax/actors/abstract_actor.rb @@ -1,7 +1,7 @@ module Hyrax module Actors ## - # `Hyrax::AbstractActor` implements the base (no-op) case for Hyrax Actor + # `Hyrax::Actors::AbstractActor` implements the base (no-op) case for Hyrax Actor # middleware. Concrete implementations may override any or all of the three # primary actions: # diff --git a/app/actors/hyrax/actors/attach_members_actor.rb b/app/actors/hyrax/actors/attach_members_actor.rb index 5aba91386f..3cfd2bcb6b 100644 --- a/app/actors/hyrax/actors/attach_members_actor.rb +++ b/app/actors/hyrax/actors/attach_members_actor.rb @@ -56,11 +56,11 @@ def remove(curation_concern, id) end # Determines if a hash contains a truthy _destroy key. - # rubocop:disable Style/PredicateName + # rubocop:disable Naming/PredicateName def has_destroy_flag?(hash) ActiveFedora::Type::Boolean.new.cast(hash['_destroy']) end - # rubocop:enable Style/PredicateName + # rubocop:enable Naming/PredicateName end end end diff --git a/app/actors/hyrax/actors/collections_membership_actor.rb b/app/actors/hyrax/actors/collections_membership_actor.rb index bfb9f1e917..22d4efddd0 100644 --- a/app/actors/hyrax/actors/collections_membership_actor.rb +++ b/app/actors/hyrax/actors/collections_membership_actor.rb @@ -1,43 +1,75 @@ module Hyrax module Actors - # Adds membership to and removes membership from collections + # Adds membership to and removes membership from collections. + # This decodes parameters that follow the rails nested parameters conventions: + # e.g. + # 'member_of_collections_attributes' => { + # '0' => { 'id' = '12312412'}, + # '1' => { 'id' = '99981228', '_destroy' => 'true' } + # } + # class CollectionsMembershipActor < AbstractActor # @param [Hyrax::Actors::Environment] env # @return [Boolean] true if create was successful def create(env) - collection_ids = env.attributes.delete(:member_of_collection_ids) - extract_collection_id(env, collection_ids) - assign_collections(env, collection_ids) && next_actor.create(env) + attributes_collection = env.attributes.delete(:member_of_collections_attributes) + extract_collection_id(env, attributes_collection) + assign_nested_attributes_for_collection(env, attributes_collection) && + next_actor.create(env) end # @param [Hyrax::Actors::Environment] env # @return [Boolean] true if update was successful def update(env) - collection_ids = env.attributes.delete(:member_of_collection_ids) - assign_collections(env, collection_ids) && next_actor.update(env) + attributes_collection = env.attributes.delete(:member_of_collections_attributes) + assign_nested_attributes_for_collection(env, attributes_collection) && + next_actor.update(env) end private - # Maps from collection ids to collection objects - def assign_collections(env, collection_ids) - return true unless collection_ids - multiple_memberships = Hyrax::MultipleMembershipChecker.new(item: env.curation_concern).check(collection_ids: collection_ids) - if multiple_memberships - env.curation_concern.errors.add(:collections, multiple_memberships) - return false + # Attaches any unattached members. Deletes those that are marked _delete + # @param [Hash] a collection of members + def assign_nested_attributes_for_collection(env, attributes_collection) + return true unless attributes_collection + return false unless valid_membership? attributes_collection + + attributes_collection = attributes_collection.sort_by { |i, _| i.to_i }.map { |_, attributes| attributes } + # checking for existing works to avoid rewriting/loading works that are + # already attached + existing_collections = env.curation_concern.member_of_collection_ids + attributes_collection.each do |attributes| + next if attributes['id'].blank? + if existing_collections.include?(attributes['id']) + remove(env.curation_concern, attributes['id']) if has_destroy_flag?(attributes) + else + add(env, attributes['id']) + end end - # grab/save collections this user has no edit access to - other_collections = collections_without_edit_access(env) - env.curation_concern.member_of_collections = ::Collection.find(collection_ids) - env.curation_concern.member_of_collections.concat other_collections end - def collections_without_edit_access(env) - env.curation_concern.member_of_collections.select { |coll| env.current_ability.cannot?(:edit, coll) } + # Adds the item to the ordered members so that it displays in the items + # along side the FileSets on the show page + def add(env, id) + member = Collection.find(id) + return unless env.current_ability.can?(:edit, member) + env.curation_concern.member_of_collections << member + end + + # Remove the object from the members set and the ordered members list + def remove(curation_concern, id) + member = Collection.find(id) + curation_concern.member_of_collections.delete(member) + end + + # Determines if a hash contains a truthy _destroy key. + # rubocop:disable Naming/PredicateName + def has_destroy_flag?(hash) + ActiveFedora::Type::Boolean.new.cast(hash['_destroy']) end + # rubocop:enable Naming/PredicateName - # Given an array of collection_ids when it is size: + # Given an array of collection_attributes when it is size: # * 0 do not set `env.attributes[:collection_id]` # * 1 set `env.attributes[:collection_id]` to the one and only one collection # * 2 do not set `env.attributes[:collection_id]` @@ -45,9 +77,21 @@ def collections_without_edit_access(env) # Later on in apply_permission_template_actor.rb, `env.attributes[:collection_id]` will be used to apply the # permissions of the collection to the created work. With one and only one collection, the work is seen as # being created directly in that collection. - def extract_collection_id(env, collection_ids) - return unless collection_ids && collection_ids.size == 1 - env.attributes[:collection_id] = collection_ids.first + # + # NOTE: Only called from create. All collections are being added as parents. None are being removed. + def extract_collection_id(env, collection_attributes) + return unless collection_attributes && collection_attributes.size == 1 + env.attributes[:collection_id] = collection_attributes.first['id'] + end + + def valid_membership? attributes_collection + collection_ids = attributes_collection.map { |_, attributes| attributes['id'] } + multiple_memberships = Hyrax::MultipleMembershipChecker.new(item: env.curation_concern).check(collection_ids: collection_ids) + if multiple_memberships + env.curation_concern.errors.add(:collections, multiple_memberships) + return false + end + true end end end diff --git a/app/actors/hyrax/actors/create_with_remote_files_actor.rb b/app/actors/hyrax/actors/create_with_remote_files_actor.rb index 5b015e76ca..00519855ab 100644 --- a/app/actors/hyrax/actors/create_with_remote_files_actor.rb +++ b/app/actors/hyrax/actors/create_with_remote_files_actor.rb @@ -5,6 +5,9 @@ module Actors # attributes[:remote_files] = filenames.map do |name| # { url: "https://example.com/file/#{name}", file_name: name } # end + # + # Browse everything may also return a local file. And although it's in the + # url property, it may have spaces, and not be a valid URI. class CreateWithRemoteFilesActor < Hyrax::Actors::AbstractActor # @param [Hyrax::Actors::Environment] env # @return [Boolean] true if create was successful @@ -26,10 +29,10 @@ def whitelisted_ingest_dirs Hyrax.config.whitelisted_ingest_dirs end - def validate_remote_url(url) - uri = URI.parse(URI.encode(url)) + # @param uri [URI] the uri fo the resource to import + def validate_remote_url(uri) if uri.scheme == 'file' - path = File.absolute_path(URI.decode(uri.path)) + path = File.absolute_path(CGI.unescape(uri.path)) whitelisted_ingest_dirs.any? do |dir| path.start_with?(dir) && path.length > dir.length end @@ -46,26 +49,29 @@ def attach_files(env, remote_files) return true unless remote_files remote_files.each do |file_info| next if file_info.blank? || file_info[:url].blank? - unless validate_remote_url(file_info[:url]) + # Escape any space characters, so that this is a legal URI + uri = URI.parse(Addressable::URI.escape(file_info[:url])) + unless validate_remote_url(uri) Rails.logger.error "User #{env.user.user_key} attempted to ingest file from url #{file_info[:url]}, which doesn't pass validation" return false end - create_file_from_url(env, file_info[:url], file_info[:file_name]) + create_file_from_url(env, uri, file_info[:file_name]) end true end # Generic utility for creating FileSet from a URL # Used in to import files using URLs from a file picker like browse_everything - def create_file_from_url(env, url, file_name) - ::FileSet.new(import_url: url, label: file_name) do |fs| + def create_file_from_url(env, uri, file_name) + ::FileSet.new(import_url: uri.to_s, label: file_name) do |fs| actor = Hyrax::Actors::FileSetActor.new(fs, env.user) actor.create_metadata(visibility: env.curation_concern.visibility) actor.attach_to_work(env.curation_concern) fs.save! - uri = URI.parse(URI.encode(url)) if uri.scheme == 'file' - IngestLocalFileJob.perform_later(fs, URI.decode(uri.path), env.user) + # Turn any %20 into spaces. + file_path = CGI.unescape(uri.path) + IngestLocalFileJob.perform_later(fs, file_path, env.user) else ImportUrlJob.perform_later(fs, operation_for(user: actor.user)) end diff --git a/app/actors/hyrax/actors/file_set_actor.rb b/app/actors/hyrax/actors/file_set_actor.rb index 1126b084fb..5e54dfe69b 100644 --- a/app/actors/hyrax/actors/file_set_actor.rb +++ b/app/actors/hyrax/actors/file_set_actor.rb @@ -14,7 +14,7 @@ def initialize(file_set, user) # Spawns asynchronous IngestJob unless ingesting from URL # Called from FileSetsController, AttachFilesToWorkJob, IngestLocalFileJob, ImportUrlJob - # @param [Hyrax::UploadedFile, File, ActionDigest::HTTP::UploadedFile] file the file uploaded by the user + # @param [Hyrax::UploadedFile, File] file the file uploaded by the user # @param [Symbol, #to_s] relation # @return [IngestJob, FalseClass] false on failure, otherwise the queued job def create_content(file, relation = :original_file, from_url: false) @@ -130,8 +130,6 @@ def wrapper!(file:, relation:) def label_for(file) if file.is_a?(Hyrax::UploadedFile) # filename not present for uncached remote file! file.uploader.filename.present? ? file.uploader.filename : File.basename(Addressable::URI.parse(file.file_url).path) - elsif file.respond_to?(:original_filename) # e.g. ActionDispatch::Http::UploadedFile, CarrierWave::SanitizedFile - file.original_filename elsif file.respond_to?(:original_name) # e.g. Hydra::Derivatives::IoDecorator file.original_name elsif file_set.import_url.present? diff --git a/app/assets/javascripts/hyrax.js b/app/assets/javascripts/hyrax.js index be7238d755..5f6919d01f 100644 --- a/app/assets/javascripts/hyrax.js +++ b/app/assets/javascripts/hyrax.js @@ -80,8 +80,8 @@ //= require hyrax/permissions //= require hyrax/autocomplete //= require hyrax/autocomplete/default -//= require hyrax/autocomplete/work //= require hyrax/autocomplete/linked_data +//= require hyrax/autocomplete/resource //= require hyrax/relationships //= require hyrax/select_work_type //= require hyrax/collections @@ -94,7 +94,6 @@ //= require hyrax/file_manager/save_manager //= require hyrax/file_manager/member //= require hyrax/file_manager -//= require hyrax/workflow_actions_affix //= require hyrax/authority_select //= require hyrax/sort_and_per_page //= require hyrax/thumbnail_select diff --git a/app/assets/javascripts/hyrax/app.js.erb b/app/assets/javascripts/hyrax/app.js.erb index 1caa158069..b26bd7b0e6 100644 --- a/app/assets/javascripts/hyrax/app.js.erb +++ b/app/assets/javascripts/hyrax/app.js.erb @@ -75,7 +75,7 @@ Hyrax = { var element = $("[data-behavior='work-form']") if (element.length > 0) { var Editor = require('hyrax/editor'); - new Editor(element) + new Editor(element).init(); } }, diff --git a/app/assets/javascripts/hyrax/autocomplete.es6 b/app/assets/javascripts/hyrax/autocomplete.es6 index 9a21d26381..41c1e86773 100644 --- a/app/assets/javascripts/hyrax/autocomplete.es6 +++ b/app/assets/javascripts/hyrax/autocomplete.es6 @@ -1,23 +1,28 @@ import Default from './autocomplete/default' -import Work from './autocomplete/work' +import Resource from './autocomplete/resource' import LinkedData from './autocomplete/linked_data' export default class Autocomplete { /** * Setup for the autocomplete field. * @param {jQuery} element - The input field to add autocompete to - # @param {string} fieldName - The name of the field (e.g. 'based_near') - # @param {string} url - The url for the autocompete search endpoint + * @param {string} fieldName - The name of the field (e.g. 'based_near') + * @param {string} url - The url for the autocompete search endpoint */ setup (element, fieldName, url) { switch (fieldName) { case 'work': - new Work( + new Resource( element, url, - element.data('exclude-work') + { excluding: element.data('exclude-work') } ) break + case 'collection': + new Resource( + element, + url) + break case 'based_near': new LinkedData(element, url) default: diff --git a/app/assets/javascripts/hyrax/autocomplete/default.es6 b/app/assets/javascripts/hyrax/autocomplete/default.es6 index e2bb81adb9..b443725aba 100644 --- a/app/assets/javascripts/hyrax/autocomplete/default.es6 +++ b/app/assets/javascripts/hyrax/autocomplete/default.es6 @@ -1,3 +1,4 @@ +// This script initializes a jquery-ui autocomplete widget export default class Default { constructor(element, url) { this.url = url; @@ -10,7 +11,6 @@ export default class Default { minLength: 2, source: (request, response) => { - console.log("Requesting " + this.url) $.getJSON(this.url, { q: request.term }, response ); diff --git a/app/assets/javascripts/hyrax/autocomplete/linked_data.es6 b/app/assets/javascripts/hyrax/autocomplete/linked_data.es6 index d83230bdbd..f2d1b8b513 100644 --- a/app/assets/javascripts/hyrax/autocomplete/linked_data.es6 +++ b/app/assets/javascripts/hyrax/autocomplete/linked_data.es6 @@ -1,4 +1,4 @@ -// Autocomplete for linked data elements +// Autocomplete for linked data elements using a select2 autocomplete widget // After selecting something, the seleted item is immutable export default class LinkedData { constructor(element, url) { diff --git a/app/assets/javascripts/hyrax/autocomplete/work.es6 b/app/assets/javascripts/hyrax/autocomplete/resource.es6 similarity index 69% rename from app/assets/javascripts/hyrax/autocomplete/work.es6 rename to app/assets/javascripts/hyrax/autocomplete/resource.es6 index 5768e58821..35144ffa16 100644 --- a/app/assets/javascripts/hyrax/autocomplete/work.es6 +++ b/app/assets/javascripts/hyrax/autocomplete/resource.es6 @@ -1,8 +1,14 @@ -export default class Work { - // Autocomplete for finding possible related works. - constructor(element, url, excludeWorkId) { +export default class Resource { + /** + * Autocomplete for finding possible related works. + * @param {jQuery} element - The input field to add autocompete to + * @param {string} url - The url for the autocompete search endpoint + * @param {Object} options - optional arguments + * @param {string} options.excluding - The id to exclude from the search + */ + constructor(element, url, options = {}) { this.url = url; - this.excludeWorkId = excludeWorkId; + this.excludeWorkId = options.excluding; this.initUI(element) } diff --git a/app/assets/javascripts/hyrax/editor.es6 b/app/assets/javascripts/hyrax/editor.es6 index 97e78a9c96..a9b15ca4ff 100644 --- a/app/assets/javascripts/hyrax/editor.es6 +++ b/app/assets/javascripts/hyrax/editor.es6 @@ -15,7 +15,9 @@ export default class { this.paramKey = element.data('paramKey') // The work type this.adminSetWidget = new AdminSetWidget(element.find('select[id$="_admin_set_id"]')) this.sharingTabElement = $('#tab-share') + } + init() { this.autocomplete() this.controlledVocabularies() this.sharingTab() @@ -78,9 +80,21 @@ export default class { } relationshipsControl() { - new RelationshipsControl(this.element.find('[data-behavior="child-relationships"]'), - 'work_members_attributes', - 'tmpl-child-work') + let collections = this.element.find('[data-behavior="collection-relationships"]') + collections.each((_idx, element) => + new RelationshipsControl(element, + collections.data('members'), + collections.data('paramKey'), + 'member_of_collections_attributes', + 'tmpl-collection').init()) + + let works = this.element.find('[data-behavior="child-relationships"]') + works.each((_idx, element) => + new RelationshipsControl(element, + works.data('members'), + works.data('paramKey'), + 'work_members_attributes', + 'tmpl-child-work').init()) } saveWorkControl() { diff --git a/app/assets/javascripts/hyrax/relationships.js b/app/assets/javascripts/hyrax/relationships.js index 16dd27a4c2..a31e71cce8 100644 --- a/app/assets/javascripts/hyrax/relationships.js +++ b/app/assets/javascripts/hyrax/relationships.js @@ -1,4 +1,5 @@ //= require hyrax/relationships/control //= require hyrax/relationships/registry //= require hyrax/relationships/registry_entry -//= require hyrax/relationships/work +//= require hyrax/relationships/confirm_remove_dialog +//= require hyrax/relationships/resource diff --git a/app/assets/javascripts/hyrax/relationships/confirm_remove_dialog.es6 b/app/assets/javascripts/hyrax/relationships/confirm_remove_dialog.es6 new file mode 100644 index 0000000000..e49ce00035 --- /dev/null +++ b/app/assets/javascripts/hyrax/relationships/confirm_remove_dialog.es6 @@ -0,0 +1,45 @@ +export default class ConfirmRemoveDialog { + /** + * The function to perform when the dialog is accepted. + * + * @callback requestCallback + */ + + /** + * Initialize the dialog + * @param {String} text the text for the body of the dialog + * @param {String} cancel the text for the cancel button + * @param {String} remove the text for the remove button + * @param {requestCallback} fn the function to perform if the remove button is pressed + */ + constructor(text, cancel, remove, fn) { + this.text = text + this.cancel = cancel + this.remove = remove + this.fn = fn + } + + template() { + return `` + } + + launch() { + let dialog = $(this.template()) + dialog.find('[data-behavior="submit"]').click(() => { + dialog.modal('hide'); + dialog.remove(); + this.fn(); + }) + dialog.modal('show') + } +} diff --git a/app/assets/javascripts/hyrax/relationships/control.es6 b/app/assets/javascripts/hyrax/relationships/control.es6 index 70116f6cd2..9880e2635d 100644 --- a/app/assets/javascripts/hyrax/relationships/control.es6 +++ b/app/assets/javascripts/hyrax/relationships/control.es6 @@ -1,23 +1,32 @@ import Registry from './registry' -import Work from './work' - +import Resource from './resource' +/** + * This depends on the passed in element containing `data-autocomplete="work'"` + * that is also a select2 element. +*/ export default class RelationshipsControl { /** * Initializes the class in the context of an individual table element - * @param {jQuery} element the table element that this class represents + * @param {HTMLElement} element the table element that this class represents. + * @param {Array} members the members to display in the table + * @param {String} paramKey the key for the type of object we're submitting (e.g. 'generic_work') * @param {String} property the property to submit * @param {String} templateId the template identifier for new rows */ - constructor(element, property, templateId) { - this.element = element - this.registry = new Registry(this.element, this.element.data('paramKey'), property, templateId) - - this.input = this.element.find("[data-autocomplete='work']") + constructor(element, members, paramKey, property, templateId) { + this.element = $(element) + this.members = this.element.data('members') + this.registry = new Registry(this.element.find('tbody'), paramKey, property, templateId) + this.input = this.element.find(`[data-autocomplete]`) this.warning = this.element.find(".message.has-warning") this.addButton = this.element.find("[data-behavior='add-relationship']") this.errors = null + } + + init() { this.bindAddButton(); + this.displayMembers(); } validate() { @@ -26,6 +35,12 @@ export default class RelationshipsControl { } } + displayMembers() { + this.members.forEach((elem) => + this.registry.addResource(new Resource(elem.id, elem.label)) + ) + } + isValid() { this.validate() return this.errors === null @@ -40,7 +55,7 @@ export default class RelationshipsControl { } attemptToAddRow() { - // Display an error when the input field is empty, or if the work ID is already related, + // Display an error when the input field is empty, or if the resource ID is already related, // otherwise clone the row and set appropriate styles if (this.isValid()) { this.addRow() @@ -52,7 +67,7 @@ export default class RelationshipsControl { addRow() { this.hideWarningMessage() let data = this.searchData() - this.registry.addWork(new Work(data.id, data.text)) + this.registry.addResource(new Resource(data.id, data.text)) // finally, empty the "add" row input value this.clearSearch(); diff --git a/app/assets/javascripts/hyrax/relationships/registry.es6 b/app/assets/javascripts/hyrax/relationships/registry.es6 index 81a4b5eca0..6a7d407aab 100644 --- a/app/assets/javascripts/hyrax/relationships/registry.es6 +++ b/app/assets/javascripts/hyrax/relationships/registry.es6 @@ -2,7 +2,8 @@ import RegistryEntry from './registry_entry' export default class Registry { /** * Initialize the registry - * @param {jQuery} element the jquery selector for the permissions container + * @param {jQuery} element the jquery selector for the permissions container. + * must be a table with a tbody element. * @param {String} object_name the name of the object, for constructing form fields (e.g. 'generic_work') * @param {String} templateId the the identifier of the template for the added elements */ @@ -13,40 +14,43 @@ export default class Registry { this.templateId = templateId this.items = [] this.element = element - - // the remove button is only on preexisting grants - element.find('[data-behavior="remove-relationship"]').on('click', (evt) => this.removeWork(evt)) + element.closest('form').on('submit', (evt) => { + this.serializeToForm() + }); } // Return an index for the hidden field when adding a new row. - // This makes the assumption that all the tr elements represent a work except - // for the final one, which is the "add another" form + // A large random will probably avoid collisions. nextIndex() { - return this.element.find('tbody').children('tr').length - 1; + return Math.floor(Math.random() * 1000000000000000) } - addWork(work) { - work.index = this.nextIndex() - this.items.push(new RegistryEntry(work, this, this.element.find('tr:last'), this.templateId)) - this.showSaveNote(); + export() { + return this.items.map(item => item.export()) } - // removes a row that has been persisted - removeWork(evt) { - evt.preventDefault(); - let button = $(evt.target); - let container = button.closest('tr'); - container.addClass('hidden'); // do not show the block - this.addDestroyField(container, button.attr('data-index')); - this.showSaveNote(); + serializeToForm() { + this.export().forEach((item, index) => { + this.addHiddenField(index, 'id', item.id) + this.addHiddenField(index, '_destroy', item['_destroy']) + }) } - addDestroyField(element, index) { + addHiddenField(index, key, value) { $('').attr({ type: 'hidden', - name: `${this.fieldPrefix(index)}[_destroy]`, - value: 'true' - }).appendTo(element); + name: `${this.fieldPrefix(index)}[${key}]`, + value: value + }).appendTo(this.element); + } + + // Adds the resource to the first row of the tbody + addResource(resource) { + resource.index = this.nextIndex() + let entry = new RegistryEntry(resource, this, this.templateId) + this.items.push(entry) + this.element.prepend(entry.view) + this.showSaveNote() } fieldPrefix(counter) { @@ -54,7 +58,6 @@ export default class Registry { } showSaveNote() { - // TODO: we may want to reveal a note that changes aren't active until the work is saved + // TODO: we may want to reveal a note that changes aren't active until the resource is saved } - } diff --git a/app/assets/javascripts/hyrax/relationships/registry_entry.es6 b/app/assets/javascripts/hyrax/relationships/registry_entry.es6 index f752fed37d..75d4b7357d 100644 --- a/app/assets/javascripts/hyrax/relationships/registry_entry.es6 +++ b/app/assets/javascripts/hyrax/relationships/registry_entry.es6 @@ -1,38 +1,45 @@ -export default class RegistryEntry { - /** - * Initialize the registry entry - * @param {Work} work the work to display on the form - * @param {Registry} registry the registry that holds this registry entry. - * @param {jQuery} element a place to insert the new row - * @param {String} template identifer of the new row template. - */ - constructor(work, registry, element, template) { - this.work = work - this.registry = registry - this.element = element +import ConfirmRemoveDialog from 'hyrax/relationships/confirm_remove_dialog' - let row = this.createRow(work, template); - this.addHiddenField(row, work); - row.effect("highlight", {}, 3000); - } +export default class RegistryEntry { + /** + * Initialize the registry entry + * @param {Resource} resource the resource to display on the form + * @param {Registry} registry the registry that holds this registry entry. + * @param {String} template identifer of the new row template. + */ + constructor(resource, registry, template) { + this.resource = resource + this.registry = registry + this.view = this.createView(resource, template); + this.destroyed = false + //this.view.effect("highlight", {}, 3000); + } - // Remove a row that has not been persisted - createRow(work, templateId) { - let row = $(tmpl(templateId, work)); - this.element.before(row); - row.find('[data-behavior="remove-relationship"]').click(function () { - row.remove(); - }); + export() { + return { 'id': this.resource.id, '_destroy': this.destroyed } + } - return row; - } + // Add a row that has not been persisted + createView(resource, templateId) { + let row = $(tmpl(templateId, resource)) + let removeButton = row.find('[data-behavior="remove-relationship"]') + removeButton.click((e) => { + e.preventDefault() + var dialog = new ConfirmRemoveDialog(removeButton.data('confirmText'), + removeButton.data('confirmCancel'), + removeButton.data('confirmRemove'), + () => this.removeResource(e)); + dialog.launch(); + }); + return row; + } - addHiddenField(element, work) { - var prefix = this.registry.fieldPrefix(work.index); - $('').attr({ - type: 'hidden', - name: prefix + '[id]', - value: work.id - }).appendTo(element); - } + // Hides the row and adds a _destroy=true field to the form + removeResource(evt) { + evt.preventDefault(); + let button = $(evt.target); + this.view.addClass('hidden'); // do not show the block + this.destroyed = true + this.registry.showSaveNote(); + } } diff --git a/app/assets/javascripts/hyrax/relationships/resource.es6 b/app/assets/javascripts/hyrax/relationships/resource.es6 new file mode 100644 index 0000000000..3ed3fba961 --- /dev/null +++ b/app/assets/javascripts/hyrax/relationships/resource.es6 @@ -0,0 +1,10 @@ +/* + * Represents a Child work or related Collection + */ +export default class Resource { + constructor(id, title) { + this.id = id + this.title = title + this.index = 0 + } +} diff --git a/app/assets/javascripts/hyrax/relationships/work.es6 b/app/assets/javascripts/hyrax/relationships/work.es6 deleted file mode 100644 index fcb8ebd5eb..0000000000 --- a/app/assets/javascripts/hyrax/relationships/work.es6 +++ /dev/null @@ -1,7 +0,0 @@ -export default class Work { - constructor(id, title) { - this.id = id - this.title = title - this.index = 0 - } -} diff --git a/app/assets/javascripts/hyrax/workflow_actions_affix.js b/app/assets/javascripts/hyrax/workflow_actions_affix.js deleted file mode 100644 index f4352d73ad..0000000000 --- a/app/assets/javascripts/hyrax/workflow_actions_affix.js +++ /dev/null @@ -1,14 +0,0 @@ -Blacklight.onLoad(function() { - if ($('.workflow-actions').length) { - $(document).on('scroll', function() { - var workflowDiv = $('#workflow_controls'); - var workflowDivPos = $('.workflow-actions').offset().top + $('#workflow_controls').height(); - workflowDiv.removeClass('workflow-affix'); - if(workflowDivPos > ($(window).scrollTop() + $(window).height())){ - workflowDiv.addClass('workflow-affix'); - } else { - workflowDiv.removeClass('workflow-affix'); - } - }); - } -}); diff --git a/app/assets/stylesheets/_bootstrap-default-overrides.scss b/app/assets/stylesheets/_bootstrap-default-overrides.scss index 8c911cb03c..e618583929 100644 --- a/app/assets/stylesheets/_bootstrap-default-overrides.scss +++ b/app/assets/stylesheets/_bootstrap-default-overrides.scss @@ -3,3 +3,18 @@ $brand-danger: #d33a35 !default; $brand-success: #387f38 !default; $brand-info: #2c76c7 !default; $brand-warning: #565653 !default; + +// Overrides for accessibility compliance +// Testing: WAVE evaluation tool: http://wave.webaim.org/ +// Chrome has a WAVE extension that you can toggle on/off for testing + +// Bootsrap upto v4.0 has poor styling for color-contrast that causes the WAVE +// tool to throw contrast errors +// Read more: https://getbootstrap.com/docs/4.0/getting-started/accessibility/#color-contrast +// These changes meet WCAG AA and AAA compliance standard +// I'm passing values to bootstrap variables as listed here: +// https://getbootstrap.com/docs/3.3/customize/#less-variables +// * Darkens the text for active breadcrumb on every page. Meets WCAG AA and AAA +// * Darkens the text for disabled pagination buttons on every page. +$breadcrumb-active-color: #4c4c4c; +$pagination-disabled-color: #4c4c4c; diff --git a/app/assets/stylesheets/hyrax/_accessibility.scss b/app/assets/stylesheets/hyrax/_accessibility.scss new file mode 100644 index 0000000000..39c2b4fcfa --- /dev/null +++ b/app/assets/stylesheets/hyrax/_accessibility.scss @@ -0,0 +1,16 @@ +// This stylesheet is a place to apply styling changes for improving the +// accessibility of Hyrax vanilla +// Use https://github.com/samvera/hyrax/blob/master/app/assets/stylesheets/_bootstrap-default-overrides.scss +// for overriding bootstrap variables + +// NOTE: This isn't an ideal fix. We're using select2-rails v3.5 +// for styling dropdowns. This can be fixed by updating to select2-rails +// v4.0.3 but it throws an uncaught error and fails to +// display the dropdown at all. +// TODO: I opened an issue that we should follow up on later +// https://github.com/argerim/select2-rails/issues/176 +// Darkens the 'Search for a user' text in dropdown on 'Dashboard page' +// Meets WCAG AA and AAA compliance standard +.select2-default { + color: #4c4c4c !important; +} diff --git a/app/assets/stylesheets/hyrax/_hyrax.scss b/app/assets/stylesheets/hyrax/_hyrax.scss index 49c3c3fc79..d2f09bdcc8 100644 --- a/app/assets/stylesheets/hyrax/_hyrax.scss +++ b/app/assets/stylesheets/hyrax/_hyrax.scss @@ -8,7 +8,7 @@ 'hyrax/file_manager', 'hyrax/form-progress', 'hyrax/positioning', 'hyrax/fixedsticky', 'hyrax/file_upload', 'hyrax/representative-media', 'hyrax/footer', 'hyrax/select_work_type', 'hyrax/users', 'hyrax/dashboard', - 'hyrax/sidebar', 'hyrax/controlled_vocabulary'; + 'hyrax/sidebar', 'hyrax/controlled_vocabulary', 'hyrax/accessibility'; @import 'typeahead'; @import 'sharing_buttons'; diff --git a/app/assets/stylesheets/hyrax/_positioning.scss b/app/assets/stylesheets/hyrax/_positioning.scss index 5899486a3a..499cbc7a4b 100644 --- a/app/assets/stylesheets/hyrax/_positioning.scss +++ b/app/assets/stylesheets/hyrax/_positioning.scss @@ -15,13 +15,3 @@ display: block; padding: 0.5em 0; } - -.workflow-affix { - bottom: 0; - background: #FFF; - z-index: 2; - left: 0; - right: 0; - max-width: 1140px; - margin: 0 auto; -} diff --git a/app/assets/stylesheets/hyrax/_work-show.scss b/app/assets/stylesheets/hyrax/_work-show.scss index 438e20e881..52a7bd5182 100644 --- a/app/assets/stylesheets/hyrax/_work-show.scss +++ b/app/assets/stylesheets/hyrax/_work-show.scss @@ -56,3 +56,25 @@ ul.tabular { margin-bottom: 18px; margin-top: -8px; } + +.panel-workflow { + background: #fff; + border-color: #e0e0e0; + + .panel-heading { + background-color: #e6ab5f; + color: #ffffff; + + a { + color: #ffffff; + } + } + + .panel-body { + background-color: #f2f1eb; + } +} + +.workflow-actions { + background-color: #f8f8f8; +} diff --git a/app/authorities/qa/authorities/collections.rb b/app/authorities/qa/authorities/collections.rb new file mode 100644 index 0000000000..4f54db3705 --- /dev/null +++ b/app/authorities/qa/authorities/collections.rb @@ -0,0 +1,30 @@ +module Qa::Authorities + class Collections < Qa::Authorities::Base + class_attribute :search_builder_class + self.search_builder_class = Hyrax::CollectionSearchBuilder + + def search(_q, controller) + # The Hyrax::CollectionSearchBuilder expects a current_user + return [] unless controller.current_user + repo = CatalogController.new.repository + builder = search_builder(controller) + response = repo.search(builder) + docs = response.documents + docs.map do |doc| + id = doc.id + title = doc.title + { id: id, label: title, value: id } + end + end + + private + + def search_builder(controller) + access = controller.params[:access] || 'read' + search_builder_class.new(controller) + .where(controller.params[:q]) + .with_access(access) + .rows(100) + end + end +end diff --git a/app/builders/hyrax/manifest_helper.rb b/app/builders/hyrax/manifest_helper.rb new file mode 100644 index 0000000000..f4aec34a00 --- /dev/null +++ b/app/builders/hyrax/manifest_helper.rb @@ -0,0 +1,15 @@ +module Hyrax + class ManifestHelper + include Rails.application.routes.url_helpers + include ActionDispatch::Routing::PolymorphicRoutes + + def initialize(hostname) + @hostname = hostname + end + + def polymorphic_url(record, opts = {}) + opts[:host] ||= @hostname + super(record, opts) + end + end +end diff --git a/app/controllers/concerns/hyrax/collections_controller_behavior.rb b/app/controllers/concerns/hyrax/collections_controller_behavior.rb index 3cbd57bbdc..21501b96b8 100644 --- a/app/controllers/concerns/hyrax/collections_controller_behavior.rb +++ b/app/controllers/concerns/hyrax/collections_controller_behavior.rb @@ -5,12 +5,6 @@ module CollectionsControllerBehavior include Blacklight::Base included do - before_action :filter_docs_with_read_access!, except: :show - - include Hyrax::Collections::AcceptsBatches - - # include the render_check_all view helper method - helper Hyrax::BatchEditsHelper # include the display_trophy_link view helper method helper Hyrax::TrophyHelper @@ -22,9 +16,6 @@ module CollectionsControllerBehavior :single_item_search_builder_class, :membership_service_class - alias_method :collection_search_builder_class, :single_item_search_builder_class - deprecation_deprecate collection_search_builder_class: "use single_item_search_builder_class instead" - self.presenter_class = Hyrax::CollectionPresenter # The search builder to find the collection @@ -61,9 +52,6 @@ def single_item_search_builder single_item_search_builder_class.new(self).with(params.except(:q, :page)) end - alias collection_search_builder single_item_search_builder - deprecation_deprecate collection_search_builder: "use single_item_search_builder instead" - def collection_params form_class.model_attributes(params[:collection]) end diff --git a/app/controllers/concerns/hyrax/parent_container.rb b/app/controllers/concerns/hyrax/parent_container.rb deleted file mode 100644 index 02da4fd9da..0000000000 --- a/app/controllers/concerns/hyrax/parent_container.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Hyrax - module ParentContainer - extend ActiveSupport::Concern - - included do - helper_method :parent - end - - # TODO: this is slow, refactor to return a Presenter (fetch from solr) - def parent - @parent ||= new_or_create? ? find_parent_by_id : lookup_parent_from_child - end - - def find_parent_by_id - ActiveFedora::Base.find(parent_id) - end - - def lookup_parent_from_child - # in_objects method is inherited from Hydra::PCDM::ObjectBehavior - return curation_concern.in_objects.first if curation_concern - return ParentService.parent_for(@presenter.id) if @presenter - raise "no child" - end - - def parent_id - @parent_id ||= new_or_create? ? params[:parent_id] : lookup_parent_from_child.id - end - - private - - def new_or_create? - %w[create new].include? action_name - end - end -end diff --git a/app/controllers/concerns/hyrax/works_controller_behavior.rb b/app/controllers/concerns/hyrax/works_controller_behavior.rb index 2ec8a158c7..5796427962 100644 --- a/app/controllers/concerns/hyrax/works_controller_behavior.rb +++ b/app/controllers/concerns/hyrax/works_controller_behavior.rb @@ -1,3 +1,5 @@ +require 'iiif_manifest' + module Hyrax module WorksControllerBehavior extend ActiveSupport::Concern @@ -20,7 +22,7 @@ module WorksControllerBehavior class_methods do def curation_concern_type=(curation_concern_type) - load_and_authorize_resource class: curation_concern_type, instance_name: :curation_concern, except: [:show, :file_manager, :inspect_work] + load_and_authorize_resource class: curation_concern_type, instance_name: :curation_concern, except: [:show, :file_manager, :inspect_work, :manifest] # Load the fedora resource to get the etag. # No need to authorize for the file manager, because it does authorization via the presenter. @@ -128,7 +130,10 @@ def inspect_work presenter end - # attr_writer :actor + def manifest + headers['Access-Control-Allow-Origin'] = '*' + render json: manifest_builder.to_h + end private @@ -136,6 +141,10 @@ def build_form @form = work_form_service.build(curation_concern, current_ability, self) end + def manifest_builder + ::IIIFManifest::ManifestFactory.new(presenter) + end + def actor @actor ||= Hyrax::CurationConcern.actor end diff --git a/app/controllers/hyrax/admin/admin_sets_controller.rb b/app/controllers/hyrax/admin/admin_sets_controller.rb index 44f502ff47..c0d26f3ac2 100644 --- a/app/controllers/hyrax/admin/admin_sets_controller.rb +++ b/app/controllers/hyrax/admin/admin_sets_controller.rb @@ -2,8 +2,6 @@ module Hyrax class Admin::AdminSetsController < ApplicationController include Hyrax::CollectionsControllerBehavior - # added skip to allow flash notices. see https://github.com/samvera/hyrax/issues/202 - skip_before_action :filter_docs_with_read_access! before_action :ensure_manager! load_and_authorize_resource diff --git a/app/controllers/hyrax/dashboard/collections_controller.rb b/app/controllers/hyrax/dashboard/collections_controller.rb index f79430bf6e..ef1560d676 100644 --- a/app/controllers/hyrax/dashboard/collections_controller.rb +++ b/app/controllers/hyrax/dashboard/collections_controller.rb @@ -33,9 +33,6 @@ class CollectionsController < Hyrax::My::CollectionsController :single_item_search_builder_class, :membership_service_class - alias collection_search_builder_class single_item_search_builder_class - deprecation_deprecate collection_search_builder_class: "use single_item_search_builder_class instead" - self.presenter_class = Hyrax::CollectionPresenter self.form_class = Hyrax::Forms::CollectionForm @@ -367,10 +364,7 @@ def presenter def single_item_search_builder single_item_search_builder_class.new(self).with(params.except(:q, :page)) end - - alias collection_search_builder single_item_search_builder - deprecation_deprecate collection_search_builder: "use single_item_search_builder instead" - + def collection_params @participants = extract_old_style_permission_attributes(params[:collection]) form_class.model_attributes(params[:collection]) diff --git a/app/controllers/hyrax/file_sets_controller.rb b/app/controllers/hyrax/file_sets_controller.rb index da1fd45f23..1841c60d06 100644 --- a/app/controllers/hyrax/file_sets_controller.rb +++ b/app/controllers/hyrax/file_sets_controller.rb @@ -12,7 +12,6 @@ class FileSetsController < ApplicationController helper PermissionsHelper helper_method :curation_concern - include Hyrax::ParentContainer copy_blacklight_config_from(::CatalogController) class_attribute :show_presenter, :form_class @@ -29,28 +28,12 @@ class FileSetsController < ApplicationController private :curation_concern= helper_method :file_set - # routed to /files/new - def new; end - - # routed to /files/:id/edit + # GET /concern/file_sets/:id def edit initialize_edit_form end - # routed to /files (POST) - def create - file = params.fetch(:file_set, {}).fetch(:files, []).detect { |f| f.respond_to?(:original_filename) } - return render_json_response(response_type: :bad_request, options: { message: 'Error! No file uploaded', description: 'missing file' }) unless file - return empty_file_response(file) if empty_file?(file) - process_non_empty_file(file: file) - rescue RSolr::Error::Http => error - logger.error "FileSetController::create rescued #{error.class}\n\t#{error}\n #{error.backtrace.join("\n")}\n\n" - render_json_response(response_type: :internal_error, options: { message: 'Error occurred while creating a FileSet.' }) - ensure - file.tempfile.delete if file.respond_to?(:tempfile) # remove tempfile (only if it is a temp file) - end - - # routed to /files/:id + # GET /concern/parent/:parent_id/file_sets/:id def show respond_to do |wants| wants.html { presenter } @@ -59,13 +42,14 @@ def show end end + # DELETE /concern/file_sets/:id def destroy parent = curation_concern.parent actor.destroy redirect_to [main_app, parent], notice: 'The file has been deleted.' end - # routed to /files/:id (PUT) + # PATCH /concern/file_sets/:id def update if attempt_update after_update_response @@ -78,53 +62,16 @@ def update render action: 'edit' end - # routed to /files/:id/stats + # GET /files/:id/stats def stats @stats = FileUsage.new(params[:id]) end - # routed to /files/:id/citation + # GET /files/:id/citation def citation; end private - def process_non_empty_file(file:) - # Relative path is set by the jquery uploader when uploading a directory - curation_concern.relative_path = params[:relative_path] if params[:relative_path] - actor.create_metadata(params[:file_set]) - actor.attach_to_work(find_parent_by_id) - if actor.create_content(file) - response_for_successfully_processed_file - else - msg = curation_concern.errors.full_messages.join(', ') - flash[:error] = msg - json_error "Error creating file #{file.original_filename}: #{msg}" - end - end - - def empty_file_response(file) - options = { - errors: { files: "#{file.original_filename} has no content! (Zero length file)" }, - description: t('hyrax.api.unprocessable_entity.empty_file') - } - render_json_response(response_type: :unprocessable_entity, options: options) - end - - def response_for_successfully_processed_file - respond_to do |format| - format.html do - if request.xhr? - render 'jq_upload', formats: 'json', content_type: 'text/html' - else - redirect_to [main_app, curation_concern.parent] - end - end - format.json do - render 'jq_upload', status: :created, location: polymorphic_path([main_app, curation_concern]) - end - end - end - # this is provided so that implementing application can override this behavior and map params to different attributes def update_metadata file_attributes = form_class.model_attributes(attributes) @@ -186,6 +133,7 @@ def search_builder_class end def initialize_edit_form + @parent = @file_set.in_objects.first original = @file_set.original_file @version_list = Hyrax::VersionListPresenter.new(original ? original.versions.all : []) @groups = current_user.groups @@ -215,26 +163,10 @@ def wants_to_revert? # Override this method to add additional response formats to your local app def additional_response_formats(_); end - def file_set_params - params.require(:file_set).permit( - :visibility_during_embargo, :embargo_release_date, :visibility_after_embargo, :visibility_during_lease, :lease_expiration_date, :visibility_after_lease, :visibility, title: [] - ) - end - - def empty_file?(file) - (file.respond_to?(:tempfile) && file.tempfile.size == 0) || (file.respond_to?(:size) && file.size == 0) - end - # This allows us to use the unauthorized and form_permission template in hyrax/base, # while prefering our local paths. Thus we are unable to just override `self.local_prefixes` def _prefixes @_prefixes ||= super + ['hyrax/base'] end - - def json_error(error, name = nil, additional_arguments = {}) - args = { error: error } - args[:name] = name if name - render additional_arguments.merge(json: [args]) - end end end diff --git a/app/controllers/hyrax/my/works_controller.rb b/app/controllers/hyrax/my/works_controller.rb index 0ca5367cea..4e94048a09 100644 --- a/app/controllers/hyrax/my/works_controller.rb +++ b/app/controllers/hyrax/my/works_controller.rb @@ -11,6 +11,9 @@ def search_builder_class end def index + # The user's collections for the "add to collection" form + @user_collections = collections_service.search_results(:edit) + add_breadcrumb t(:'hyrax.controls.home'), root_path add_breadcrumb t(:'hyrax.dashboard.breadcrumbs.admin'), hyrax.dashboard_path add_breadcrumb t(:'hyrax.admin.sidebar.works'), hyrax.my_works_path @@ -21,6 +24,10 @@ def index private + def collections_service + Hyrax::CollectionsService.new(self) + end + def search_action_url(*args) hyrax.my_works_url(*args) end diff --git a/app/controllers/hyrax/my_controller.rb b/app/controllers/hyrax/my_controller.rb index a61fd5a5f0..2ff4ef1e06 100644 --- a/app/controllers/hyrax/my_controller.rb +++ b/app/controllers/hyrax/my_controller.rb @@ -39,10 +39,6 @@ def self.configure_facets helper_method :suppressed_to_status def index - # The user's collections for the "add to collection" form - # TODO: could this be only on the My::WorksController? - @user_collections = collections_service.search_results(:edit) - @user = current_user (@response, @document_list) = query_solr prepare_instance_variables_for_batch_control_display @@ -65,10 +61,6 @@ def suppressed_to_status(value) private - def collections_service - Hyrax::CollectionsService.new(self) - end - # TODO: Extract a presenter object that wrangles all of these instance variables. def prepare_instance_variables_for_batch_control_display # set up some parameters for allowing the batch controls to show appropriately diff --git a/app/forms/hyrax/forms/admin/appearance.rb b/app/forms/hyrax/forms/admin/appearance.rb index 1e01b97c62..506bf8f2ea 100644 --- a/app/forms/hyrax/forms/admin/appearance.rb +++ b/app/forms/hyrax/forms/admin/appearance.rb @@ -46,7 +46,7 @@ def header_text_color # The color links def link_color - block_for('link_color', '#337ab7') + block_for('link_color', '#2e74b2') end # The color for links in the footer diff --git a/app/forms/hyrax/forms/batch_edit_form.rb b/app/forms/hyrax/forms/batch_edit_form.rb index 2a9e6380ee..1e4caa264d 100644 --- a/app/forms/hyrax/forms/batch_edit_form.rb +++ b/app/forms/hyrax/forms/batch_edit_form.rb @@ -24,15 +24,36 @@ def initialize(model, current_ability, batch_document_ids) attr_reader :batch_document_ids - # Which parameters can we accept from the form + # Returns a list of parameters we accept from the form + # rubocop:disable Metrics/MethodLength def self.build_permitted_params - (super + [:visibility_during_embargo, :embargo_release_date, - :visibility_after_embargo, :visibility_during_lease, - :lease_expiration_date, :visibility_after_lease, :visibility, - based_near_attributes: [:id, :_destroy]]) - - [{ work_members_attributes: [:id, :_destroy], - based_near_attributes: [:id, :_destroy] }] + [{ creator: [] }, + { contributor: [] }, + { description: [] }, + { keyword: [] }, + { resource_type: [] }, + { license: [] }, + { publisher: [] }, + { date_created: [] }, + { subject: [] }, + { language: [] }, + { identifier: [] }, + { based_near: [] }, + { related_url: [] }, + { permissions_attributes: [:type, :name, :access, :id, :_destroy] }, + :on_behalf_of, + :version, + :add_works_to_collection, + :visibility_during_embargo, + :embargo_release_date, + :visibility_after_embargo, + :visibility_during_lease, + :lease_expiration_date, + :visibility_after_lease, + :visibility, + { based_near_attributes: [:id, :_destroy] }] end + # rubocop:enable Metrics/MethodLength private diff --git a/app/forms/hyrax/forms/work_form.rb b/app/forms/hyrax/forms/work_form.rb index 81339e4a32..881770b3e5 100644 --- a/app/forms/hyrax/forms/work_form.rb +++ b/app/forms/hyrax/forms/work_form.rb @@ -10,13 +10,14 @@ class WorkForm # This is required so that fields_for will draw a nested form. # See ActionView::Helpers#nested_attributes_association? # https://github.com/rails/rails/blob/v5.0.2/actionview/lib/action_view/helpers/form_helper.rb#L1890 - delegate :work_members_attributes=, to: :model + delegate :work_members_attributes=, :member_of_collections_attributes=, to: :model + delegate :human_readable_type, :open_access?, :authenticated_only_access?, :open_access_with_embargo_release_date?, :private_access?, :visibility_during_embargo, :embargo_release_date, :visibility_after_embargo, :visibility_during_lease, :lease_expiration_date, :visibility_after_lease, :visibility, :in_works_ids, :depositor, :on_behalf_of, :permissions, - :member_ids, :member_of_collection_ids, to: :model + :member_ids, to: :model attr_reader :agreement_accepted @@ -41,11 +42,43 @@ def initialize(model, current_ability, controller) super(model) end + # when the add_works_to_collection parameter is set, they mean to create + # a new work and add it to that collection. + def member_of_collections + base = model.member_of_collections + return base unless @controller.params[:add_works_to_collection] + base + [Collection.find(@controller.params[:add_works_to_collection])] + end + # @return [String] an etag representing the current version of this form def version model.etag end + # backs the child work search element + # @return [NilClass] + def find_child_work; end + + def member_of_collections_json + member_of_collections.map do |coll| + { + id: coll.id, + label: coll.to_s, + path: @controller.url_for(coll) + } + end.to_json + end + + def work_members_json + work_members.map do |child| + { + id: child.id, + label: child.to_s, + path: @controller.url_for(child) + } + end.to_json + end + # The value for some fields should not be set to the defaults (['']) # because it should be an empty array instead def initialize_field(key) @@ -104,12 +137,7 @@ def collections_for_select service = Hyrax::CollectionsService.new(@controller) CollectionOptionsPresenter.new(service).select_options(:edit) end - - # Select collection(s) based on passed-in params and existing memberships. - # @return [Array] a list of collection identifiers - def member_of_collections(collection_ids) - (member_of_collection_ids + Array.wrap(collection_ids)).uniq - end + deprecation_deprecate collections_for_select: "will be removed in Hyrax 3" # Sanitize the parameters coming from the form. This ensures that the client # doesn't send us any more parameters than we expect. @@ -132,8 +160,9 @@ def self.build_permitted_params :version, :add_works_to_collection, { - work_members_attributes: [:id, :_destroy], - based_near_attributes: [:id, :_destroy] + based_near_attributes: [:id, :_destroy], + member_of_collections_attributes: [:id, :_destroy], + work_members_attributes: [:id, :_destroy] } ] end diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb deleted file mode 100644 index 8011719d0a..0000000000 --- a/app/helpers/dashboard_helper.rb +++ /dev/null @@ -1,3 +0,0 @@ -module DashboardHelper - include Hyrax::DashboardHelperBehavior -end diff --git a/app/helpers/hyrax/hyrax_helper_behavior.rb b/app/helpers/hyrax/hyrax_helper_behavior.rb index a9171fe474..60ad71156f 100644 --- a/app/helpers/hyrax/hyrax_helper_behavior.rb +++ b/app/helpers/hyrax/hyrax_helper_behavior.rb @@ -33,12 +33,14 @@ def banner_image end def orcid_label(style_class = '') - "#{image_tag 'orcid.png', alt: t('hyrax.user_profile.orcid.alt'), class: style_class} #{t('hyrax.user_profile.orcid.label')}".html_safe + 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] || '' - "#{image_tag 'zotero.png', alt: t('hyrax.user_profile.zotero.alt'), class: html_class} #{t('hyrax.user_profile.zotero.label')}".html_safe + 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) @@ -84,7 +86,7 @@ def link_to_facet(item, field) # @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".html_safe, separator = ", ") + def link_to_facet_list(values, solr_field, empty_message = "No value entered", separator = ", ") return empty_message if values.blank? facet_field = Solrizer.solr_name(solr_field, :facetable) safe_join(values.map { |item| link_to_facet(item, facet_field) }, separator) @@ -200,7 +202,7 @@ def link_to_profile(args) # @return [ActiveSupport::SafeBuffer] license links, html_safe def license_links(options) service = Hyrax::LicenseService.new - options[:value].map { |right| link_to service.label(right), right }.to_sentence.html_safe + to_sentence(options[:value].map { |right| link_to service.label(right), right }) end # A Blacklight index field helper_method @@ -208,7 +210,7 @@ def license_links(options) # @return [ActiveSupport::SafeBuffer] rights statement links, html_safe def rights_statement_links(options) service = Hyrax::RightsStatementService.new - options[:value].map { |right| link_to service.label(right), right }.to_sentence.html_safe + to_sentence(options[:value].map { |right| link_to service.label(right), right }) end def link_to_telephone(user) diff --git a/app/helpers/hyrax/trophy_helper.rb b/app/helpers/hyrax/trophy_helper.rb index c19a48f93c..9b14bf2ee6 100644 --- a/app/helpers/hyrax/trophy_helper.rb +++ b/app/helpers/hyrax/trophy_helper.rb @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- module Hyrax module TrophyHelper # rubocop:disable Metrics/MethodLength diff --git a/app/models/admin_set.rb b/app/models/admin_set.rb index 4dc07463a1..48a9eff67b 100644 --- a/app/models/admin_set.rb +++ b/app/models/admin_set.rb @@ -39,9 +39,11 @@ class AdminSet < ActiveFedora::Base index.as :symbol end + # rubocop:disable Rails/HasManyOrHasOneDependent has_many :members, predicate: Hyrax.config.admin_set_predicate, class_name: 'ActiveFedora::Base' + # rubocop:enable Rails/HasManyOrHasOneDependent before_destroy :check_if_not_default_set, :check_if_empty after_destroy :destroy_permission_template diff --git a/app/models/concerns/hyrax/solr_document_behavior.rb b/app/models/concerns/hyrax/solr_document_behavior.rb index 2992158012..ffe3a655f9 100644 --- a/app/models/concerns/hyrax/solr_document_behavior.rb +++ b/app/models/concerns/hyrax/solr_document_behavior.rb @@ -1,5 +1,3 @@ -# -*- encoding : utf-8 -*- - module Hyrax module SolrDocumentBehavior extend ActiveSupport::Concern diff --git a/app/models/concerns/hyrax/user.rb b/app/models/concerns/hyrax/user.rb index ba91cc2798..5a6e36c858 100644 --- a/app/models/concerns/hyrax/user.rb +++ b/app/models/concerns/hyrax/user.rb @@ -16,13 +16,17 @@ module Hyrax::User acts_as_messageable # Set up proxy-related relationships - has_many :proxy_deposit_requests, foreign_key: 'receiving_user_id' + has_many :proxy_deposit_requests, + foreign_key: 'receiving_user_id', + dependent: :destroy has_many :deposit_rights_given, foreign_key: 'grantor_id', class_name: 'ProxyDepositRights', dependent: :destroy has_many :can_receive_deposits_from, through: :deposit_rights_given, source: :grantee has_many :deposit_rights_received, foreign_key: 'grantee_id', class_name: 'ProxyDepositRights', dependent: :destroy has_many :can_make_deposits_for, through: :deposit_rights_received, source: :grantor - has_many :job_io_wrappers, inverse_of: 'user' + has_many :job_io_wrappers, + inverse_of: 'user', + dependent: :destroy scope :guests, ->() { where(guest: true) } scope :registered, ->() { where(guest: false) } @@ -39,7 +43,8 @@ module Hyrax::User # Add token to authenticate Arkivo API calls after_initialize :set_arkivo_token, unless: :persisted? if Hyrax.config.arkivo_api? - has_many :trophies + has_many :trophies, + dependent: :destroy has_one :sipity_agent, as: :proxy_for, dependent: :destroy, class_name: 'Sipity::Agent' end @@ -107,10 +112,10 @@ def as_json(_opts = nil) { id: user_key, text: display_name ? "#{display_name} (#{user_key})" : user_key } end + ## + # @return [String] a name for the user def name - display_name.titleize || raise - rescue - user_key + display_name || user_key end # Redefine this for more intuitive keys in Redis diff --git a/app/models/hyrax/uploaded_file.rb b/app/models/hyrax/uploaded_file.rb index b7bc631ae5..c30ada793f 100644 --- a/app/models/hyrax/uploaded_file.rb +++ b/app/models/hyrax/uploaded_file.rb @@ -5,7 +5,10 @@ class UploadedFile < ActiveRecord::Base self.table_name = 'uploaded_files' mount_uploader :file, UploadedFileUploader alias uploader file - has_many :job_io_wrappers, inverse_of: 'uploaded_file', class_name: 'JobIoWrapper' + has_many :job_io_wrappers, + inverse_of: 'uploaded_file', + class_name: 'JobIoWrapper', + dependent: :destroy belongs_to :user, class_name: '::User' before_destroy :remove_file! diff --git a/app/presenters/hyrax/collection_options_presenter.rb b/app/presenters/hyrax/collection_options_presenter.rb index be1ae0b7d8..a14a6b260b 100644 --- a/app/presenters/hyrax/collection_options_presenter.rb +++ b/app/presenters/hyrax/collection_options_presenter.rb @@ -4,6 +4,7 @@ class CollectionOptionsPresenter # @param [Hyrax::CollectionsService] service def initialize(service) @service = service + Deprecation.warn(self, 'CollectionOptionsPresenter is deprecated and will be removed in Hyrax 3') end # Return Collection selectbox options based on access type diff --git a/app/presenters/hyrax/displays_image.rb b/app/presenters/hyrax/displays_image.rb new file mode 100644 index 0000000000..021f00fb1a --- /dev/null +++ b/app/presenters/hyrax/displays_image.rb @@ -0,0 +1,39 @@ +require 'iiif_manifest' + +module Hyrax + # This gets mixed into FileSetPresenter in order to create + # a canvas on a IIIF manifest + module DisplaysImage + extend ActiveSupport::Concern + + # Creates a display image only where FileSet is an image. + # + # @return [IIIFManifest::DisplayImage] the display image required by the manifest builder. + def display_image + return nil unless ::FileSet.exists?(id) && solr_document.image? + # @todo this is slow, find a better way (perhaps index iiif url): + original_file = ::FileSet.find(id).original_file + + url = Hyrax.config.iiif_image_url_builder.call( + original_file.id, + request.base_url, + Hyrax.config.iiif_image_size_default + ) + # @see https://github.com/samvera-labs/iiif_manifest + IIIFManifest::DisplayImage.new(url, + width: 640, + height: 480, + iiif_endpoint: iiif_endpoint(original_file.id)) + end + + private + + def iiif_endpoint(file_id) + return unless Hyrax.config.iiif_image_server? + IIIFManifest::IIIFEndpoint.new( + Hyrax.config.iiif_info_url_builder.call(file_id, request.base_url), + profile: Hyrax.config.iiif_image_compliance_level_uri + ) + end + end +end diff --git a/app/presenters/hyrax/file_set_presenter.rb b/app/presenters/hyrax/file_set_presenter.rb index b5a74bbf61..4a82a29cf2 100644 --- a/app/presenters/hyrax/file_set_presenter.rb +++ b/app/presenters/hyrax/file_set_presenter.rb @@ -4,6 +4,7 @@ class FileSetPresenter include PresentsAttributes include CharacterizationBehavior include WithEvents + include DisplaysImage attr_accessor :solr_document, :current_ability, :request diff --git a/app/presenters/hyrax/fixity_status_presenter.rb b/app/presenters/hyrax/fixity_status_presenter.rb index 8b2c2e5a6e..52e861d7bc 100644 --- a/app/presenters/hyrax/fixity_status_presenter.rb +++ b/app/presenters/hyrax/fixity_status_presenter.rb @@ -10,7 +10,7 @@ class FixityStatusPresenter include ActionView::Helpers::TextHelper include ActionView::Helpers::OutputSafetyHelper - attr_reader :file_set_id, :relevant_log_records + attr_reader :file_set_id # Note this takes a file_set_id NOT a FileSet, easy use from either solr # or AF object. def initialize(file_set_id) @@ -75,7 +75,7 @@ def render_date_range # Should be all _latest_ ChecksumAuditLog about different files/versions # currently existing in specified FileSet. def relevant_log_records - @relevant_log_records = ChecksumAuditLog.latest_for_file_set_id(file_set_id) + @relevant_log_records ||= ChecksumAuditLog.latest_for_file_set_id(file_set_id) end def num_checked_files diff --git a/app/presenters/hyrax/work_show_presenter.rb b/app/presenters/hyrax/work_show_presenter.rb index d84e1bf617..a2dc88eaab 100644 --- a/app/presenters/hyrax/work_show_presenter.rb +++ b/app/presenters/hyrax/work_show_presenter.rb @@ -54,6 +54,10 @@ def download_url Hyrax::Engine.routes.url_helpers.download_url(representative_presenter, host: request.host) end + def manifest_url + manifest_helper.polymorphic_url([:manifest, self]) + end + # @return FileSetPresenter presenter for the representative FileSets def representative_presenter return nil if representative_id.blank? @@ -153,6 +157,10 @@ def featured? @featured end + def manifest_helper + @manifest_helper ||= ManifestHelper.new(request.base_url) + end + def user_can_feature_works? current_ability.can?(:create, FeaturedWork) end diff --git a/app/renderers/hyrax/renderers/license_attribute_renderer.rb b/app/renderers/hyrax/renderers/license_attribute_renderer.rb index d349d2ad35..fbfe9dd857 100644 --- a/app/renderers/hyrax/renderers/license_attribute_renderer.rb +++ b/app/renderers/hyrax/renderers/license_attribute_renderer.rb @@ -11,7 +11,7 @@ class LicenseAttributeRenderer < AttributeRenderer def attribute_value_to_html(value) begin parsed_uri = URI.parse(value) - rescue + rescue URI::InvalidURIError nil end if parsed_uri.nil? diff --git a/app/renderers/hyrax/renderers/rights_statement_attribute_renderer.rb b/app/renderers/hyrax/renderers/rights_statement_attribute_renderer.rb index 90ff31c447..e25f62fb69 100644 --- a/app/renderers/hyrax/renderers/rights_statement_attribute_renderer.rb +++ b/app/renderers/hyrax/renderers/rights_statement_attribute_renderer.rb @@ -11,7 +11,7 @@ class RightsStatementAttributeRenderer < AttributeRenderer def attribute_value_to_html(value) begin parsed_uri = URI.parse(value) - rescue + rescue URI::InvalidURIError nil end if parsed_uri.nil? diff --git a/app/search_builders/hyrax/search_filters.rb b/app/search_builders/hyrax/search_filters.rb index 8a9f57c72d..a6e567a185 100644 --- a/app/search_builders/hyrax/search_filters.rb +++ b/app/search_builders/hyrax/search_filters.rb @@ -4,6 +4,14 @@ module SearchFilters include Hyrax::FilterByType include FilterSuppressed + # TODO: move this to Hydra::AccessControlsEnforcement + # @param access [String] what access level to set. Either 'read' or 'edit' + # @return [SearchBuilder] + def with_access(access) + @discovery_permissions = Array.wrap(access) + self + end + # Override Hydra::AccessControlsEnforcement (or Hydra::PolicyAwareAccessControlsEnforcement) # Allows admin users to see everything (don't apply any gated_discovery_filters for those users) def gated_discovery_filters(permission_types = discovery_permissions, ability = current_ability) diff --git a/app/services/hyrax/collections_service.rb b/app/services/hyrax/collections_service.rb index 2d453e0e76..f9da2c6da3 100644 --- a/app/services/hyrax/collections_service.rb +++ b/app/services/hyrax/collections_service.rb @@ -20,9 +20,9 @@ def search_results(access) private def list_search_builder(access) - list_search_builder_class.new(context).rows(100).tap do |builder| - builder.discovery_permissions = [access] - end + list_search_builder_class.new(context) + .rows(100) + .with_access(access) end end end diff --git a/app/services/hyrax/file_set_fixity_check_service.rb b/app/services/hyrax/file_set_fixity_check_service.rb index c8feab4137..b25f13027f 100644 --- a/app/services/hyrax/file_set_fixity_check_service.rb +++ b/app/services/hyrax/file_set_fixity_check_service.rb @@ -22,7 +22,7 @@ module Hyrax # `max_days_between_fixity_checks` arg, which defaults to config'd # `Hyrax.config.max_days_between_fixity_checks` class FileSetFixityCheckService - attr_reader :file_set, :id, :latest_version_only, + attr_reader :id, :latest_version_only, :async_jobs, :max_days_between_fixity_checks # @param file_set [ActiveFedora::Base, String] file_set diff --git a/app/services/hyrax/iiif_authorization_service.rb b/app/services/hyrax/iiif_authorization_service.rb new file mode 100644 index 0000000000..a328a086d8 --- /dev/null +++ b/app/services/hyrax/iiif_authorization_service.rb @@ -0,0 +1,19 @@ +module Hyrax + class IIIFAuthorizationService + attr_reader :controller + def initialize(controller) + @controller = controller + end + + # @note we ignore the `action` param here in favor of the `:show` action for all permissions + def can?(_action, object) + controller.current_ability.can?(:show, file_set_id_for(object)) + end + + private + + def file_set_id_for(object) + object.id.split('/').first + end + end +end diff --git a/app/services/hyrax/parent_service.rb b/app/services/hyrax/parent_service.rb deleted file mode 100644 index fc88bb81c4..0000000000 --- a/app/services/hyrax/parent_service.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Hyrax - class ParentService - # @param [String] id - the id of a child FileSet - # @return [ActiveFedora::Base] the parent object - def self.parent_for(id) - ids = ordered_by_ids(id) - ActiveFedora::Base.find(ordered_by_ids(id).first) if ids.present? - end - - def self.ordered_by_ids(id) - if id.present? - ActiveFedora::SolrService.query("{!join from=proxy_in_ssi to=id}ordered_targets_ssim:#{id}") - .map { |x| x["id"] } - else - [] - end - end - end -end diff --git a/app/services/hyrax/working_directory.rb b/app/services/hyrax/working_directory.rb index ce62d31193..a4ab39dc82 100644 --- a/app/services/hyrax/working_directory.rb +++ b/app/services/hyrax/working_directory.rb @@ -19,14 +19,6 @@ def find_or_retrieve(repository_file_id, id, filepath = nil) copy_repository_resource_to_working_directory(repository_file, id) end - # @param [File, ActionDispatch::Http::UploadedFile] file - # @param [String] id the identifier of the FileSet - # @return [String] path of the working file - def copy_file_to_working_directory(file, id) - file_name = file.respond_to?(:original_filename) ? file.original_filename : ::File.basename(file) - copy_stream_to_working_directory(id, file_name, file) - end - # @param [ActiveFedora::File] file the resource in the repo # @param [String] id the identifier of the FileSet # @return [String] path of the working file diff --git a/app/views/_flash_msg.html.erb b/app/views/_flash_msg.html.erb index 5a0bfdc9a4..68241a883a 100644 --- a/app/views/_flash_msg.html.erb +++ b/app/views/_flash_msg.html.erb @@ -2,7 +2,7 @@ <% if flash[type].present? %> <% flash.delete(type) %> <% end %> diff --git a/app/views/catalog/_show_partials/_default.html.erb b/app/views/catalog/_show_partials/_default.html.erb deleted file mode 100644 index 24729e765e..0000000000 --- a/app/views/catalog/_show_partials/_default.html.erb +++ /dev/null @@ -1,21 +0,0 @@ -<%# default partial to display solr document fields in catalog show view -%> - -
-
-
ID:
-
<%= render_document_show_field_value(document: document, field: "id") %>
-
Download:
-
-
    - <% %> -
-
-
Copyright:
-
<%= render_document_show_field_value(document: document, field: "rights_t") %>
-
-
-
- <%= render partial: "#{params[:controller]}/_#{params[:action]}_partials/default_details", locals: { document: document } %> -
- -<%= async_load_tag(hydra_asset_downloads_path(params[:id]), 'downloads') %> diff --git a/app/views/catalog/_show_partials/_default_details.html.erb b/app/views/catalog/_show_partials/_default_details.html.erb deleted file mode 100644 index 224c31c72c..0000000000 --- a/app/views/catalog/_show_partials/_default_details.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -<%# default partial to display solr document fields in catalog show view -%> -
-
-
Date:
-
<%= render_document_show_field_value(document: document, field: "date_t") %>
-
Title:
-
<%= render_document_show_field_value(document: document, field: "title_t") %>
-
Document Type:
-
<%= render_document_show_field_value(document: document, field: "medium_t") %>
-
Location:
-
<%= render_document_show_field_value(document: document, field: "location_t") %>
-
Date:
-
<%= render_document_show_field_value(document: document, field: "date_t") %>
-
-
\ No newline at end of file diff --git a/app/views/catalog/_show_partials/_facets.html.erb b/app/views/catalog/_show_partials/_facets.html.erb deleted file mode 100644 index b1dd41840a..0000000000 --- a/app/views/catalog/_show_partials/_facets.html.erb +++ /dev/null @@ -1,52 +0,0 @@ -<% facets_display_heading ||= "" %> -<%# main container for facets/limits menu -%> -
-

<%= h(facets_display_heading) %>

- <% facet_field_names.each do |solr_fname| %> -
- <% display_facet = @response.facets.detect {|f| f.name == solr_fname} -%> - <%# raise solr_fname.inspect unless display_facet -%> - <% if display_facet && display_facet.items.length > 0 %> -

<%= facet_field_labels[solr_fname] -%>

-
    - <% item_counter = 0 %> - <% display_facet.items.each do |item| -%> - <% if item.hits > 0 %> - <% facet_count_for_value = facet_value_hits(@facet_lookup, solr_fname, item.value) %> - <% if facet_in_params? solr_fname, item.value %> -
  • - <%= h(item.value) %> (<%= format_num facet_count_for_value %>) - <% link_options = remove_facet_params(solr_fname, item.value, params).merge!({ controller: "catalog", action: "index" }) %> - [<%= link_to 'remove', url_for(link_options), class: 'remove' %>] -
  • - <% else %> - <%- if item_counter > Blacklight.config[:facet_more_num] - 1 -%> -
  • - <%- else -%> -
  • - <%- end -%> - <% link_options = add_facet_params(solr_fname, item.value).merge!({ controller: "catalog", action: "index" }) %> - <%= link_to h(item.value), url_for(link_options) %> (<%= format_num facet_count_for_value %>) -
  • - <% end -%> - <% end -%> - <% item_counter += 1 %> - <% end %> - <%= display_facet.items.length > Blacklight.config[:facet_more_num] ? "
  • more #{pluralize(2, facet_field_labels[solr_fname])[2..-1].downcase}
  • " : "" %> - <%= display_facet.items.length > Blacklight.config[:facet_more_num] ? "" : "" %> -
- <% end %> -
- <% end %> -
- diff --git a/app/views/hyrax/base/_find_work_widget.html.erb b/app/views/hyrax/base/_find_work_widget.html.erb deleted file mode 100644 index f14ddbc223..0000000000 --- a/app/views/hyrax/base/_find_work_widget.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -<%= text_field_tag "find_#{name}", - '', - placeholder: t('.placeholder', default: 'Search for a work'), - autocomplete: 'off', - style: "width: 100%", - data: { - autocomplete: 'work', - 'autocomplete-url' => Rails.application.routes.url_helpers.qa_path + '/search/find_works', - 'exclude-work': id # exclude this item from the result set. - } %> diff --git a/app/views/hyrax/base/_form_child_work_relationships.html.erb b/app/views/hyrax/base/_form_child_work_relationships.html.erb index 16f19bc4b2..035abb005f 100644 --- a/app/views/hyrax/base/_form_child_work_relationships.html.erb +++ b/app/views/hyrax/base/_form_child_work_relationships.html.erb @@ -10,39 +10,32 @@ HTML Properties: [data-behavior="child-relationships"] : allows the javascript to be initialized data-param-key : the parameter key value for this model type %> -
- <%= link_to "Attach New Work", polymorphic_path([main_app, :new, :hyrax, :parent, curation_concern.model_name.singular], parent_id: curation_concern.id), target: "_blank", class: 'btn btn-primary' %> - - +
+
+ <%= f.label :find_child_work %> + <%= f.input_field :find_child_work, + prompt: :translate, + autocomplete: 'off', + data: { + autocomplete: 'work', + 'autocomplete-url' => Rails.application.routes.url_helpers.qa_path + '/search/find_works', + 'exclude-work': f.object.model.id # exclude this item from the result set. + } %> + Add +
+ +
+ <%= link_to t('.attach_new_work'), polymorphic_path([main_app, :new, :hyrax, :parent, curation_concern.model_name.singular], parent_id: curation_concern.id), target: "_blank", class: 'btn btn-primary' %> +
+
+ - - + + - - <%= f.fields_for :work_members do |row| %> - - - - - <% end %> - - - -
<%= t('.caption') %>
Child WorkActions<%= t('.header.title') %><%= t('.header.actions') %>
- <%= link_to row.object.title.first, [main_app, row.object] %> - - Remove -
- <%= render "find_work_widget", f: f, - name: 'child_work', - id: f.object.model.id %> - - - Add -
@@ -50,6 +43,6 @@ HTML Properties: diff --git a/app/views/hyrax/base/_form_member_of_collections.html.erb b/app/views/hyrax/base/_form_member_of_collections.html.erb index c0b9031e0d..84302bf524 100644 --- a/app/views/hyrax/base/_form_member_of_collections.html.erb +++ b/app/views/hyrax/base/_form_member_of_collections.html.erb @@ -1,8 +1,46 @@ +<%# Form UI behavior code and details; +Code: + app/assets/javascripts/hyrax/relationships +CSS: + [data-behavior="remove-relationship"] : Button to remove its parent TR from the table + [data-behavior="add-relationship"] : Button to clone its parent TR and inject a new row into the table + .message.has-warning : Used to display UI errors related to input values and server errors +HTML Properties: + table: + [data-behavior="child-relationships"] : allows the javascript to be initialized + data-param-key : the parameter key value for this model type +%>

<%= t("hyrax.works.form.in_collections") %>

-
- <%= f.input :member_of_collection_ids, - as: :select, - collection: f.object.collections_for_select, - selected: f.object.member_of_collections(params[:add_works_to_collection]), - input_html: { class: 'form-control', multiple: true } %> + +
+ +
+ <%= f.label :member_of_collection_ids %> + <%= f.input_field :member_of_collection_ids, + prompt: :translate, + data: { + autocomplete: 'collection', + 'autocomplete-url' => Rails.application.routes.url_helpers.qa_path + '/search/collections?access=edit' + } %> + Add +
+ + + + + + + + + + + +
<%= t('.caption') %>
<%= t('.header.title') %><%= t('.header.actions') %>
+ + diff --git a/app/views/hyrax/base/_form_relationships.html.erb b/app/views/hyrax/base/_form_relationships.html.erb index 35952fe331..2ef2ac812e 100644 --- a/app/views/hyrax/base/_form_relationships.html.erb +++ b/app/views/hyrax/base/_form_relationships.html.erb @@ -10,6 +10,6 @@ <%= render 'form_member_of_collections', f: f %> <% if f.object.persisted? %> -

<%= t("hyrax.works.form.in_this_work") %>

+

<%= t("hyrax.works.form.in_this_work") %>

<%= render 'form_child_work_relationships', f: f %> <% end %> diff --git a/app/views/hyrax/base/_workflow_actions.html.erb b/app/views/hyrax/base/_workflow_actions.html.erb index 40c02f497e..e227bc3d7c 100644 --- a/app/views/hyrax/base/_workflow_actions.html.erb +++ b/app/views/hyrax/base/_workflow_actions.html.erb @@ -1,11 +1,11 @@ -
+
<%= form_tag main_app.hyrax_workflow_action_path(presenter), method: :put do %> -
+

Actions

diff --git a/app/views/hyrax/base/_workflow_actions_widget.erb b/app/views/hyrax/base/_workflow_actions_widget.erb index ae94beaf3c..7ada7ce954 100644 --- a/app/views/hyrax/base/_workflow_actions_widget.erb +++ b/app/views/hyrax/base/_workflow_actions_widget.erb @@ -1,3 +1,5 @@ -
- <%= render 'workflow_actions', presenter: @presenter if @presenter.workflow.actions.present? %> +
+
+ <%= render 'workflow_actions', presenter: @presenter if @presenter.workflow.actions.present? %> +
diff --git a/app/views/hyrax/base/show.html.erb b/app/views/hyrax/base/show.html.erb index 9908e6cf8e..97256f9e78 100644 --- a/app/views/hyrax/base/show.html.erb +++ b/app/views/hyrax/base/show.html.erb @@ -14,6 +14,7 @@
+ <%= render 'workflow_actions_widget', presenter: @presenter %>
<%= render 'representative_media', presenter: @presenter %> <%= render 'citations', presenter: @presenter %> @@ -26,7 +27,6 @@
<%= render 'relationships', presenter: @presenter %> <%= render 'items', presenter: @presenter %> - <%= render 'workflow_actions_widget', presenter: @presenter %> <%# TODO: we may consider adding these partials in the future %> <%#= render 'sharing_with', presenter: @presenter %> <%#= render 'user_activity', presenter: @presenter %> diff --git a/app/views/hyrax/file_sets/_form.html.erb b/app/views/hyrax/file_sets/_form.html.erb index ef3d134282..df137181e7 100644 --- a/app/views/hyrax/file_sets/_form.html.erb +++ b/app/views/hyrax/file_sets/_form.html.erb @@ -9,10 +9,10 @@
<%= f.submit( - (curation_concern.persisted? ? "Update Attached File" : %(Attach to #{parent.human_readable_type})), + (curation_concern.persisted? ? "Update Attached File" : %(Attach to #{@parent.human_readable_type})), class: 'btn btn-primary' ) %> - <%= link_to 'Cancel', parent_path(parent), class: 'btn btn-link' %> + <%= link_to 'Cancel', parent_path(@parent), class: 'btn btn-link' %>
<% end %> diff --git a/app/views/hyrax/file_sets/jq_upload.json.jbuilder b/app/views/hyrax/file_sets/jq_upload.json.jbuilder deleted file mode 100644 index be48ac58d3..0000000000 --- a/app/views/hyrax/file_sets/jq_upload.json.jbuilder +++ /dev/null @@ -1,8 +0,0 @@ -json.array! [@file_set] do |file_set| - json.name file_set.title.first - json.size file_set.file_size.first - json.url "/files/#{file_set.id}" - json.thumbnail_url file_set.id - json.delete_url "deleteme" - json.delete_type "DELETE" -end diff --git a/config/locales/hyrax.de.yml b/config/locales/hyrax.de.yml index 24dfdc522b..1911dad005 100644 --- a/config/locales/hyrax.de.yml +++ b/config/locales/hyrax.de.yml @@ -3,6 +3,7 @@ de: activefedora: models: batch_upload_item: Arbeiten im Batch-Upload + file_set: Datei activemodel: errors: messages: @@ -265,14 +266,24 @@ de: default: Sie müssen eingeloggt sein! unprocessable_entity: default: Es ist nicht möglich, die Ressource entsprechend Ihrer Anfrage, zu ändern. - empty_file: Die hochgeladene Datei hat keinen Inhalt. background_attribution_html: '' base: citations: header: 'Zitate:' + form_child_work_relationships: + actions: + remove: Entferne diese Arbeit + attach_new_work: Hinterlegen Sie neue Arbeit als Kind dieser Arbeit + caption: Diese Arbeit enthält derzeit diese Kinderarbeiten + confirm: + cancel: Stornieren + remove: Löschen + text: Durch das Entfernen dieser untergeordneten Arbeit wird sie nicht nur aus dieser übergeordneten Arbeit aus dem Repository entfernt. Möchten Sie diese Arbeit wirklich aus dieser übergeordneten Arbeit entfernen? + header: + actions: Aktion + title: Arbeitstitel form_files: dropzone: Dateien hier hinziehen. - local_upload_html:

Sie können eine oder mehr Dateien hinzufügen, mit dieser Arbeit zu verknüpfen.

local_upload_browse_everything_html: |

Sie können eine oder mehr Dateien hinzufügen, mit dieser Arbeit zu verknüpfen.

@@ -281,6 +292,18 @@ de: möglicherweise nicht in der Lage, Ihrer Anfrage nachzukommen. Wenn Sie irgendwelche Fehler beim Upload aus der Cloud erhalten, lassen Sie uns dies über %{contact_href} wissen.

+ local_upload_html: "

Sie können eine oder mehr Dateien hinzufügen, mit dieser Arbeit zu verknüpfen.

" + form_member_of_collections: + actions: + remove: Aus der Sammlung entfernen + caption: Diese Arbeit befindet sich derzeit in diesen Sammlungen + confirm: + cancel: Stornieren + remove: Löschen + text: Durch das Entfernen dieser Arbeit wird sie nicht nur aus dieser Sammlung aus dem Repository entfernt. Möchten Sie diese Arbeit wirklich aus der Sammlung entfernen? + header: + actions: Aktion + title: Sammlungstitel form_permission_under_embargo: help_html: "Diese Arbeit ist unter Embargo. Hier können Sie die Einstellungen des Embargos ändern, oder Sie können den %{edit_link} besuchen, um ihn zu deaktivieren." legend_html: Sichtbarkeit Wer sollte in der Lage sein, diesen Inhalt anzusehen oder herunterzuladen? @@ -294,6 +317,10 @@ de: required_descriptions: Beschreiben Sie Ihre Arbeit required_files: Dateien hinzufügen requirements: Bedarf + form_share: + add_sharing: Hinzufügen von Teilen + currently_sharing: Derzeit freigegeben mit + directions: Unabhängig von den Sichtbarkeitseinstellungen für diese Arbeit können Sie sie auch für andere Benutzer und Gruppen freigeben. items: actions: Aktionen date_uploaded: Datum Upload @@ -308,7 +335,7 @@ de: relationships_parent_row: label: 'In %{type}:' show: - last_modified: 'Zuletzt geändert' + last_modified: Zuletzt geändert social_media: facebook: Facebook google: Google+ @@ -872,5 +899,9 @@ de: proxy_deposit_request: sender_comment: Bemerkungen transfer_to: Benutzer + placeholders: + defaults: + find_child_work: Suche nach einer Arbeit ... + member_of_collection_ids: Wählen Sie eine Sammlung ... required: html: erforderlich diff --git a/config/locales/hyrax.en.yml b/config/locales/hyrax.en.yml index b9c18c8c5a..ebba52ad5a 100644 --- a/config/locales/hyrax.en.yml +++ b/config/locales/hyrax.en.yml @@ -13,15 +13,15 @@ en: blacklight: search: fields: + facet: + admin_set_sim: "Collection" + resource_type_sim: "Resource type" + suppressed_bsi: "Status" show: admin_set: "In Administrative Set:" based_near_label: Location contributor: Contributors keyword: Keyword - facet: - admin_set_sim: "Collection" - suppressed_bsi: "Status" - resource_type_sim: "Resource type" filters: title: "Filtering by:" start_over: Clear filters @@ -66,14 +66,14 @@ en: active_consent_to_agreement: "I have read and agree to the" admin: admin_sets: - document_list: - edit: "Edit" - no_works: "The administrative set does not contain any works." - title: "List of items in this administrative set" delete: error_default_set: "Administrative set cannot be deleted as it is the default set" error_not_empty: "Administrative set cannot be deleted as it is not empty" notification: "Administrative set successfully deleted" + document_list: + edit: "Edit" + no_works: "The administrative set does not contain any works." + title: "List of items in this administrative set" edit: header: "Edit Administrative Set" form: @@ -81,17 +81,17 @@ en: note: "Users granted a new role will only gain the role on works that are deposited after that role has been granted." permission_destroy_errors: admin_group: "The repository administrators group cannot be removed" + permission_update_errors: + error: "Invalid update option for permission template." + no_date: "A date is required for the selected release option." + no_embargo: "An embargo period is required for the selected option." + nothing: "Select release options before pressing save." permission_update_notices: new_admin_set: "The administrative set '%{name}' has been created. Use the additional tabs to define other aspects of the administrative set." - updated_admin_set: "The administrative set '%{name}' has been updated." participants: "The administrative set's participant rights have been updated" + updated_admin_set: "The administrative set '%{name}' has been updated." visibility: "The administrative set's release & visibility settings have been updated." workflow: "The administrative set's workflow has been updated." - permission_update_errors: - no_date: "A date is required for the selected release option." - no_embargo: "An embargo period is required for the selected option." - nothing: "Select release options before pressing save." - error: "Invalid update option for permission template." tabs: description: "Description" participants: "Participants" @@ -141,7 +141,6 @@ en: varies: any: "Depositor can choose any embargo length; leases are allowed" between: "Depositor can choose embargo up to date:" - period: "Depositor can choose embargo period up to:" description: "Allow depositor to choose settings:" embargo: 1yr: "1 year after deposit" @@ -149,6 +148,7 @@ en: 3yrs: "3 years after deposit" 6mos: "6 months after deposit" select: "Select embargo period.." + period: "Depositor can choose embargo period up to:" visibility: description: "Set visibility policies for the administrative set. Setting honors embargo policies above." everyone: "Public - depositor can only choose public visibility setting" @@ -249,10 +249,10 @@ en: not_empty: "Collection type cannot be altered as it has collections" features: index: - header: Features - feature: Feature - description: Description action: Action + description: Description + feature: Feature + header: Features sidebar: activity: "Activity" appearance: "Appearance" @@ -270,8 +270,8 @@ en: tasks: "Tasks" technical: "Features" transfers: "Transfers" - users: "Manage Users" user_activity: "Your activity" + users: "Manage Users" workflow_review: "Review Submissions" workflow_roles: "Workflow Roles" works: "Works" @@ -306,6 +306,7 @@ en: workflow_roles: header: "Workflow Roles" index: + current_roles: "Current Roles" delete: confirm: Are you sure? header: @@ -313,7 +314,6 @@ en: user: User new_role: "Assign Role" no_roles: No roles - current_roles: "Current Roles" workflows: index: header: "Review Submissions" @@ -339,14 +339,24 @@ en: default: "You must be logged in to do that!" unprocessable_entity: default: "The resource you attempted to modify cannot be modified according to your request." - empty_file: "The file you uploaded has no content." background_attribution_html: '' base: citations: header: 'Citations:' + form_child_work_relationships: + actions: + remove: Remove from this work + attach_new_work: Deposit new work as child of this work + caption: This work currently contains these child works + confirm: + cancel: Cancel + remove: Remove + text: "Removing this child work will not remove it from the repository, only from this parent work. Are you sure you want to remove this work from this parent work?" + header: + actions: Action + title: Work title form_files: dropzone: "Drop files here." - local_upload_html:

You can add one or more files to associate with this work.

local_upload_browse_everything_html: |

You can add one or more files to associate with this work. Add files from your local system or a cloud provider.

@@ -354,18 +364,30 @@ en: files within a short period of time, the provider may not be able to accommodate your request. If you experience errors uploading from the cloud, let us know via the %{contact_href}.

+ local_upload_html:

You can add one or more files to associate with this work.

+ form_member_of_collections: + actions: + remove: Remove from collection + caption: This work is currently in these collections + confirm: + cancel: Cancel + remove: Remove + text: "Removing this work will not remove it from the repository, only from this collection. Are you sure you want to remove this work from the collection?" + header: + actions: Action + title: Collection title form_permission_under_embargo: - legend_html: Visibility Who should be able to view or download this content? help_html: "This work is under embargo. You can change the settings of the embargo here, or you can visit the %{edit_link} to deactivate it." + legend_html: Visibility Who should be able to view or download this content? management_page: Embargo Management Page form_permission_under_lease: - legend_html: Visibility Who should be able to view or download this content? help_html: "This work is under lease. You can change the settings of the lease here, or you can visit the %{edit_link} to deactivate it." + legend_html: Visibility Who should be able to view or download this content? management_page: Lease Management Page form_progress: + required_agreement: Check deposit agreement required_descriptions: Describe your work required_files: Add files - required_agreement: Check deposit agreement requirements: Requirements form_share: add_sharing: Add Sharing @@ -398,9 +420,9 @@ en: batch_uploads: disabled: Feature disabled by administrator files: + button_label: Add New Work instructions: Each file will be uploaded to a separate new work resulting in one work per uploaded file. upload_type_instructions: To create a single work for all the files, go to - button_label: Add New Work new: header: Batch Create New Works in_collections: These Works in Collections @@ -602,6 +624,7 @@ en: select_type_of_collection: "Select type of collection" create_work: "Create Work" current_proxies: "Current Proxies" + delete_notification: "Delete Notification" heading_actions: close: "Close" create_work: "Create work" @@ -674,26 +697,25 @@ en: create_under_failed: "'%{child_title}' cannot be added to '%{parent_title}'" no_activity: "User has no recent activity" no_notifications: "User has no notifications" - delete_notification: "Delete Notification" no_transfer_requests: "You haven't received any work transfer requests" no_transfers: "You haven't transferred any work" proxy_activity: "Proxy Activity" - proxy_user: "Proxy User" proxy_delete: "Delete Proxy" + proxy_user: "Proxy User" show_admin: new_visitors: "New Visitors" registered_users: "Registered users" repository_growth: - title: Repository Growth subtitle: Past 90 days + title: Repository Growth repository_objects: - title: Repository Objects subtitle: Current Status + title: Repository Objects returning_visitors: "Returning Visitors" total_visitors: "Total Visitors" user_activity: - title: User Activity subtitle: New user signups + title: User Activity stats: collections: "Collections created" file_downloads: "Download" @@ -714,35 +736,35 @@ en: document_language: "en" edit_profile: "Edit Profile" embargoes: - index: - manage_embargoes: Manage Embargoes - active: All Active Embargoes - expired: Expired Active Embargoes - deactivated: Deactivated Embargoes edit: - manage_embargoes_html: "Manage Embargoes for %{cc} (%{cc_type})" - header: - current: Current Embargo - past: Past Embargoes - embargo_true_html: "This %{cc} is under embargo." - embargo_false_html: "This %{cc} is not currently under embargo. If you would like to apply an embargo, provide the information here." - embargo_update: Update Embargo - embargo_deactivate: Deactivate Embargo embargo_apply: Apply Embargo embargo_cancel: Cancel and manage all embargoes + embargo_deactivate: Deactivate Embargo + embargo_false_html: "This %{cc} is not currently under embargo. If you would like to apply an embargo, provide the information here." embargo_return: "Return to editing this %{cc}" + embargo_true_html: "This %{cc} is under embargo." + embargo_update: Update Embargo + header: + current: Current Embargo + past: Past Embargoes history_empty: "This %{cc} has no previous embargoes applied to it." + manage_embargoes_html: "Manage Embargoes for %{cc} (%{cc_type})" + index: + active: All Active Embargoes + deactivated: Deactivated Embargoes + expired: Expired Active Embargoes + manage_embargoes: Manage Embargoes list_expired_active_embargoes: - missing: There are no expired embargoes in effect at this time. change_all: "Change all files within %{cc} to " - deactivate_selected: Deactivate Embargoes for Selected deactivate: Deactivate Embargo + deactivate_selected: Deactivate Embargoes for Selected + missing: There are no expired embargoes in effect at this time. table_headers: - type: Type of Work - title: Title release_date: Embargo Release Date - viz_current: Current Visibility + title: Title + type: Type of Work viz_after: Visibility will Change to + viz_current: Current Visibility featured_researchers: "Featured Researchers" file_manager: link_text: 'File Manager' @@ -789,35 +811,35 @@ en: collection: 'fa fa-cubes' default: 'fa fa-cube' leases: - index: - manage_leases: Manage Leases - active: All Active Leases - expired: Expired Active Leases - deactivated: Deactivated Leases edit: - manage_leases_html: "Manage Leases for %{cc} (%{cc_type})" header: current: Current Lease past: Past Leases - lease_true_html: "This %{cc} is under lease." - lease_false_html: "This %{cc} is not currently under lease. If you would like to apply a lease, provide the information here." - lease_update: Update Lease - lease_deactivate: Deactivate Lease + history_empty: "This %{cc} has no previous leases applied to it." lease_apply: Apply Lease lease_cancel: Cancel and manage all leases + lease_deactivate: Deactivate Lease + lease_false_html: "This %{cc} is not currently under lease. If you would like to apply a lease, provide the information here." lease_return: "Return to editing this %{cc}" - history_empty: "This %{cc} has no previous leases applied to it." + lease_true_html: "This %{cc} is under lease." + lease_update: Update Lease + manage_leases_html: "Manage Leases for %{cc} (%{cc_type})" + index: + active: All Active Leases + deactivated: Deactivated Leases + expired: Expired Active Leases + manage_leases: Manage Leases list_expired_active_leases: - missing: There are no expired leases in effect at this time. change_all: "Change all files within %{cc} to " - deactivate_selected: Deactivate Leases for Selected deactivate: Deactivate Lease + deactivate_selected: Deactivate Leases for Selected + missing: There are no expired leases in effect at this time. table_headers: - type: Type of Work - title: Title release_date: Lease Release Date - viz_current: Current Visibility + title: Title + type: Type of Work viz_after: Visibility will Change to + viz_current: Current Visibility mailbox: date: 'Date' delete: 'Delete Message' @@ -844,8 +866,8 @@ en: cancel: "Cancel" tabs: about_page: "About Page" - help_page: "Help Page" agreement_page: "Deposit Agreement" + help_page: "Help Page" terms_page: "Terms of Use" updated: "Pages updated." passive_consent_to_agreement: "By saving this work I agree to the" @@ -901,9 +923,9 @@ en: menu: "Dashboard" language_switch: "Switch language" notifications: - zero: "You have no unread notifications" - one: "You have one unread notification" many: "You have %{count} unread notifications" + one: "You have one unread notification" + zero: "You have no unread notifications" profile: login: "Login" logout: "Logout" @@ -1002,12 +1024,12 @@ en: in_collections: Collections in_other_works: This Work in Other Works in_this_work: Other Works in this Work - visibility_until: 'until' tab: files: "Files" metadata: "Descriptions" relationships: "Relationships" share: "Sharing" + visibility_until: 'until' progress: header: "Save Work" show: @@ -1028,10 +1050,10 @@ en: identifier: "A unique handle identifying the collection. An example would be a DOI for a journal article, or an ISBN or OCLC number for a book." keyword: "Words or phrases you select to describe what the collection is about. These are used to search for content." language: "The language of the collection's content." + license: "Licensing and distribution information governing access to the collection. Select from the provided drop-down list." publisher: "The person or group making the collection available. Generally this is the institution." related_url: "A link to a website or other specific content (audio, video, PDF document) related to the collection. An example is the URL of a research project from which the collection was derived." resource_type: "Pre-defined categories to describe the type of content being uploaded, such as "article" or "dataset." More than one type may be selected." - license: "Licensing and distribution information governing access to the collection. Select from the provided drop-down list." subject: "Headings or index terms describing what the collection is about; these do need to conform to an existing vocabulary." title: "A name to aid in identifying a collection." collection_type: @@ -1053,10 +1075,10 @@ en: identifier: "A unique handle identifying the work. An example would be a DOI for a journal article, or an ISBN or OCLC number for a book." keyword: "Words or phrases you select to describe what the work is about. These are used to search for content." language: "The language of the work's content." + license: "Licensing and distribution information governing access to the work. Select from the provided drop-down list." publisher: "The person or group making the work available. Generally this is the institution." related_url: "A link to a website or other specific content (audio, video, PDF document) related to the work. An example is the URL of a research project from which the work was derived." resource_type: "Pre-defined categories to describe the type of content being uploaded, such as "article" or "dataset." More than one type may be selected." - license: "Licensing and distribution information governing access to the work. Select from the provided drop-down list." subject: "Headings or index terms describing what the work is about; these do need to conform to an existing vocabulary." title: "A name to aid in identifying a work." labels: @@ -1092,7 +1114,11 @@ en: visibility_during_embargo: 'Restricted to' visibility_during_lease: 'Is available to' proxy_deposit_request: - transfer_to: "User" sender_comment: "Comments" + transfer_to: "User" + placeholders: + defaults: + find_child_work: 'Search for a work…' + member_of_collection_ids: 'Select a collection…' required: html: 'required' diff --git a/config/locales/hyrax.es.yml b/config/locales/hyrax.es.yml index 9ff30089be..f14bcd6227 100644 --- a/config/locales/hyrax.es.yml +++ b/config/locales/hyrax.es.yml @@ -3,6 +3,7 @@ es: activefedora: models: batch_upload_item: Trabajos por Lote + file_set: Archivo activemodel: errors: messages: @@ -260,20 +261,42 @@ es: default: "¡Debe iniciar sesión para hacer eso!" unprocessable_entity: default: El recurso que intentó modificar no se puede modificar de acuerdo a su solicitud. - empty_file: El archivo que subió no tiene contenido. background_attribution_html: '' base: citations: header: 'Citaciones:' + form_child_work_relationships: + actions: + remove: Eliminar de este trabajo + attach_new_work: Depositar nuevo trabajo como hijo de este trabajo + caption: Este trabajo actualmente contiene estos trabajos infantiles + confirm: + cancel: Cancelar + remove: retirar + text: La eliminación de este trabajo secundario no lo eliminará del repositorio, solo de este trabajo principal. ¿Seguro que quieres eliminar este trabajo de este trabajo principal? + header: + actions: Acción + title: Título de trabajo form_files: dropzone: Soltar archivos aquí. - local_upload_html:

Puede agregar uno o más archivos para asociar con esta obra.

local_upload_browse_everything_html: |

Puede agregar uno o más archivos para asociar con esta obra.

En caso de subir una gran cantidad de archivos en un corto periodo de tiempo, el proveedor en la nube podría no ser capaz de resolver la petición. Si se experimentan errores durante la carga, por favor, comuníquese con nosotros a través de %{contact_href}.

+ local_upload_html: "

Puede agregar uno o más archivos para asociar con esta obra.

" + form_member_of_collections: + actions: + remove: Eliminar de la colección + caption: Este trabajo se encuentra actualmente en estas colecciones + confirm: + cancel: Cancelar + remove: retirar + text: La eliminación de este trabajo no lo eliminará del repositorio, solo de esta colección. ¿Seguro que quieres eliminar este trabajo de la colección? + header: + actions: Acción + title: Título de la colección form_permission_under_embargo: help_html: "Esta obra está bajo embargo. Puede cambiar la configuración del embargo aquí, o puede visitar el %{edit_link} para desactivarlo." legend_html: Visibilidad ¿Quién debería ser capaz de ver o descargar este contenido? @@ -287,6 +310,10 @@ es: required_descriptions: Describa su trabajo required_files: Añadir archivos requirements: Requisitos + form_share: + add_sharing: Agregar Compartir + currently_sharing: Actualmente compartido con + directions: Independientemente de la configuración de visibilidad para este trabajo, también puede compartirlo con otros usuarios y grupos. items: actions: Acciones date_uploaded: Fecha de subida @@ -301,7 +328,7 @@ es: relationships_parent_row: label: 'En %{type}:' show: - last_modified: 'Última modificación' + last_modified: Última modificación social_media: facebook: Facebook google: Google+ @@ -867,5 +894,9 @@ es: proxy_deposit_request: sender_comment: Comentarios transfer_to: Usuario + placeholders: + defaults: + find_child_work: Busque un trabajo ... + member_of_collection_ids: Seleccione una colección ... required: html: obligatorio diff --git a/config/locales/hyrax.fr.yml b/config/locales/hyrax.fr.yml index b5d66c3be2..a2e82a782e 100644 --- a/config/locales/hyrax.fr.yml +++ b/config/locales/hyrax.fr.yml @@ -3,6 +3,7 @@ fr: activefedora: models: batch_upload_item: Travaux par lot + file_set: Fichier activemodel: errors: messages: @@ -265,14 +266,24 @@ fr: default: Vous devez être connecté pour faire cela! unprocessable_entity: default: La ressource que vous avez tenté de modifier ne peut pas être modifiée selon votre demande. - empty_file: Le fichier que vous avez téléchargé n'a aucun contenu. background_attribution_html: '' base: citations: header: 'Citations:' + form_child_work_relationships: + actions: + remove: Supprimer de ce travail + attach_new_work: Déposer un nouveau travail en tant qu'enfant de ce travail + caption: Ce travail contient actuellement ces travaux d'enfants + confirm: + cancel: Annuler + remove: Retirer + text: Supprimer ce travail enfant ne le supprimera pas du référentiel, uniquement à partir de ce travail parent. Êtes-vous sûr de vouloir supprimer ce travail de ce travail parent? + header: + actions: action + title: Titre du travail form_files: dropzone: Déposez les fichiers ici. - local_upload_html:

Vous pouvez ajouter un ou plusieurs fichiers à associer à ce travail.

local_upload_browse_everything_html: |

Vous pouvez ajouter un ou plusieurs fichiers à associer à ce travail.

Notez que si vous téléchargez un grand nombre de fichiers dans un @@ -280,6 +291,18 @@ fr: répondre à votre demande. Si vous rencontrez des erreurs de téléchargement depuis le nuage, faites-nous savoir via le %{contact_href}.

+ local_upload_html: "

Vous pouvez ajouter un ou plusieurs fichiers à associer à ce travail.

" + form_member_of_collections: + actions: + remove: Supprimer de la collection + caption: Ce travail est actuellement dans ces collections + confirm: + cancel: Annuler + remove: Retirer + text: La suppression de ce travail ne le supprimera pas du référentiel, mais seulement de cette collection. Êtes-vous sûr de vouloir supprimer ce travail de la collection? + header: + actions: action + title: Titre de la collection form_permission_under_embargo: help_html: "Ce travail est sous embargo. Vous pouvez modifier les paramètres de l'embargo ici, ou vous pouvez visiter le %{edit_link} pour le désactiver." legend_html: Visibilité Qui devrait pouvoir afficher ou télécharger ce contenu? @@ -293,6 +316,10 @@ fr: required_descriptions: Décrivez votre travail required_files: Ajouter des fichiers requirements: Exigences + form_share: + add_sharing: Ajouter un partage + currently_sharing: Actuellement partagé avec + directions: Indépendamment des paramètres de visibilité pour ce travail, vous pouvez également le partager avec d'autres utilisateurs et groupes. items: actions: actes date_uploaded: Date de téléchargement @@ -307,7 +334,7 @@ fr: relationships_parent_row: label: 'Dans %{type}:' show: - last_modified: 'Dernière modification' + last_modified: Dernière modification social_media: facebook: Facebook google: Google+ @@ -871,5 +898,9 @@ fr: proxy_deposit_request: sender_comment: commentaires transfer_to: Utilisateur + placeholders: + defaults: + find_child_work: Rechercher un travail ... + member_of_collection_ids: Sélectionnez une collection ... required: html: Champs obligatoires diff --git a/config/locales/hyrax.it.yml b/config/locales/hyrax.it.yml index 965611a8e3..7404d08c60 100644 --- a/config/locales/hyrax.it.yml +++ b/config/locales/hyrax.it.yml @@ -3,6 +3,7 @@ it: activefedora: models: batch_upload_item: Opere per Batch + file_set: File activemodel: errors: messages: @@ -265,20 +266,42 @@ it: default: Devi essere autenticato per farlo! unprocessable_entity: default: La risorsa che hai tentato di modificare non può essere modificata in base alla tua richiesta. - empty_file: Il file che hai caricato non ha alcun contenuto. background_attribution_html: '' base: citations: header: 'citazioni:' + form_child_work_relationships: + actions: + remove: Rimuovi da questo lavoro + attach_new_work: Deposita un nuovo lavoro come figlio di questo lavoro + caption: Attualmente questo lavoro contiene queste opere per bambini + confirm: + cancel: Annulla + remove: Rimuovere + text: La rimozione di questo lavoro figlio non lo rimuoverà dal repository, ma solo da questo lavoro genitore. Sei sicuro di voler rimuovere questo lavoro da questo lavoro genitore? + header: + actions: Azione + title: Titolo del lavoro form_files: dropzone: Drop file qui. - local_upload_html:

È possibile aggiungere uno o più file da associare a questo lavoro.

local_upload_browse_everything_html: |

È possibile aggiungere uno o più file da associare a questo lavoro.

Tieni presente che se carichi un gran numero di file in un breve periodo di tempo, il provider di cloud potrebbe non essere in grado di accogliere la tua richiesta. Se si verificano errori di caricamento dalla nube, fateci sapere tramite %{contact_href}.

+ local_upload_html: "

È possibile aggiungere uno o più file da associare a questo lavoro.

" + form_member_of_collections: + actions: + remove: Rimuovi dalla raccolta + caption: Questo lavoro è attualmente in queste raccolte + confirm: + cancel: Annulla + remove: Rimuovere + text: La rimozione di questo lavoro non la rimuoverà dal repository, ma solo da questa raccolta. Sei sicuro di voler rimuovere questo lavoro dalla raccolta? + header: + actions: Azione + title: Titolo della collezione form_permission_under_embargo: help_html: "Questo lavoro è sotto embargo. È possibile modificare le impostazioni dell'embargo qui oppure è possibile visitare lo %{edit_link} per disattivarlo." legend_html: Visibilità Chi dovrebbe essere in grado di visualizzare o scaricare questo contenuto? @@ -292,6 +315,10 @@ it: required_descriptions: Descrivi il tuo lavoro required_files: Aggiungere i file requirements: Requisiti + form_share: + add_sharing: Aggiungi condivisione + currently_sharing: Attualmente condiviso con + directions: Indipendentemente dalle impostazioni di visibilità per questo lavoro, puoi anche condividerlo con altri utenti e gruppi. items: actions: Azioni date_uploaded: Data caricata @@ -306,7 +333,7 @@ it: relationships_parent_row: label: 'In %{type}:' show: - last_modified: 'Ultima modifica' + last_modified: Ultima modifica social_media: facebook: Facebook google: Google+ @@ -870,5 +897,9 @@ it: proxy_deposit_request: sender_comment: Commenti transfer_to: Utente + placeholders: + defaults: + find_child_work: Cerca un lavoro ... + member_of_collection_ids: Seleziona una collezione ... required: html: necessario diff --git a/config/locales/hyrax.pt-BR.yml b/config/locales/hyrax.pt-BR.yml index 0d2dd87ac8..2781f85748 100644 --- a/config/locales/hyrax.pt-BR.yml +++ b/config/locales/hyrax.pt-BR.yml @@ -3,6 +3,7 @@ pt-BR: activefedora: models: batch_upload_item: Obras por lote + file_set: Arquivo activemodel: errors: messages: @@ -265,21 +266,37 @@ pt-BR: default: Você deve estar logado para fazer isso! unprocessable_entity: default: O recurso que você tentou modificar não pode ser modificado de acordo com sua solicitação. - empty_file: O arquivo que você enviou não possui conteúdo. background_attribution_html: '' base: citations: header: 'Citações:' + form_child_work_relationships: + actions: + remove: Retire deste trabalho + attach_new_work: Deposite o novo trabalho como filho desse trabalho + caption: Este trabalho atualmente contém esses trabalhos infantis + confirm: + cancel: Cancelar + remove: Remover + text: A remoção deste trabalho infantil não o removerá do repositório, apenas deste trabalho pai. Tem certeza de que deseja remover esse trabalho desse trabalho pai? + header: + actions: Açao + title: Título do trabalho form_files: dropzone: Largue os arquivos aqui. - local_upload_html:

Você pode adicionar um ou mais arquivos para associar a este trabalho.

- local_upload_browse_everything_html: -

Você pode adicionar um ou mais arquivos para associar a este - trabalho.

-

Por favor, note que, se você carregar uma grande quantidade de - arquivos dentro de um curto período de tempo, o fornecedor da nuvem - pode não ser capaz de acomodar sua solicitação. Se você tiver algum - erro ao carregar da nuvem, avise-nos através do %{contact_href}.

+ local_upload_browse_everything_html: "

Você pode adicionar um ou mais arquivos para associar a este trabalho.

Por favor, note que, se você carregar uma grande quantidade de arquivos dentro de um curto período de tempo, o fornecedor da nuvem pode não ser capaz de acomodar sua solicitação. Se você tiver algum erro ao carregar da nuvem, avise-nos através do %{contact_href}.

" + local_upload_html: "

Você pode adicionar um ou mais arquivos para associar a este trabalho.

" + form_member_of_collections: + actions: + remove: Remover da coleção + caption: Este trabalho está atualmente nessas coleções + confirm: + cancel: Cancelar + remove: Remover + text: A remoção deste trabalho não o removerá do repositório, apenas dessa coleção. Tem certeza de que deseja remover esse trabalho da coleção? + header: + actions: Açao + title: Título da coleção form_permission_under_embargo: help_html: "Este trabalho está sob embargo. Você pode alterar as configurações do embargo aqui, ou você pode visitar o %{edit_link} para desativá-lo." legend_html: Visibilidade Quem deve poder visualizar ou fazer o download desse conteúdo? @@ -293,6 +310,10 @@ pt-BR: required_descriptions: Descreva seu trabalho required_files: Adicionar arquivos requirements: Requisitos + form_share: + add_sharing: Adicionar compartilhamento + currently_sharing: Atualmente compartilhado com + directions: Independentemente das configurações de visibilidade para este trabalho, você também pode compartilhá-lo com outros usuários e grupos. items: actions: Ações date_uploaded: Data carregada @@ -307,7 +328,7 @@ pt-BR: relationships_parent_row: label: 'Em %{type}:' show: - last_modified: 'Última modificação' + last_modified: Última modificação social_media: facebook: Facebook google: Google+ @@ -871,5 +892,9 @@ pt-BR: proxy_deposit_request: sender_comment: Comentários transfer_to: Do utilizador + placeholders: + defaults: + find_child_work: Procure um trabalho ... + member_of_collection_ids: Selecione uma coleção ... required: html: requeridos diff --git a/config/locales/hyrax.zh.yml b/config/locales/hyrax.zh.yml index 2dad1de3a1..62637d0cde 100644 --- a/config/locales/hyrax.zh.yml +++ b/config/locales/hyrax.zh.yml @@ -3,6 +3,7 @@ zh: activefedora: models: batch_upload_item: 批量上传的作品 + file_set: 文件 activemodel: errors: messages: @@ -25,15 +26,15 @@ zh: contributor: 贡献者 keyword: 关键词 filters: - title: '过滤:' + title: '筛选:' rss_feed: RSS Feed - start_over: 清除过滤 + start_over: 清除筛选 errors: messages: carrierwave_download_error: 图片不能下载。 carrierwave_integrity_error: 不是图片。 carrierwave_processing_error: 图片大小不能调整。 - extension_blacklist_error: "%{extension} 的文件不能上传, 禁止上传的扩展类型: %{prohibited_types}" + extension_blacklist_error: "%{extension} 的文件不能上传, 限制上传的扩展类型: %{prohibited_types}" extension_whitelist_error: "%{extension} 的文件不能上传, 可上传的扩展类型: %{allowed_types}" helpers: action: @@ -72,8 +73,8 @@ zh: notification: 管理集删除成功 document_list: edit: 编辑 - no_works: 管理集中不包含任何作品。 - title: 管理集中的项目列表 + no_works: 管理集不包含任何作品 + title: 管理集清单 edit: header: 编辑管理集 form: @@ -140,21 +141,21 @@ zh: no_delay: 没有延迟 -- 所有作品一旦存储即可发布 title: 发布 varies: - any: 允许存款人决定 - between: 在'现在'之间 - description: '可变的 -- 存储者可以设置一件作品的发布日期:' + any: 存储者可以选择任何延迟发布时间 + between: 存储者可以选择最近时间 + description: '允许存储者设置发布日期:' embargo: 1yr: 存储一年之后 2yrs: 存储两年之后 3yrs: 存储三年之后 6mos: 存储六个月之后 - select: 选择时滞期限.. - period: 存款人可以选择禁运期限: + select: 选择延迟发布期限.. + period: 存储者可以选择延迟发布期限: visibility: description: '发布日期之后, 管理集内的作品可以被发现和下载:' - everyone: 每个人 -- 管理集内的所有作品会公开 + everyone: 公开 -- 管理集内的所有作品会公开 institution: 机构 -- 所有作品只对机构内的认证用户公开 - restricted: 有限制的 -- 所有作品只会对存储库的管理者和此管理集的管理者与评审者公开 + restricted: 私有 -- 所有作品只会对存储库的管理员和此管理集的管理员与评审员公开 title: 公开度 varies: 可变的 -- 默认值是公开, 但是存储者可以限制个别作品的公开度 form_workflow: @@ -220,7 +221,7 @@ zh: headers: main: 工作统计 total: 总作品: - visibility: 总计可见度 + visibility: 总计公开度 users: index: access_label: 上次访问 @@ -249,7 +250,7 @@ zh: under_review: 在审查中 api: accepted: - default: 您的请求已被接受,正在处理中。参见??See job for more info。 + default: 您的请求已被接受,还在处理中,详细信息请见后台作业。 bad_request: default: 不能处理您的请求,参见错误信息。 deleted: @@ -257,40 +258,66 @@ zh: forbidden: default: 您无权访问此内容。 internal_error: - default: 服务器遇到错误。 + default: 服务器遇到问题。 not_found: default: 找不到您要求的文件 success: default: 您的请求已被成功处理。 unauthorized: - default: 您必须要先登录才可以做这一点! + default: 您必须要先登录才可以做! unprocessable_entity: - default: 您试着要修改的资源按照您的请求是不能被修改。 - empty_file: 您上传的文件无内容。 + default: 您试着要修改的资源按照您的请求无法被修改。 background_attribution_html: '' base: citations: header: '引文:' + form_child_work_relationships: + actions: + remove: 从这个工作中删除 + attach_new_work: 作为这项工作的孩子存入新的工作 + caption: 这项工作目前包含这些儿童作品 + confirm: + cancel: 取消 + remove: 去掉 + text: 删除这个孩子的工作将不会将其从存储库中删除,只能从这个父母的工作。你确定要从这项家务中删除这项工作吗? + header: + actions: 行动 + title: 职称 form_files: dropzone: 请将文件拖放此处。 - local_upload_html:

您可以添加一个或多个文件与该工作相关联。

local_upload_browse_everything_html: |

您可以添加一个或多个文件与该工作相关联。

请注意如果您在短时间内上传了大量文件, 云提供商可能不能满足您的请求。 如果您上传中遇到问题,请用%{contact_href}报告。

+ local_upload_html: "

您可以添加一个或多个文件与该工作相关联。

" + form_member_of_collections: + actions: + remove: 从收藏中删除 + caption: 这项工作目前在这些收藏 + confirm: + cancel: 取消 + remove: 去掉 + text: 删除这项工作不会将其从存储库中删除,只能从这个集合中删除。你确定你想从集合中删除这项工作吗? + header: + actions: 行动 + title: 集合标题 form_permission_under_embargo: - help_html: "这项工作受到禁运。您可以在此更改禁令的设置,也可以访问%{edit_link}以停用它。" - legend_html: 可见性谁可以查看或下载此内容? - management_page: 禁运管理页面 + help_html: "这件作品限制公开。您可以在此更改设置,也可以访问%{edit_link}以取消限制。" + legend_html: 公开度谁可以查看或下载此内容? + management_page: 限制管理页面 form_permission_under_lease: - help_html: "这项工作正在进行中。您可以在此处更改租约的设置,也可以访问%{edit_link}以停用该租约。" - legend_html: 可见性谁可以查看或下载此内容? - management_page: 租赁管理页面 + help_html: "这件作品被租借。您可以在此处更改租借的设置,也可以访问%{edit_link}以停用该租约。" + legend_html: 公开度谁可以查看或下载此内容? + management_page: 租约管理页面 form_progress: required_agreement: 核对存储协议 required_descriptions: 描述您的作品 required_files: 添加文件 requirements: 必要条件 + form_share: + add_sharing: 添加共享 + currently_sharing: 目前与...共享 + directions: 无论此工作的可见性设置如何,您也可以与其他用户和组共享。 items: actions: 行动 date_uploaded: 上传日期 @@ -305,7 +332,7 @@ zh: relationships_parent_row: label: '属于 %{type}:' show: - last_modified: '最新修改' + last_modified: 最新修改 social_media: facebook: Facebook google: Google+ @@ -316,11 +343,11 @@ zh: resource_type: 您可以选择适用于所有文件的多种类型 title: 文件名将作为默认标题。请提供更有意义的标题,文件名将会在系统内保留。 batch_uploads: - disabled: 功能由管理员禁用 + disabled: 功能被管理员禁用 files: button_label: 添加新作品 instructions: 上传的每份文件都会分别按新作品生成。 - upload_type_instructions: 如果建立一件作品包括所有文件,请去 + upload_type_instructions: 创建一件包括所有文件的作品,请去 new: header: 批量建立新作品 in_collections: 这些作品包括在收藏集中 @@ -368,15 +395,15 @@ zh: tabs: description: 描述 sharing: 分享 - visibility: 能见度 + visibility: 公开度 new: header: 新系列 search_form: - button_label: 走 + button_label: 去 label: 搜索收藏集 %{title} placeholder: 搜索收藏集 show: - works_in_collection: 作品在此集合 + works_in_collection: 作品在此收藏集 contact_form: button_label: 发送 email_label: 您的邮件 @@ -421,16 +448,16 @@ zh: admin: 管理 collections: edit: - header: 编辑集合:%{title} + header: 编辑收藏集:%{title} form: tabs: description: 描述 sharing: 分享 visibility: 能见度 new: - header: 创建新集合 + header: 创建新收藏集 show: - works_in_collection: 在这个集合的作品 + works_in_collection: 在这个收藏集的作品 create_work: 创建作品 current_proxies: 目前代理 delete_notification: 删除通知 @@ -441,18 +468,18 @@ zh: manage_proxies: 管理代理 my: action: - collection_confirmation: 从%{application_name}删除是永久性的。点击 OK 从%{application_name}删除此收藏集,或取消删除。 + collection_confirmation: 删除收藏集, 从%{application_name}删除收藏集是永久性的。从%{application_name}点击'OK'删除此收藏集,或点击'取消'取消操作。 delete_collection: 删除收藏集 delete_work: 删除作品 edit_collection: 编辑收藏集 edit_work: 编辑作品 - highlight: 突出工作在配置文件 - select: 选择行动 + highlight: 在个人资料栏加重点作品 + select: 选择 select_all: 选择当前页面 - select_none: 选择无 + select_none: 选择没有 transfer: 转让作品所有权 - unhighlight: 从工作中删除高亮 - work_confirmation: 从%{application_name}删除作品是永久性的。点击 OK 从%{application_name}删除此作品,或取消删除。 + unhighlight: 删除重点作品 + work_confirmation: 删除作品,从%{application_name}删除作品是永久性的。从%{application_name}点击'OK'删除此收藏集,或点击'取消'取消操作。 collection_list: description: 描述: edit_access: 编辑访问: @@ -460,20 +487,20 @@ zh: users: '用户:' collections: 你的收藏集 facet_label: - collections: '过滤您的收藏集:' - highlighted: '过滤您的收藏精品:' - shared: '过滤您分享:' - works: '过滤作品:' + collections: '筛选您的收藏集:' + highlighted: '筛选您的收藏精品:' + shared: '筛选您的分享:' + works: '筛选作品:' heading: action: 行动 date_uploaded: 上传日期 - highlighted: 突出 + highlighted: 重点 title: 标题 visibility: 公开度 highlighted: 我的重点 shared: 与我分享的作品 sr: - batch_checkbox: 点击向收藏集添加或者编辑列表 + batch_checkbox: 点击向收藏集添加或者编辑清单 check_all_label: 选择添加或编辑所有文件 detail_label: 展示概括细节 listing: 您存储细项列单 @@ -521,34 +548,34 @@ zh: edit_profile: 编辑个人资料 embargoes: edit: - embargo_apply: 申请禁运 - embargo_cancel: 取消并管理所有禁运 - embargo_deactivate: 停用禁运 - embargo_false_html: "这个%{cc}目前没有受到禁运。如果您想申请禁运,请在此处提供信息。" + embargo_apply: 申请限制 + embargo_cancel: 取消并管理所有限制 + embargo_deactivate: 停用限制 + embargo_false_html: "这个%{cc}目前没有受到限制。如果您想申请限制,请在此处提供信息。" embargo_return: 返回编辑这个%{cc} - embargo_true_html: "这个%{cc}受到禁运。" - embargo_update: 更新禁运 + embargo_true_html: "这个%{cc}受到限制。" + embargo_update: 更新限制 header: - current: 当前禁运 - past: 过去的禁运 - history_empty: 这个%{cc}没有应用先前的禁运。 - manage_embargoes_html: 管理%{cc} (%{cc_type})的禁运 + current: 当前限制 + past: 过去的限制 + history_empty: 这个%{cc}没有先前的限制。 + manage_embargoes_html: 管理%{cc} (%{cc_type})的限制 index: - active: 所有主动禁运 - deactivated: 停用的禁运 - expired: 过期主动禁运 - manage_embargoes: 管理禁运 + active: 所有主动限制 + deactivated: 停用的限制 + expired: 过期主动限制 + manage_embargoes: 管理限制 list_expired_active_embargoes: change_all: 将%{cc}中的所有文件更改为 - deactivate: 停用禁运 - deactivate_selected: 停用选定的禁运 - missing: 目前没有有效的禁运。 + deactivate: 停用限制 + deactivate_selected: 停用选定的限制 + missing: 目前没有有效的限制。 table_headers: - release_date: 禁运发布日期 + release_date: 限制发布日期 title: 标题 type: 什么样的工作 - viz_after: 可见度将更改为 - viz_current: 当前可见度 + viz_after: 公开度将更改为 + viz_current: 当前公开度 featured_researchers: 特色研究者 file_manager: link_text: 文件管理员 @@ -564,21 +591,19 @@ zh: pdf_link: 下载PDF文件 file_sets: groups_description: - description_html: '"标有‘选择一个群’的下拉框内是您所属的一组用户管理群的列表。您可以选择一个特定的群,指定对%{application_name}中文件的访问级别,类似于添加用户访问级别。" - -' + description_html: 标有"选择一个群"的下拉框内是您所属的一组用户管理群的清单。您可以选择一个特定的群,指定对%{application_name}中文件的访问级别,类似于添加用户访问级别。 help: - header: User Support - override_text: 用 app/views/static/help.html.erb 覆盖此文件。 + header: 用户支持 + override_text: 用 app/views/static/help.html.erb 取代此文件。 homepage: admin_sets: link: 查看所有收藏 tab_label: 探索收藏集 title: 探索收藏集 featured_researcher: - missing: 没有研究人员被特色。 - tab_label: 特色研究者 - title: 特色研究者 + missing: 没有特色研究员。 + tab_label: 特色研究员 + title: 特色研究员 featured_works: drag: 拖 no_works: 没有特色作品 @@ -596,33 +621,33 @@ zh: leases: edit: header: - current: 目前租赁 - past: 过去租赁 + current: 目前租借 + past: 过去租借 history_empty: 这个%{cc}没有应用之前的租约。 - lease_apply: 申请租赁 - lease_cancel: 取消并管理所有租赁 - lease_deactivate: 停用租赁 - lease_false_html: "这个%{cc}目前不在租赁中。如果您想申请租赁,请在此处提供信息。" + lease_apply: 申请租借 + lease_cancel: 取消并管理所有租借 + lease_deactivate: 停用租借 + lease_false_html: "这个%{cc}目前不在租借中。如果您想申请租借,请在此处提供信息。" lease_return: 返回编辑这个%{cc} - lease_true_html: "这个%{cc}是租赁的。" - lease_update: 更新租赁 - manage_leases_html: 管理%{cc} (%{cc_type})的租赁 + lease_true_html: "这个%{cc}是租借的。" + lease_update: 更新租借 + manage_leases_html: 管理%{cc} (%{cc_type})的租借 index: - active: 所有活跃租赁 - deactivated: 停用租赁 - expired: 过期活跃租赁 - manage_leases: 管理租赁 + active: 所有在使用的租借 + deactivated: 停用租借 + expired: 过期使用的租借 + manage_leases: 管理租借 list_expired_active_leases: change_all: 将%{cc}中的所有文件更改为 - deactivate: 停用租赁 - deactivate_selected: 停用选定的租赁 - missing: 目前没有到期的租赁期限。 + deactivate: 停用租借 + deactivate_selected: 停用选定的租借 + missing: 目前没有到期的租借期限。 table_headers: - release_date: 租赁发布日期 + release_date: 租借发布日期 title: 标题 type: 什么样的工作 - viz_after: 可见度将更改为 - viz_current: 当前可见度 + viz_after: 公开度将更改为 + viz_current: 当前公开度 mailbox: date: 日期 delete: 删除讯息 @@ -700,7 +725,7 @@ zh: link: 链接 no_links: 链接未生成 title: 单次使用链接 - sort_label: 对列表中单件排序 + sort_label: 对清单中单件排序 toolbar: dashboard: menu: 控件板 @@ -716,10 +741,10 @@ zh: sr_target: 个人资料 transfers: new: - confirm: 您确定要将此作品的所有权转让给其他使用者吗? 单击确定转移或取消返回到传输屏幕 + confirm: 您确定要将此作品的所有权转让给其他使用者吗?单击确定转移或取消返回到转让页面 header: 所有权转让 placeholder: 搜索用户 - sr_only_description: 选择要传输 “%{work_title}” 的用户,添加可选注释,然后按传输。 + sr_only_description: 选择要转让 “%{work_title}” 的用户,添加可选注释,然后按转让。 title: 转让所有权 "%{work_title}" upload: alert: @@ -751,7 +776,7 @@ zh: alt: ORCID 图标 label: ORCID 资料 tab_activity: 活动 - tab_highlighted: Highlighted + tab_highlighted: 重点 tab_profile: 个人资料 zotero: alt: Zotero 图标 @@ -760,27 +785,23 @@ zh: unlinked: 与Zotero链接 visibility: authenticated: - note_html: 访问仅限于%{institution} 的用户 + note_html: 访问仅限于%{institution} text: "%{institution}" embargo: note_html: 设定日后发布。 - text: 时滞期限 + text: 限制运用 lease: note_html: 设置日期以减少访问。 text: 租约 open: - note_html: 各位请注意, 如果您计划申请专利或在期刊上发表您的%{type},请在SHERPA/RoMEO 查看相关出版社的版权政策 - text: 上市 + note_html: 向所有人公开 + text: 公开 warning_html:

请注意, 在此公开(例如,加上%{label}标记)会被视作已出版,从而影响:

  • 为您的作品申请专利
  • 在期刊上发表您的作品

请在SHERPA/RoMEO查看相关出版社的版权政策。

open_title_attr: 修改此资源的公开度 - private: - note_html: 只有指定用户有权访问"共享"内容。 - text: 私人的 - private_title_attr: 修改此资源的公开度 restricted: - note_html: 只有在“分享到”部分中被赋予特定访问权限的用户和/或组。 + note_html: 非公开保留分享权限。 text: 非公开 - restricted_title_attr: 更改此资源的可见性 + restricted_title_attr: 更改此资源的公开度 workflow: default: deposit: 存储 @@ -804,7 +825,7 @@ zh: metadata: 描述 relationships: 关联 share: 分享 - visibility_until: 直到 + visibility_until: 一直到 progress: header: 保存作品 show: @@ -825,7 +846,7 @@ zh: identifier: 一个独特的句柄用于标识收藏集。 例如用于标识期刊文章的DOI,或者标识一本书的ISBN或者OCLC系统编号。 keyword: 用于描述和检索收藏集内容的的词或词组。 language: 收藏集内容使用的语言。 - license: 用于管理收藏集访问权限的许可与发布信息。 可从下拉列表中选择。 + license: 用于管理收藏集访问权限的许可与发布信息。 可从下拉清单中选择。 publisher: 出版收藏集的个人或团体。 一般来说是机构单位。 related_url: 相关链接,用于链接与收藏集相关的网页或其它具体内容(音频、视频、PDF文件)。例如一个研究项目的网址,收藏集是此研究项目的成果 resource_type: 预定类型用于描述上传内容类型,例如"文章" or "数据集。" 可以选择多项类型。 @@ -840,7 +861,7 @@ zh: identifier: 一个独特的句柄用于标识作品。 例如用于标识期刊文章的DOI,或者标识一本书的ISBN或者OCLC系统编号。 keyword: 用于描述和搜索作品内容的的词或词组。 language: 作品内容使用的语言。 - license: 用于管理作品访问权限的许可与发布信息。 可从下拉列表中选择。 + license: 用于管理作品访问权限的许可与发布信息。 可从下拉清单中选择。 publisher: 出版作品的个人或团体,一般来说是机构单位。 related_url: 相关链接,用于链接与作品相关的网页或其它具体内容(音频、视频、PDF文件)。例如一个研究项目的网址,作品是此研究项目的成果。 resource_type: 预定类型用于描述上传内容类型,例如"文章" or "数据集。" 可以选择多项类型。 @@ -849,7 +870,7 @@ zh: labels: collection: size: 大小 - total_items: 总作品 + total_items: 总件数 defaults: admin_set_id: 在行政 based_near: 地点 @@ -871,5 +892,9 @@ zh: proxy_deposit_request: sender_comment: 注释 transfer_to: 用户 + placeholders: + defaults: + find_child_work: 搜索工作... + member_of_collection_ids: 选择一个集合... required: html: 需要的 diff --git a/hyrax.gemspec b/hyrax.gemspec index 87c62006e6..3f5f76037c 100644 --- a/hyrax.gemspec +++ b/hyrax.gemspec @@ -1,5 +1,3 @@ -# -*- encoding: utf-8 -*- - lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'hyrax/version' @@ -8,13 +6,13 @@ Gem::Specification.new do |spec| spec.authors = ["Justin Coyne", 'Michael J. Giarlo', "Carolyn Cole", "Matt Zumwalt", 'Jeremy Friesen', 'Trey Pendragon', 'Esmé Cowles'] spec.email = ["jcoyne85@stanford.edu", 'mjgiarlo@stanford.edu', 'cam156@psu.edu', 'matt@databindery.com', "jeremy.n.friesen@gmail.com", 'tpendragon@princeton.edu', 'escowles@ticklefish.org'] spec.description = 'Hyrax is a featureful Samvera front-end based on the latest and greatest Samvera software components.' - spec.summary = <<-EOF + spec.summary = <<-SUMMARY Hyrax is a front-end based on the robust Samvera framework, providing a user interface for common repository features. Hyrax offers the ability to create repository object types on demand, to deposit content via multiple workflows, and to describe content with flexible metadata. Numerous optional features may be turned on in the administrative dashboard or added through plugins. -EOF +SUMMARY spec.homepage = "http://github.com/samvera/hyrax" @@ -31,66 +29,67 @@ EOF # http://guides.rubyonrails.org/maintenance_policy.html spec.add_dependency 'rails', '~> 5.0' - spec.add_dependency 'hydra-head', '>= 10.5.0' - spec.add_dependency 'hydra-editor', '~> 3.3' - spec.add_dependency 'hydra-works', '~> 0.16' - spec.add_dependency 'hydra-derivatives', '~> 3.3' - spec.add_dependency 'browse-everything', '>= 0.10.5' + spec.add_dependency 'active-fedora', '~> 11.5', '>= 11.5.2' + spec.add_dependency 'active_fedora-noid', '~> 2.0', '>= 2.0.2' + spec.add_dependency 'almond-rails', '~> 0.1' + spec.add_dependency 'awesome_nested_set', '~> 3.1' spec.add_dependency 'blacklight', '~> 6.11', '>= 6.11.2' spec.add_dependency 'blacklight-gallery', '~> 0.7' - spec.add_dependency 'tinymce-rails', '~> 4.1' + spec.add_dependency 'breadcrumbs_on_rails', '~> 3.0' + spec.add_dependency 'browse-everything', '>= 0.10.5' + spec.add_dependency 'carrierwave', '~> 1.0' + spec.add_dependency 'clipboard-rails', '~> 1.5' + spec.add_dependency 'dry-equalizer', '~> 0.2' + spec.add_dependency 'dry-struct', '~> 0.1' + spec.add_dependency 'dry-validation', '~> 0.9' + spec.add_dependency 'flipflop', '~> 2.3' + # Pin more tightly because 0.x gems are potentially unstable + spec.add_dependency 'flot-rails', '~> 0.0.6' spec.add_dependency 'font-awesome-rails', '~> 4.2' - spec.add_dependency 'select2-rails', '~> 3.5' + spec.add_dependency 'hydra-derivatives', '~> 3.3' + spec.add_dependency 'hydra-editor', '~> 3.3' + spec.add_dependency 'hydra-head', '>= 10.5.0' + spec.add_dependency 'hydra-works', '~> 0.16' + spec.add_dependency 'iiif_manifest', '~> 0.3.0' + spec.add_dependency 'jquery-datatables-rails', '~> 3.4' + spec.add_dependency 'jquery-ui-rails', '~> 5.0' spec.add_dependency 'json-schema' # for Arkivo - spec.add_dependency 'nest', '~> 2.0' + # Pin more tightly because 0.x gems are potentially unstable + spec.add_dependency 'kaminari_route_prefix', '~> 0.1.1' + spec.add_dependency 'legato', '~> 0.3' + spec.add_dependency 'linkeddata' # Required for getting values from geonames spec.add_dependency 'mailboxer', '~> 0.12' - spec.add_dependency 'carrierwave', '~> 1.0' + spec.add_dependency 'nest', '~> 2.0' spec.add_dependency 'oauth' spec.add_dependency 'oauth2', '~> 1.2' - spec.add_dependency 'signet' - spec.add_dependency 'legato', '~> 0.3' spec.add_dependency 'posix-spawn' - spec.add_dependency 'jquery-ui-rails', '~> 5.0' - spec.add_dependency 'redis-namespace', '~> 1.5' - # Pin more tightly because 0.x gems are potentially unstable - spec.add_dependency 'flot-rails', '~> 0.0.6' - spec.add_dependency 'almond-rails', '~> 0.1' + spec.add_dependency 'power_converter', '~> 0.1', '>= 0.1.2' spec.add_dependency 'qa', '~> 2.0' # questioning_authority - spec.add_dependency 'flipflop', '~> 2.3' - spec.add_dependency 'jquery-datatables-rails', '~> 3.4' - spec.add_dependency 'rdf-rdfxml' # controlled vocabulary importer - spec.add_dependency 'clipboard-rails', '~> 1.5' spec.add_dependency 'rails_autolink', '~> 1.1' - spec.add_dependency 'active_fedora-noid', '~> 2.0', '>= 2.0.2' - spec.add_dependency 'awesome_nested_set', '~> 3.1' - spec.add_dependency 'breadcrumbs_on_rails', '~> 3.0' - # Pin more tightly because 0.x gems are potentially unstable - spec.add_dependency 'kaminari_route_prefix', '~> 0.1.1' - spec.add_dependency 'power_converter', '~> 0.1', '>= 0.1.2' - spec.add_dependency 'dry-validation', '~> 0.9' - spec.add_dependency 'dry-equalizer', '~> 0.2' - spec.add_dependency 'dry-struct', '~> 0.1' + spec.add_dependency 'rdf-rdfxml' # controlled vocabulary importer + spec.add_dependency 'redis-namespace', '~> 1.5' spec.add_dependency 'redlock', '>= 0.1.2' spec.add_dependency 'retriable', '>= 2.9', '< 4.0' - spec.add_dependency 'active-fedora', '~> 11.5', '>= 11.5.2' spec.add_dependency 'samvera-nesting_indexer', '~> 0.8' - spec.add_dependency 'linkeddata' # Required for getting values from geonames + spec.add_dependency 'select2-rails', '~> 3.5' + spec.add_dependency 'signet' + spec.add_dependency 'tinymce-rails', '~> 4.1' - spec.add_development_dependency 'engine_cart', '~> 1.2' - spec.add_development_dependency 'mida', '~> 0.3' - spec.add_development_dependency 'database_cleaner', '~> 1.3' - spec.add_development_dependency 'solr_wrapper', '~> 1.1' - spec.add_development_dependency 'fcrepo_wrapper', '~> 0.5', '>= 0.5.1' - spec.add_development_dependency 'rspec-rails', '~> 3.1' - spec.add_development_dependency 'rspec-its', '~> 1.1' - spec.add_development_dependency 'rspec-activemodel-mocks', '~> 1.0' spec.add_development_dependency "capybara", '~> 2.4' spec.add_development_dependency 'capybara-maleficent', '~> 0.2' spec.add_development_dependency "chromedriver-helper" - spec.add_development_dependency "selenium-webdriver" - spec.add_development_dependency "factory_bot_rails", '~> 4.4' + spec.add_development_dependency 'database_cleaner', '~> 1.3' + spec.add_development_dependency 'engine_cart', '~> 1.2' spec.add_development_dependency "equivalent-xml", '~> 0.5' + spec.add_development_dependency "factory_bot_rails", '~> 4.4' + spec.add_development_dependency 'fcrepo_wrapper', '~> 0.5', '>= 0.5.1' spec.add_development_dependency "jasmine", '~> 2.3' + spec.add_development_dependency 'mida', '~> 0.3' + spec.add_development_dependency 'rspec-activemodel-mocks', '~> 1.0' + spec.add_development_dependency 'rspec-its', '~> 1.1' + spec.add_development_dependency 'rspec-rails', '~> 3.1' + spec.add_development_dependency "selenium-webdriver" + spec.add_development_dependency 'solr_wrapper', '~> 1.1' # Pin rubocop and rubocop-rspec tightly. Minor-level version bumps # in these gems cause Rubocop violations, and those violations cause # continuous integration builds to fail, and those failures prevent @@ -101,12 +100,12 @@ EOF # conventions. This allows us to take a managed approach to code # style -- we choose to update style when we wish, not when a # minor-level version bump in a dependency comes out. - spec.add_development_dependency 'rubocop', '~> 0.49.1' - spec.add_development_dependency 'rubocop-rspec', '~> 1.16.0' - spec.add_development_dependency 'shoulda-matchers', '~> 3.1' - spec.add_development_dependency 'rails-controller-testing', '~> 1' - spec.add_development_dependency 'webmock' spec.add_development_dependency 'i18n-debug' if ENV['I18N_DEBUG'] spec.add_development_dependency 'i18n_yaml_sorter' unless ENV['TRAVIS'] spec.add_development_dependency 'shoulda-callback-matchers', '~> 1.1.1' + spec.add_development_dependency 'rails-controller-testing', '~> 1' + spec.add_development_dependency 'rubocop', '~> 0.51.0' + spec.add_development_dependency 'rubocop-rspec', '~> 1.20.1' + spec.add_development_dependency 'shoulda-matchers', '~> 3.1' + spec.add_development_dependency 'webmock' end diff --git a/lib/generators/hyrax/assets_generator.rb b/lib/generators/hyrax/assets_generator.rb index 5cfb3b6d27..9f6d3e2ac1 100644 --- a/lib/generators/hyrax/assets_generator.rb +++ b/lib/generators/hyrax/assets_generator.rb @@ -1,5 +1,3 @@ -# -*- encoding : utf-8 -*- - require 'rails/generators' class Hyrax::AssetsGenerator < Rails::Generators::Base @@ -20,10 +18,10 @@ def inject_css def inject_js return if hyrax_javascript_installed? insert_into_file 'app/assets/javascripts/application.js', after: '//= require_tree .' do - <<-EOF.strip_heredoc + <<-JS.strip_heredoc //= require hyrax - EOF + JS end end diff --git a/lib/generators/hyrax/clamav_generator.rb b/lib/generators/hyrax/clamav_generator.rb index 261e8c4ee8..5de989b2fa 100644 --- a/lib/generators/hyrax/clamav_generator.rb +++ b/lib/generators/hyrax/clamav_generator.rb @@ -1,5 +1,3 @@ -# -*- encoding : utf-8 -*- - require 'rails/generators' class Hyrax::ClamavGenerator < Rails::Generators::Base diff --git a/lib/generators/hyrax/config_generator.rb b/lib/generators/hyrax/config_generator.rb index a7698e4d71..a845f69cdc 100644 --- a/lib/generators/hyrax/config_generator.rb +++ b/lib/generators/hyrax/config_generator.rb @@ -1,5 +1,3 @@ -# -*- encoding : utf-8 -*- - require 'rails/generators' class Hyrax::ConfigGenerator < Rails::Generators::Base diff --git a/lib/generators/hyrax/install_generator.rb b/lib/generators/hyrax/install_generator.rb index 73788170ca..3cc02c8f2b 100644 --- a/lib/generators/hyrax/install_generator.rb +++ b/lib/generators/hyrax/install_generator.rb @@ -1,7 +1,11 @@ module Hyrax class Install < Rails::Generators::Base source_root File.expand_path('../templates', __FILE__) + argument :model_name, type: :string, default: "user", desc: "Model name for User model (primarily passed to devise, but also used elsewhere)" + + class_option :'skip-riiif', type: :boolean, default: false, desc: "Skip generating RIIIF image service." + desc """ This generator makes the following changes to your application: 1. Runs installers for blacklight & hydra-head (which also install & configure devise) @@ -25,6 +29,7 @@ class Install < Rails::Generators::Base 15. Installs Blacklight gallery (and removes it's scss) 16. Install jquery-datatables 17. Initializes the active-fedora_noid database-backed minter + 18. Generates RIIIF image server implementation """ def run_required_generators @@ -163,5 +168,9 @@ def datatables def af_noid_database_minter_initialize generate 'active_fedora:noid:install' end + + def riiif_image_server + generate 'hyrax:riiif' unless options[:'skip-riiif'] + end end end diff --git a/lib/generators/hyrax/riiif_generator.rb b/lib/generators/hyrax/riiif_generator.rb new file mode 100644 index 0000000000..25ec87faf7 --- /dev/null +++ b/lib/generators/hyrax/riiif_generator.rb @@ -0,0 +1,54 @@ +require 'rails/generators' + +class Hyrax::RiiifGenerator < Rails::Generators::Base + source_root File.expand_path('../templates', __FILE__) + + desc ' + This generator makes the following changes to your application: + 1. Adds riiif dependency to application Gemfile + 2. Copies riiif initializer to application + 3. Mounts riiif engine within application + 4. Overrides Hyrax config variables to use riiif for image and info URLs + 5. Copies 404 image for riiif unauthorized responses' + + def banner + say_status('info', 'GENERATING RIIIF IMAGE SERVER', :blue) + end + + def add_to_gemfile + gem 'riiif', '~> 1.1' + + Bundler.with_clean_env do + run "bundle install" + end + end + + def copy_initializer + copy_file 'config/initializers/riiif.rb' + end + + def mount_route + route "mount Riiif::Engine => 'images', as: :riiif if Hyrax.config.iiif_image_server?" + end + + def override_image_url_builder_in_hyrax_config + insert_into_file 'config/initializers/hyrax.rb', before: /^ # config.iiif_image_url_builder/ do + " config.iiif_image_url_builder = lambda do |file_id, base_url, size|\n" \ + " Riiif::Engine.routes.url_helpers.image_url(file_id, host: base_url, size: size)\n" \ + " end\n" + end + end + + def override_info_url_builder_in_hyrax_config + insert_into_file 'config/initializers/hyrax.rb', before: /^ # config.iiif_info_url_builder/ do + " config.iiif_info_url_builder = lambda do |file_id, base_url|\n" \ + " uri = Riiif::Engine.routes.url_helpers.info_url(file_id, host: base_url)\n" \ + " uri.sub(%r{/info\\.json\\Z}, '')\n" \ + " end\n" + end + end + + def copy_unauthorized_image + copy_file 'app/assets/images/us_404.svg' + end +end diff --git a/lib/generators/hyrax/templates/app/assets/images/us_404.svg b/lib/generators/hyrax/templates/app/assets/images/us_404.svg new file mode 100644 index 0000000000..7ac476d37b --- /dev/null +++ b/lib/generators/hyrax/templates/app/assets/images/us_404.svg @@ -0,0 +1,91 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/generators/hyrax/templates/config/initializers/hyrax.rb b/lib/generators/hyrax/templates/config/initializers/hyrax.rb index 808dbe681e..123792043e 100644 --- a/lib/generators/hyrax/templates/config/initializers/hyrax.rb +++ b/lib/generators/hyrax/templates/config/initializers/hyrax.rb @@ -111,6 +111,38 @@ # The default is true. # config.work_requires_files = true + # Enable IIIF image service. This is required to use the + # UniversalViewer-ified show page + # + # If you have run the riiif generator, an embedded riiif service + # will be used to deliver images via IIIF. If you have not, you will + # need to configure the following other configuration values to work + # with your image server: + # + # * iiif_image_url_builder + # * iiif_info_url_builder + # * iiif_image_compliance_level_uri + # * iiif_image_size_default + # + # Default is false + # config.iiif_image_server = false + + # Returns a URL that resolves to an image provided by a IIIF image server + # config.iiif_image_url_builder = lambda do |file_id, base_url, size| + # "#{base_url}/downloads/#{file_id.split('/').first}" + # end + + # Returns a URL that resolves to an info.json file provided by a IIIF image server + # config.iiif_info_url_builder = lambda do |_, _| + # "" + # end + + # Returns a URL that indicates your IIIF image server compliance level + # config.iiif_image_compliance_level_uri = 'http://iiif.io/api/image/2/level2.json' + + # Returns a IIIF image size default + # config.iiif_image_size_default = '600,' + # Should a button with "Share my work" show on the front page to all users (even those not logged in)? # config.display_share_button_when_not_logged_in = true diff --git a/lib/generators/hyrax/templates/config/initializers/riiif.rb b/lib/generators/hyrax/templates/config/initializers/riiif.rb new file mode 100644 index 0000000000..e64daf6443 --- /dev/null +++ b/lib/generators/hyrax/templates/config/initializers/riiif.rb @@ -0,0 +1,26 @@ +Riiif::Image.file_resolver = Riiif::HTTPFileResolver.new +Riiif::Image.info_service = lambda do |id, _file| + # id will look like a path to a pcdm:file + # (e.g. rv042t299%2Ffiles%2F6d71677a-4f80-42f1-ae58-ed1063fd79c7) + # but we just want the id for the FileSet it's attached to. + + # Capture everything before the first slash + fs_id = id.sub(/\A([^\/]*)\/.*/, '\1') + resp = ActiveFedora::SolrService.get("id:#{fs_id}") + doc = resp['response']['docs'].first + raise "Unable to find solr document with id:#{fs_id}" unless doc + { height: doc['height_is'], width: doc['width_is'] } +end + +Riiif::Image.file_resolver.id_to_uri = lambda do |id| + ActiveFedora::Base.id_to_uri(CGI.unescape(id)).tap do |url| + Rails.logger.info "Riiif resolved #{id} to #{url}" + end +end + +Riiif::Image.authorization_service = Hyrax::IIIFAuthorizationService + +Riiif.not_found_image = Rails.root.join('app', 'assets', 'images', 'us_404.svg') +Riiif.unauthorized_image = Rails.root.join('app', 'assets', 'images', 'us_404.svg') + +Riiif::Engine.config.cache_duration_in_days = 365 diff --git a/lib/generators/hyrax/templates/config/locales/hyrax.en.yml b/lib/generators/hyrax/templates/config/locales/hyrax.en.yml index b2c01e9883..34e9e07d68 100644 --- a/lib/generators/hyrax/templates/config/locales/hyrax.en.yml +++ b/lib/generators/hyrax/templates/config/locales/hyrax.en.yml @@ -45,13 +45,13 @@ en: subject_tesim: Subject title_tesim: Title hyrax: - product_name: "Hyrax" - product_twitter_handle: "@SamveraRepo" - institution_name: "Institution" - institution_name_full: "The Institution Name" account_name: "My Institution Account Id" directory: suffix: "@example.org" footer: copyright_html: "Copyright © 2017 Samvera Licensed under the Apache License, Version 2.0" service_html: A service of Samvera. + institution_name: "Institution" + institution_name_full: "The Institution Name" + product_name: "Hyrax" + product_twitter_handle: "@SamveraRepo" diff --git a/lib/generators/hyrax/work/templates/feature_spec.rb.erb b/lib/generators/hyrax/work/templates/feature_spec.rb.erb index c675efbfea..6ff53011c9 100644 --- a/lib/generators/hyrax/work/templates/feature_spec.rb.erb +++ b/lib/generators/hyrax/work/templates/feature_spec.rb.erb @@ -12,9 +12,21 @@ RSpec.feature 'Create a <%= class_name %>', js: false do let(:user) do User.new(user_attributes) { |u| u.save(validate: false) } end + let(:admin_set_id) { AdminSet.find_or_create_default_admin_set_id } + let(:permission_template) { Hyrax::PermissionTemplate.find_or_create_by!(admin_set_id: admin_set_id) } + let(:workflow) { Sipity::Workflow.create!(active: true, name: 'test-workflow', permission_template: permission_template) } before do - AdminSet.find_or_create_default_admin_set_id + # Create a single action that can be taken + Sipity::WorkflowAction.create!(name: 'submit', workflow: workflow) + + # Grant the user access to deposit into the admin set. + Hyrax::PermissionTemplateAccess.create!( + permission_template_id: permission_template.id, + agent_type: 'user', + agent_id: user.user_key, + access: 'deposit' + ) login_as user end @@ -28,6 +40,30 @@ RSpec.feature 'Create a <%= class_name %>', js: false do # click_button "Create work" expect(page).to have_content "Add New <%= human_name %>" + click_link "Files" # switch tab + expect(page).to have_content "Add files" + expect(page).to have_content "Add folder" + within('span#addfiles') do + attach_file("files[]", "#{Hyrax::Engine.root}/spec/fixtures/image.jp2", visible: false) + attach_file("files[]", "#{Hyrax::Engine.root}/spec/fixtures/jp2_fits.xml", visible: false) + end + click_link "Descriptions" # switch tab + fill_in('Title', with: 'My Test Work') + fill_in('Creator', with: 'Doe, Jane') + fill_in('Keyword', with: 'testing') + select('In Copyright', from: 'Rights statement') + + # With selenium and the chrome driver, focus remains on the + # select box. Click outside the box so the next line can't find + # its element + find('body').click + choose('<%= file_name %>_visibility_open') + expect(page).to have_content('Please note, making something visible to the world (i.e. marking this as Public) may be viewed as publishing which could impact your ability to') + check('agreement') + + click_on('Save') + expect(page).to have_content('My Test Work') + expect(page).to have_content "Your files are being processed by Hyrax in the background." end end end diff --git a/lib/generators/hyrax/work/templates/locale.de.yml.erb b/lib/generators/hyrax/work/templates/locale.de.yml.erb index f76f4e41f0..00357c36fc 100644 --- a/lib/generators/hyrax/work/templates/locale.de.yml.erb +++ b/lib/generators/hyrax/work/templates/locale.de.yml.erb @@ -4,5 +4,5 @@ de: <%= file_name %>: 'fa fa-file-text-o' select_type: <%= file_name %>: - name: "<%= human_name.titleize %>" description: "<%= human_name %> Werke" + name: "<%= human_name.titleize %>" diff --git a/lib/generators/hyrax/work/templates/locale.en.yml.erb b/lib/generators/hyrax/work/templates/locale.en.yml.erb index 77ec1cccd2..efdd9041c5 100644 --- a/lib/generators/hyrax/work/templates/locale.en.yml.erb +++ b/lib/generators/hyrax/work/templates/locale.en.yml.erb @@ -4,5 +4,5 @@ en: <%= file_name %>: 'fa fa-file-text-o' select_type: <%= file_name %>: - name: "<%= human_name.titleize %>" description: "<%= human_name %> works" + name: "<%= human_name.titleize %>" diff --git a/lib/generators/hyrax/work/templates/locale.es.yml.erb b/lib/generators/hyrax/work/templates/locale.es.yml.erb index 8bc70ef8e8..a6b4fbb9ba 100644 --- a/lib/generators/hyrax/work/templates/locale.es.yml.erb +++ b/lib/generators/hyrax/work/templates/locale.es.yml.erb @@ -5,6 +5,6 @@ es: select_type: <%= file_name %>: # TODO: translate `human_name` into Spanish + description: "<%= human_name %> trabajos" name: "<%= human_name.titleize %>" # TODO: translate `human_name` into Spanish - description: "<%= human_name %> trabajos" diff --git a/lib/generators/hyrax/work/templates/locale.fr.yml.erb b/lib/generators/hyrax/work/templates/locale.fr.yml.erb index 09c4e612c3..fa18200643 100644 --- a/lib/generators/hyrax/work/templates/locale.fr.yml.erb +++ b/lib/generators/hyrax/work/templates/locale.fr.yml.erb @@ -4,5 +4,5 @@ fr: <%= file_name %>: 'fa fa-file-text-o' select_type: <%= file_name %>: - name: "<%= human_name.titleize %>" description: "<%= human_name %> œuvres" + name: "<%= human_name.titleize %>" diff --git a/lib/generators/hyrax/work/templates/locale.it.yml.erb b/lib/generators/hyrax/work/templates/locale.it.yml.erb index e8ef6a54b5..affcdb77d3 100644 --- a/lib/generators/hyrax/work/templates/locale.it.yml.erb +++ b/lib/generators/hyrax/work/templates/locale.it.yml.erb @@ -4,5 +4,5 @@ it: <%= file_name %>: 'fa fa-file-text-o' select_type: <%= file_name %>: - name: "<%= human_name.titleize %>" description: "<%= human_name %> opere" + name: "<%= human_name.titleize %>" diff --git a/lib/generators/hyrax/work/templates/locale.pt-BR.yml.erb b/lib/generators/hyrax/work/templates/locale.pt-BR.yml.erb index 47df04e215..326fa3052c 100644 --- a/lib/generators/hyrax/work/templates/locale.pt-BR.yml.erb +++ b/lib/generators/hyrax/work/templates/locale.pt-BR.yml.erb @@ -4,5 +4,5 @@ pt-BR: <%= file_name %>: 'fa fa-file-text-o' select_type: <%= file_name %>: - name: "<%= human_name.titleize %>" description: "<%= human_name %> trabalhos" + name: "<%= human_name.titleize %>" diff --git a/lib/generators/hyrax/work/templates/locale.zh.yml.erb b/lib/generators/hyrax/work/templates/locale.zh.yml.erb index 9552084c5e..4d21ba25d2 100644 --- a/lib/generators/hyrax/work/templates/locale.zh.yml.erb +++ b/lib/generators/hyrax/work/templates/locale.zh.yml.erb @@ -5,6 +5,6 @@ zh: select_type: <%= file_name %>: # TODO: translate `human_name` into Chinese + description: "<%= human_name %> 作品" name: "<%= human_name.titleize %>" # TODO: translate `human_name` into Chinese - description: "<%= human_name %> 作品" diff --git a/lib/hyrax/configuration.rb b/lib/hyrax/configuration.rb index 9af3f23b0e..4d92dd5a2b 100644 --- a/lib/hyrax/configuration.rb +++ b/lib/hyrax/configuration.rb @@ -87,8 +87,7 @@ def feature_config_path @feature_config_path ||= Rails.root.join('config', 'features.yml') end - attr_accessor :temp_file_base, :enable_local_ingest, - :analytics, :analytic_start_date + attr_accessor :temp_file_base, :enable_local_ingest attr_writer :display_microdata def display_microdata? @@ -238,8 +237,8 @@ def license_service_class # A configuration point for changing the behavior of the rights statement service. # - # @!attribute [w] license_service_class - # A configuration point for changing the behavior of the license service. + # @!attribute [w] rights_statement_service_class + # A configuration point for changing the behavior of the rights statement service. # # @see Hyrax::RightsStatementService for implementation details attr_writer :rights_statement_service_class @@ -274,6 +273,7 @@ def browse_everything? end attr_writer :analytics + attr_reader :analytics def analytics? @analytics ||= false end @@ -356,6 +356,60 @@ def cache_path @cache_path ||= ->() { Rails.root + 'tmp' + 'uploads' + 'cache' } end + # Enable IIIF image service. This is required to use the + # UniversalViewer-enabled show page + # + # If you have run the hyrax:riiif generator, an embedded riiif service + # will be used to deliver images via IIIF. If you have not, you will + # need to configure the following other configuration values to work + # with your image server. + # + # @see config.iiif_image_url_builder + # @see config.iiif_info_url_builder + # @see config.iiif_image_compliance_level_uri + # @see config.iiif_image_size_default + # + # @note Default is false + # + # @return [Boolean] true to enable, false to disable + def iiif_image_server? + return @iiif_image_server unless @iiif_image_server.nil? + @iiif_image_server = false + end + attr_writer :iiif_image_server + + # URL that resolves to an image provided by a IIIF image server + # + # @return [#call] lambda/proc that generates a URL to an image + def iiif_image_url_builder + @iiif_image_url_builder ||= ->(file_id, base_url, _size) { "#{base_url}/downloads/#{file_id.split('/').first}" } + end + attr_writer :iiif_image_url_builder + + # URL that resolves to an info.json file provided by a IIIF image server + # + # @return [#call] lambda/proc that generates a URL to image info + def iiif_info_url_builder + @iiif_info_url_builder ||= ->(_file_id, _base_url) { '' } + end + attr_writer :iiif_info_url_builder + + # URL that indicates your IIIF image server compliance level + # + # @return [String] valid IIIF image compliance level URI + def iiif_image_compliance_level_uri + @iiif_image_compliance_level_uri ||= 'http://iiif.io/api/image/2/level2.json' + end + attr_writer :iiif_image_compliance_level_uri + + # IIIF image size default + # + # @return [#String] valid IIIF image size parameter + def iiif_image_size_default + @iiif_image_size_default ||= '600,' + end + attr_writer :iiif_image_size_default + # Should a button with "Share my work" show on the front page to users who are not logged in? attr_writer :display_share_button_when_not_logged_in def display_share_button_when_not_logged_in? diff --git a/lib/hyrax/engine.rb b/lib/hyrax/engine.rb index e678fcdf3c..55e5c3e3de 100644 --- a/lib/hyrax/engine.rb +++ b/lib/hyrax/engine.rb @@ -34,7 +34,7 @@ class Engine < ::Rails::Engine begin Hyrax.config.persist_registered_roles! Rails.logger.info("Hyrax::Engine.after_initialize - persisting registered roles!") - rescue + rescue ActiveRecord::StatementInvalid message = "Hyrax::Engine.after_initialize - unable to persist registered roles.\n" message += "It is expected during the application installation - during integration tests, rails install.\n" message += "It is UNEXPECTED if you are booting up a Hyrax powered application via `rails server'" diff --git a/lib/hyrax/rails/routes.rb b/lib/hyrax/rails/routes.rb index 24c416dd22..1f7240ad8f 100644 --- a/lib/hyrax/rails/routes.rb +++ b/lib/hyrax/rails/routes.rb @@ -14,6 +14,7 @@ def curation_concerns_basic_routes(&block) namespaced_resources curation_concern_name, except: [:index], &block namespaced_resources curation_concern_name, only: [] do member do + get :manifest get :file_manager get :inspect_work end @@ -36,7 +37,6 @@ def curation_concerns_basic_routes(&block) post :copy end end - resources :file_sets, only: [:new, :create], path: 'container/:parent_id/file_sets' resources :file_sets, only: [:show, :edit, :update, :destroy] do member do get :versions diff --git a/lib/tasks/default_admin_set.rake b/lib/tasks/default_admin_set.rake index 9598fafd97..912afe8ab0 100644 --- a/lib/tasks/default_admin_set.rake +++ b/lib/tasks/default_admin_set.rake @@ -6,7 +6,7 @@ namespace :hyrax do if Hyrax::PermissionTemplate.find_by(source_id: id) $stdout.puts "Successfully created default admin set" else - $stderr.puts "ERROR: Default admin set exists but it does not have an " \ + warn "ERROR: Default admin set exists but it does not have an " \ "associated permission template.\n\nThis may happen if you cleared your " \ "database but you did not clear out Fedora and Solr.\n\n" \ "You could manually create the permission template in the rails console" \ diff --git a/spec/actors/hyrax/actors/collections_membership_actor_spec.rb b/spec/actors/hyrax/actors/collections_membership_actor_spec.rb index fde71a9903..88031f742b 100644 --- a/spec/actors/hyrax/actors/collections_membership_actor_spec.rb +++ b/spec/actors/hyrax/actors/collections_membership_actor_spec.rb @@ -1,7 +1,7 @@ RSpec.describe Hyrax::Actors::CollectionsMembershipActor do let(:user) { create(:user) } let(:ability) { ::Ability.new(user) } - let(:curation_concern) { GenericWork.new } + let(:curation_concern) { build(:work) } let(:attributes) { {} } let(:terminator) { Hyrax::Actors::Terminator.new } let(:env) { Hyrax::Actors::Environment.new(curation_concern, ability, attributes) } @@ -17,11 +17,14 @@ describe 'the next actor' do let(:attributes) do - { member_of_collection_ids: ['123'], title: ['test'] } + { + member_of_collections_attributes: { '0' => { id: '123' } }, + title: ['test'] + } end before do - allow(Collection).to receive(:find).with(['123']) + allow(Collection).to receive(:find).with('123') allow(curation_concern).to receive(:member_of_collections=) end @@ -34,9 +37,12 @@ end describe 'create' do - let(:collection) { create(:collection) } + let(:collection) { create(:collection, edit_users: [user.user_key]) } let(:attributes) do - { member_of_collection_ids: [collection.id], title: ['test'] } + { + member_of_collections_attributes: { '0' => { id: collection.id } }, + title: ['test'] + } end it 'adds it to the collection' do @@ -60,13 +66,15 @@ end end - context "when work is in user's own collection" do + context "when work is in user's own collection and destroy is passed" do let(:collection) { create(:collection, user: user, title: ['A good title']) } - let(:attributes) { { member_of_collection_ids: [] } } + let(:attributes) do + { member_of_collections_attributes: { '0' => { id: collection.id, _destroy: 'true' } } } + end before do - subject.create(Hyrax::Actors::Environment.new(curation_concern, ability, - member_of_collection_ids: [collection.id], title: ['test'])) + curation_concern.member_of_collections = [collection] + curation_concern.save! end it "removes the work from that collection" do @@ -77,12 +85,17 @@ context "when work is in another user's collection" do let(:other_user) { create(:user) } - let(:collection) { create(:collection, user: other_user, title: ['A good title']) } + let(:other_collection) { create(:collection, user: other_user, title: ['A good title']) } + + before do + curation_concern.member_of_collections = [other_collection] + curation_concern.save! + end - it "doesn't remove the work from that collection" do + it "doesn't remove the work from the other user's collection" do subject.create(env) expect(subject.create(env)).to be true - expect(curation_concern.member_of_collections).to eq [collection] + expect(curation_concern.member_of_collections).to match_array [collection, other_collection] # TODO: Seems wrong that collection is included. I don't see where this test adds the work to collection. end end diff --git a/spec/actors/hyrax/actors/create_with_remote_files_actor_spec.rb b/spec/actors/hyrax/actors/create_with_remote_files_actor_spec.rb index 65e65179db..f4a25bc4e7 100644 --- a/spec/actors/hyrax/actors/create_with_remote_files_actor_spec.rb +++ b/spec/actors/hyrax/actors/create_with_remote_files_actor_spec.rb @@ -86,19 +86,19 @@ end it "accepts file: urls in whitelisted directories" do - expect(actor.send(:validate_remote_url, "file:///local/file/test.txt")).to be true - expect(actor.send(:validate_remote_url, "file:///local/file/subdirectory/test.txt")).to be true - expect(actor.send(:validate_remote_url, "file:///test/test.txt")).to be true + expect(actor.send(:validate_remote_url, URI('file:///local/file/test.txt'))).to be true + expect(actor.send(:validate_remote_url, URI('file:///local/file/subdirectory/test.txt'))).to be true + expect(actor.send(:validate_remote_url, URI('file:///test/test.txt'))).to be true end it "rejects file: urls outside whitelisted directories" do - expect(actor.send(:validate_remote_url, "file:///tmp/test.txt")).to be false - expect(actor.send(:validate_remote_url, "file:///test/../tmp/test.txt")).to be false - expect(actor.send(:validate_remote_url, "file:///test/")).to be false + expect(actor.send(:validate_remote_url, URI('file:///tmp/test.txt'))).to be false + expect(actor.send(:validate_remote_url, URI('file:///test/../tmp/test.txt'))).to be false + expect(actor.send(:validate_remote_url, URI('file:///test/'))).to be false end it "accepts other types of urls" do - expect(actor.send(:validate_remote_url, "https://example.com/test.txt")).to be true + expect(actor.send(:validate_remote_url, URI('https://example.com/test.txt'))).to be true end end end diff --git a/spec/actors/hyrax/actors/file_actor_spec.rb b/spec/actors/hyrax/actors/file_actor_spec.rb index 2e5963fbd8..4d43188803 100644 --- a/spec/actors/hyrax/actors/file_actor_spec.rb +++ b/spec/actors/hyrax/actors/file_actor_spec.rb @@ -77,7 +77,6 @@ class FileSetWithExtras < FileSet describe '#ingest_file' do before do expect(Hydra::Works::AddFileToFileSet).to receive(:call).with(file_set, io, relation, versioning: false) - expect(Hyrax::WorkingDirectory).not_to receive(:copy_file_to_working_directory) end it 'when the file is available' do allow(file_set).to receive(:save).and_return(true) diff --git a/spec/actors/hyrax/actors/generic_work_actor_spec.rb b/spec/actors/hyrax/actors/generic_work_actor_spec.rb index 8d735b2438..334bbfed0c 100644 --- a/spec/actors/hyrax/actors/generic_work_actor_spec.rb +++ b/spec/actors/hyrax/actors/generic_work_actor_spec.rb @@ -285,34 +285,6 @@ end end - context 'adding to collections' do - let!(:collection1) { create(:collection, user: user) } - let!(:collection2) { create(:collection, user: user) } - let(:attributes) do - attributes_for(:generic_work, member_of_collection_ids: [collection2.id]) - end - - before do - curation_concern.apply_depositor_metadata(user.user_key) - curation_concern.member_of_collections = [collection1] - curation_concern.save! - end - - it 'remove from the old collection and adds to the new collection' do - curation_concern.reload - expect(curation_concern.member_of_collection_ids).to eq [collection1.id] - # before running actor.update, the work is in collection1 - - expect(subject.update(env)).to be true - - curation_concern.reload - expect(curation_concern.identifier).to be_blank - expect(curation_concern).to be_persisted - # after running actor.update, the work is in collection2 and no longer in collection1 - expect(curation_concern.member_of_collections).to eq [collection2] - end - end - context 'with multiple file sets' do let(:file_set1) { create(:file_set) } let(:file_set2) { create(:file_set) } diff --git a/spec/actors/hyrax/actors/model_actor_spec.rb b/spec/actors/hyrax/actors/model_actor_spec.rb index 5f9202a78c..31f6cca033 100644 --- a/spec/actors/hyrax/actors/model_actor_spec.rb +++ b/spec/actors/hyrax/actors/model_actor_spec.rb @@ -1,5 +1,3 @@ -# coding: utf-8 - module MusicalWork class Cover < ActiveFedora::Base end diff --git a/spec/authorities/qa/authorities/collections_spec.rb b/spec/authorities/qa/authorities/collections_spec.rb new file mode 100644 index 0000000000..e9c2b6133a --- /dev/null +++ b/spec/authorities/qa/authorities/collections_spec.rb @@ -0,0 +1,25 @@ +RSpec.describe Qa::Authorities::Collections, :clean_repo do + let(:controller) { Qa::TermsController.new } + let(:user1) { create(:user) } + let(:user2) { create(:user) } + let(:q) { "foo" } + let(:params) { ActionController::Parameters.new(q: q, access: 'edit') } + let(:service) { described_class.new } + let!(:collection1) { create(:collection, :public, title: ['foo foo'], user: user1) } + let!(:collection2) { create(:collection, :public, title: ['bar'], user: user1) } + let!(:collection3) { create(:collection, :public, title: ['another foo'], user: user1) } + let!(:collection4) { create(:collection, :public, title: ['foo foo foo'], user: user2) } + + before do + allow(controller).to receive(:params).and_return(params) + allow(controller).to receive(:current_user).and_return(user1) + end + + subject { service.search(q, controller) } + + describe '#search' do + it 'displays a list of collections for the current user' do + expect(subject.map { |result| result[:id] }).to match_array [collection1.id, collection3.id] + end + end +end diff --git a/spec/channels/hyrax/application_cable/channel_spec.rb b/spec/channels/hyrax/application_cable/channel_spec.rb index d393b2a38c..8ac2cd41b3 100644 --- a/spec/channels/hyrax/application_cable/channel_spec.rb +++ b/spec/channels/hyrax/application_cable/channel_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe Hyrax::ApplicationCable::Channel, no_clean: true do +RSpec.describe Hyrax::ApplicationCable::Channel do subject { described_class.new(connection, identifier) } let(:connection) { double('connection', identifiers: []) } diff --git a/spec/channels/hyrax/application_cable/connection_spec.rb b/spec/channels/hyrax/application_cable/connection_spec.rb index 32041e305c..a37a718625 100644 --- a/spec/channels/hyrax/application_cable/connection_spec.rb +++ b/spec/channels/hyrax/application_cable/connection_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe Hyrax::ApplicationCable::Connection, no_clean: true do +RSpec.describe Hyrax::ApplicationCable::Connection do subject { described_class.new(server, env) } let(:server) { ActionCable::Server::Base.new } diff --git a/spec/channels/hyrax/notifications_channel_spec.rb b/spec/channels/hyrax/notifications_channel_spec.rb index 3d960f52ae..5a788ede94 100644 --- a/spec/channels/hyrax/notifications_channel_spec.rb +++ b/spec/channels/hyrax/notifications_channel_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe Hyrax::NotificationsChannel, no_clean: true do +RSpec.describe Hyrax::NotificationsChannel do subject { described_class.new(connection, nil) } let(:connection) { Hyrax::ApplicationCable::Connection.new(server, {}) } diff --git a/spec/controllers/catalog_controller_spec.rb b/spec/controllers/catalog_controller_spec.rb index bf59ba96ad..80d4891ee3 100644 --- a/spec/controllers/catalog_controller_spec.rb +++ b/spec/controllers/catalog_controller_spec.rb @@ -131,5 +131,5 @@ expect(assigns(:document_list).map(&:id)).to contain_exactly(work1[:id]) end end - end # describe "#index" + end end diff --git a/spec/controllers/hyrax/file_sets_controller_spec.rb b/spec/controllers/hyrax/file_sets_controller_spec.rb index d0195c77c0..5e4bbf7548 100644 --- a/spec/controllers/hyrax/file_sets_controller_spec.rb +++ b/spec/controllers/hyrax/file_sets_controller_spec.rb @@ -12,105 +12,6 @@ allow(CreateDerivativesJob).to receive(:perform_later) end - describe '#create' do - let(:parent) do - create(:generic_work, :public, edit_users: [user.user_key]) - end - let(:file) { fixture_file_upload('image.png', 'image/png') } - let(:file_path) { fixture_path + '/small_file.txt' } - - context 'on the happy path' do - let(:expected_params) do - { files: [file], - title: ['test title'], - visibility: 'restricted' } - end - - it 'calls the actor to create metadata and content' do - expect(actor).to receive(:create_metadata).with(ActionController::Parameters) do |ac_params| - expect(ac_params['files'].map(&:class)).to eq [ActionDispatch::Http::UploadedFile] - expect(ac_params['title']).to eq expected_params[:title] - expect(ac_params['visibility']).to eq expected_params[:visibility] - end - expect(actor).to receive(:attach_to_work).with(parent).and_return(true) - expect(actor).to receive(:create_content).with(ActionDispatch::Http::UploadedFile).and_return(true) - - post :create, xhr: true, params: { parent_id: parent, - file_set: { - files: [file], - title: ['test title'], - visibility: 'restricted' - } } - expect(response).to be_success - expect(flash[:error]).to be_nil - end - end - - context "on something that isn't a file" do - # Note: This is a duplicate of coverage in file_sets_controller_json_spec.rb - it 'renders error' do - post :create, xhr: true, params: { parent_id: parent, - file_set: { files: ['hello'] }, - permission: { group: { 'public' => 'read' } }, - terms_of_service: '1' } - expect(response.status).to eq 400 - msg = JSON.parse(response.body)['message'] - expect(msg).to match(/No file uploaded/i) - end - end - - subject { create(:file_set) } - - context 'when the file has a virus' do - before do - allow(subject).to receive(:warn) # suppress virus warnings - allow(Hydra::Works::VirusCheckerService).to receive(:file_has_virus?) { true } - of = subject.build_original_file - of.content = File.open(file_path) - end - it 'populates the errors hash during validation' do - expect(subject).not_to be_valid - expect(subject.errors.messages[:base].first).to eq "Failed to verify uploaded file is not a virus" - end - end - - context 'when solr is down' do - before do - allow(actor).to receive(:create_metadata) - allow(actor).to receive(:attach_to_work) - allow(actor).to receive(:create_content).and_raise(RSolr::Error::Http.new({}, {})) - end - - it 'errors out of create after on continuous rsolr error' do - post :create, xhr: true, params: { - parent_id: parent, - file_set: { files: [file] }, - permission: { group: { 'public' => 'read' } }, - terms_of_service: '1' - } - expect(response.body).to include('Error occurred while creating a FileSet.') - end - end - - context 'when the file is not created' do - before do - allow(controller.send(:actor)).to receive(:create_metadata) - allow(controller.send(:actor)).to receive(:attach_to_work) - allow(controller.send(:actor)).to receive(:create_content).and_return(false) - end - - it 'errors out of create after on continuous rsolr error' do - post :create, xhr: true, params: { - parent_id: parent, - file_set: { files: [file] }, - permission: { group: { 'public' => 'read' } }, - terms_of_service: '1' - } - expect(response.body).to include('Error creating file image.png') - end - end - end - describe "#destroy" do context "file_set with a parent" do let(:file_set) do @@ -138,8 +39,14 @@ end describe "#edit" do + let(:parent) do + create(:work, :public, user: user) + end let(:file_set) do - create(:file_set, user: user) + create(:file_set, user: user).tap do |file_set| + parent.ordered_members << file_set + parent.save! + end end before do @@ -157,6 +64,7 @@ expect(response).to be_success expect(assigns[:file_set]).to eq file_set expect(assigns[:version_list]).to be_kind_of Hyrax::VersionListPresenter + expect(assigns[:parent]).to eq parent expect(response).to render_template(:edit) end end @@ -405,41 +313,5 @@ expect(response).to be_success end end - - describe '#new' do - let(:parent) do - create(:generic_work, :public) - end - - it 'does not let the user submit' do - get :new, params: { parent_id: parent } - expect(response).to fail_redirect_and_flash(main_app.new_user_session_path, 'You need to sign in or sign up before continuing.') - end - end - end - - context 'finds parents' do - let(:parent) do - create(:generic_work, :public, edit_users: [user.user_key]) - end - - let(:file_set) do - file_set = create(:file_set, user: user) - parent.ordered_members << file_set - parent.save - file_set - end - - before do - allow(controller).to receive(:curation_concern).and_return(file_set) - end - - it 'finds a parent' do - expect(controller.parent).to eq(parent) - end - - it 'finds a parent id' do - expect(controller.parent_id).to eq(parent.id) - end end end diff --git a/spec/controllers/hyrax/fixity_checks_controller_spec.rb b/spec/controllers/hyrax/fixity_checks_controller_spec.rb index 1d1dd039e6..28b861e8ea 100644 --- a/spec/controllers/hyrax/fixity_checks_controller_spec.rb +++ b/spec/controllers/hyrax/fixity_checks_controller_spec.rb @@ -21,7 +21,7 @@ # { file_id => [{ "checked_uri" => "...4-4d71-83ba-1bc52a5e4300/fcr:versions/version1", "passed" => true }, # { "checked_uri" => ".../version2", "passed" => false }, # ...] } - json_response.each do |_file_id, array_of_checks| + json_response.each_value do |array_of_checks| array_of_checks.each do |check_hash| expect(check_hash.keys).to include("file_set_id", "file_id", "checked_uri", "passed", "expected_result", "created_at") expect(check_hash["passed"]).to be_in([true, false]) diff --git a/spec/controllers/hyrax/generic_works_controller_spec.rb b/spec/controllers/hyrax/generic_works_controller_spec.rb index 5100a23840..fbf1d3eb80 100644 --- a/spec/controllers/hyrax/generic_works_controller_spec.rb +++ b/spec/controllers/hyrax/generic_works_controller_spec.rb @@ -548,4 +548,24 @@ expect(assigns(:form)).not_to be_blank end end + + describe '#manifest' do + let(:work) { create(:work_with_one_file, user: user) } + let(:file_set) { work.ordered_members.to_a.first } + let(:manifest_factory) { double(to_h: { test: 'manifest' }) } + + before do + Hydra::Works::AddFileToFileSet.call(file_set, + File.open(fixture_path + '/world.png'), + :original_file) + allow(IIIFManifest::ManifestFactory).to receive(:new) + .with(Hyrax::WorkShowPresenter) + .and_return(manifest_factory) + end + + it "produces a manifest" do + get :manifest, params: { id: work, format: :json } + expect(response.body).to eq "{\"test\":\"manifest\"}" + end + end end diff --git a/spec/conversions/power_converters/polymorphic_type_spec.rb b/spec/conversions/power_converters/polymorphic_type_spec.rb index 1f481fdac6..240fb7b3d6 100644 --- a/spec/conversions/power_converters/polymorphic_type_spec.rb +++ b/spec/conversions/power_converters/polymorphic_type_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -RSpec.describe 'PowerConverter', no_clean: true do +RSpec.describe 'PowerConverter' do describe '#convert_to_polymorphic_type' do it 'will convert an object that responds to #to_polymorphic_type' do object = double(to_polymorphic_type: :symbol) diff --git a/spec/conversions/power_converters/sipity_agent_spec.rb b/spec/conversions/power_converters/sipity_agent_spec.rb index 20287b5a62..b10418f6ef 100644 --- a/spec/conversions/power_converters/sipity_agent_spec.rb +++ b/spec/conversions/power_converters/sipity_agent_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -RSpec.describe 'PowerConverter', no_clean: true do +RSpec.describe 'PowerConverter' do context '#convert_to_sipity_agent' do it 'will convert a Sipity::Agent' do object = Sipity::Agent.new diff --git a/spec/conversions/power_converters/sipity_role_spec.rb b/spec/conversions/power_converters/sipity_role_spec.rb index f1bb6e2d66..498970066c 100644 --- a/spec/conversions/power_converters/sipity_role_spec.rb +++ b/spec/conversions/power_converters/sipity_role_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe 'PowerConverter', no_clean: true do +RSpec.describe 'PowerConverter' do context 'role' do it "converts Sipity::Role" do object = Sipity::Role.new diff --git a/spec/conversions/power_converters/sipity_workflow_state_spec.rb b/spec/conversions/power_converters/sipity_workflow_state_spec.rb index fc5b5eeee7..14bca1da0a 100644 --- a/spec/conversions/power_converters/sipity_workflow_state_spec.rb +++ b/spec/conversions/power_converters/sipity_workflow_state_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe 'PowerConverter', no_clean: true do +RSpec.describe 'PowerConverter' do context 'sipity_workflow_state' do let(:workflow_state) { Sipity::WorkflowState.new(id: 1, name: 'hello') } let(:workflow) { create(:workflow) } diff --git a/spec/factories/permission_templates.rb b/spec/factories/permission_templates.rb index 150f5d4a5c..2758d66e93 100644 --- a/spec/factories/permission_templates.rb +++ b/spec/factories/permission_templates.rb @@ -11,7 +11,7 @@ if source_id.present? begin AdminSet.find(source_id) - rescue + rescue ActiveFedora::ObjectNotFoundError create(:admin_set, id: source_id) end else diff --git a/spec/features/admin_spec.rb b/spec/features/admin_spec.rb index dec2267496..18502eeb7d 100644 --- a/spec/features/admin_spec.rb +++ b/spec/features/admin_spec.rb @@ -17,7 +17,7 @@ create(:work_with_two_children, title: ["Work C"], admin_set_id: admin_set_2.id, edit_users: [user]) end - scenario do + it do login_as(user, scope: :user) visit '/dashboard' diff --git a/spec/features/batch_create_spec.rb b/spec/features/batch_create_spec.rb index b844156925..81494ba016 100644 --- a/spec/features/batch_create_spec.rb +++ b/spec/features/batch_create_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.feature 'Batch creation of works', type: :feature do +RSpec.describe 'Batch creation of works', type: :feature do let(:user) { create(:user) } before do diff --git a/spec/features/browse_catalog_spec.rb b/spec/features/browse_catalog_spec.rb index ec92aa9095..84ef587ad2 100644 --- a/spec/features/browse_catalog_spec.rb +++ b/spec/features/browse_catalog_spec.rb @@ -1,5 +1,3 @@ -# coding: utf-8 - RSpec.describe "Browse catalog:", type: :feature, clean_repo: true do let!(:jills_work) do GenericWork.new do |work| diff --git a/spec/features/create_child_work_spec.rb b/spec/features/create_child_work_spec.rb index 23610fc994..f1503f4d37 100644 --- a/spec/features/create_child_work_spec.rb +++ b/spec/features/create_child_work_spec.rb @@ -1,6 +1,6 @@ require 'redlock' -RSpec.feature 'Creating a new child Work', :workflow do +RSpec.describe 'Creating a new child Work', :workflow do let(:user) { create(:user) } let!(:sipity_entity) do create(:sipity_entity, proxy_for_global_id: parent.to_global_id.to_s) diff --git a/spec/features/create_work_spec.rb b/spec/features/create_work_spec.rb index efdee887f6..bce19dabea 100644 --- a/spec/features/create_work_spec.rb +++ b/spec/features/create_work_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature 'Creating a new Work', :js, :workflow do +RSpec.describe 'Creating a new Work', :js, :workflow do let(:user) { create(:user) } let(:file1) { File.open(fixture_path + '/world.png') } let(:file2) { File.open(fixture_path + '/image.jp2') } diff --git a/spec/features/dashboard/all_works.rb b/spec/features/dashboard/all_works.rb index 7ddf75080c..d246ad2e30 100644 --- a/spec/features/dashboard/all_works.rb +++ b/spec/features/dashboard/all_works.rb @@ -5,7 +5,7 @@ before do sign_in create(:admin) end - scenario do + it do visit '/dashboard/works' expect(page).to have_content 'Works' expect(page).to have_content 'Testing #1' diff --git a/spec/features/delete_work_spec.rb b/spec/features/delete_work_spec.rb index ac941ac511..c1ca2c2af3 100644 --- a/spec/features/delete_work_spec.rb +++ b/spec/features/delete_work_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature 'Deleting a work', type: :feature do +RSpec.describe 'Deleting a work', type: :feature do let(:user) { create(:user) } let(:work) { build(:work, user: user) } let(:file_set) { create(:file_set, user: user, title: ['ABC123xyz']) } diff --git a/spec/features/edit_file_spec.rb b/spec/features/edit_file_spec.rb index 0ab250f965..6ed8b786b4 100644 --- a/spec/features/edit_file_spec.rb +++ b/spec/features/edit_file_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature "Editing a file:", type: :feature do +RSpec.describe "Editing a file:", type: :feature do let(:user) { create(:user) } let(:file_title) { 'Some kind of title' } let(:work) { build(:work, user: user) } diff --git a/spec/features/edit_work_spec.rb b/spec/features/edit_work_spec.rb index 4417eb3203..0f78528440 100644 --- a/spec/features/edit_work_spec.rb +++ b/spec/features/edit_work_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature 'Editing a work', type: :feature do +RSpec.describe 'Editing a work', type: :feature do let(:user) { create(:user) } let(:work) { build(:work, user: user) } diff --git a/spec/features/embargo_spec.rb b/spec/features/embargo_spec.rb index 3ceef6abae..6b8f5e9e2c 100644 --- a/spec/features/embargo_spec.rb +++ b/spec/features/embargo_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature 'embargo' do +RSpec.describe 'embargo' do let(:user) { create(:user) } before do diff --git a/spec/features/homepage_spec.rb b/spec/features/homepage_spec.rb index 643b56ea46..5e6ee0269b 100644 --- a/spec/features/homepage_spec.rb +++ b/spec/features/homepage_spec.rb @@ -1,11 +1,11 @@ -RSpec.feature "The homepage" do +RSpec.describe "The homepage" do let(:work1) { create(:work, :public, title: ['Work 1']) } before do create(:featured_work, work_id: work1.id) end - scenario 'it shows featured works' do + it 'shows featured works' do visit root_path expect(page).to have_link "Work 1" end @@ -17,7 +17,7 @@ sign_in user end - scenario 'it shows featured works that I can sort' do + it 'shows featured works that I can sort' do visit root_path within '.dd-item' do expect(page).to have_link "Work 1" diff --git a/spec/features/lease_spec.rb b/spec/features/lease_spec.rb index 64a6447b92..95ea9f4f5a 100644 --- a/spec/features/lease_spec.rb +++ b/spec/features/lease_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature 'leases' do +RSpec.describe 'leases' do let(:user) { create(:user) } before do diff --git a/spec/features/notifications_spec.rb b/spec/features/notifications_spec.rb index 25fdbe355e..324ceefc75 100644 --- a/spec/features/notifications_spec.rb +++ b/spec/features/notifications_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature "Notifications page", type: :feature do +RSpec.describe "Notifications page", type: :feature do before do sign_in create(:user_with_mail) visit "/notifications" diff --git a/spec/features/ownership_transfer_spec.rb b/spec/features/ownership_transfer_spec.rb index 2dce4abcfe..86ed42da93 100644 --- a/spec/features/ownership_transfer_spec.rb +++ b/spec/features/ownership_transfer_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature 'Transferring work ownership:', type: :feature do +RSpec.describe 'Transferring work ownership:', type: :feature do include Selectors::Dashboard include Selectors::NewTransfers include Selectors::Transfers diff --git a/spec/features/proxy_spec.rb b/spec/features/proxy_spec.rb index a8850a94db..7ba3564793 100644 --- a/spec/features/proxy_spec.rb +++ b/spec/features/proxy_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature 'proxy', type: :feature do +RSpec.describe 'proxy', type: :feature do let(:user) { create(:user) } let(:second_user) { create(:user) } diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index 7de8257614..1895d562d8 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature 'searching' do +RSpec.describe 'searching' do let(:user) { create :user } let(:subject_value) { 'mustache' } let!(:work) do diff --git a/spec/features/static_pages_spec.rb b/spec/features/static_pages_spec.rb index e07e71f896..40c7e6449e 100644 --- a/spec/features/static_pages_spec.rb +++ b/spec/features/static_pages_spec.rb @@ -1,5 +1,5 @@ RSpec.describe "The static pages" do - scenario do + it do visit root_path click_link "About" click_link "Help" diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index b3957a8849..004e432326 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature "User Profile", type: :feature do +RSpec.describe "User Profile", type: :feature do before do sign_in user end diff --git a/spec/features/work_generator_spec.rb b/spec/features/work_generator_spec.rb index 180314e984..2684cd267c 100644 --- a/spec/features/work_generator_spec.rb +++ b/spec/features/work_generator_spec.rb @@ -1,7 +1,7 @@ require 'rails/generators' require 'redlock' -RSpec.feature 'Creating a new Work' do +RSpec.describe 'Creating a new Work' do before do Rails::Generators.invoke('hyrax:work', ['Catapult', '--quiet'], destination_root: Rails.root) load "#{EngineCart.destination}/app/indexers/catapult_indexer.rb" diff --git a/spec/features/work_show_spec.rb b/spec/features/work_show_spec.rb index 78f1689b8a..d60773de1a 100644 --- a/spec/features/work_show_spec.rb +++ b/spec/features/work_show_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature "display a work as its owner" do +RSpec.describe "display a work as its owner" do let(:work_path) { "/concern/generic_works/#{work.id}" } before do diff --git a/spec/features/workflow_roles_spec.rb b/spec/features/workflow_roles_spec.rb index 2d568d3bcd..f20377e958 100644 --- a/spec/features/workflow_roles_spec.rb +++ b/spec/features/workflow_roles_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature "Manage workflow roles", type: :feature do +RSpec.describe "Manage workflow roles", type: :feature do let(:user) { create(:admin) } let(:one_step_workflow) do { diff --git a/spec/features/workflow_state_changes_spec.rb b/spec/features/workflow_state_changes_spec.rb index d33028faf6..db4a34ea79 100644 --- a/spec/features/workflow_state_changes_spec.rb +++ b/spec/features/workflow_state_changes_spec.rb @@ -1,4 +1,4 @@ -RSpec.feature "Workflow state changes", type: :feature do +RSpec.describe "Workflow state changes", type: :feature do let(:workflow_name) { 'with_comment' } let(:approving_user) { create(:admin) } let(:depositing_user) { create(:admin) } diff --git a/spec/forms/hyrax/forms/work_form_spec.rb b/spec/forms/hyrax/forms/work_form_spec.rb index f80de66a8c..d831bf67ce 100644 --- a/spec/forms/hyrax/forms/work_form_spec.rb +++ b/spec/forms/hyrax/forms/work_form_spec.rb @@ -1,7 +1,8 @@ RSpec.describe Hyrax::Forms::WorkForm do let(:work) { GenericWork.new } - let(:form) { described_class.new(work, nil, nil) } + let(:form) { described_class.new(work, nil, controller) } let(:works) { [GenericWork.new, FileSet.new, GenericWork.new] } + let(:controller) { instance_double(Hyrax::GenericWorksController) } # This class is an abstract class, so we have to set model_class # TODO: merge with generic_work_form_spec @@ -30,26 +31,34 @@ end describe '#member_of_collections' do - subject { form.member_of_collections(args) } + subject { form.member_of_collections } + + before do + allow(controller).to receive(:params).and_return(add_works_to_collection: collection_id) + end context 'when passed nil' do - let(:args) { nil } + let(:collection_id) { nil } it { is_expected.to be_empty } end + context 'when passed a string' do - let(:args) { 'foobar' } + let(:collection) { create(:collection) } + let(:collection_id) { collection.id } - it { is_expected.to match_array([args]) } + it { is_expected.to match_array([collection]) } end + context 'when member of other collections' do - let(:args) { 'bar' } + let(:collection) { create(:collection) } + let(:collection_id) { collection.id } before do - allow(work).to receive(:member_of_collection_ids).and_return(['foo']) + allow(work).to receive(:member_of_collections).and_return(['foo']) end - it { is_expected.to match_array(['foo', args]) } + it { is_expected.to match_array(['foo', collection]) } end end @@ -80,8 +89,9 @@ :version, :on_behalf_of, { permissions_attributes: [:type, :name, :access, :id, :_destroy] }, - work_members_attributes: [:id, :_destroy], - based_near_attributes: [:id, :_destroy]) + based_near_attributes: [:id, :_destroy], + member_of_collections_attributes: [:id, :_destroy], + work_members_attributes: [:id, :_destroy]) } end end diff --git a/spec/forms/hyrax/forms/workflow_action_form_spec.rb b/spec/forms/hyrax/forms/workflow_action_form_spec.rb index e09b325bed..1f05af089c 100644 --- a/spec/forms/hyrax/forms/workflow_action_form_spec.rb +++ b/spec/forms/hyrax/forms/workflow_action_form_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe Hyrax::Forms::WorkflowActionForm, no_clean: true do +RSpec.describe Hyrax::Forms::WorkflowActionForm do let(:work) { create(:work) } let(:sipity_entity) do create(:sipity_entity, proxy_for_global_id: work.to_global_id.to_s, workflow_state_id: 2) diff --git a/spec/helpers/hyrax/collections_helper_spec.rb b/spec/helpers/hyrax/collections_helper_spec.rb index 4ad13b24e7..12b7e15c0f 100644 --- a/spec/helpers/hyrax/collections_helper_spec.rb +++ b/spec/helpers/hyrax/collections_helper_spec.rb @@ -53,44 +53,6 @@ end end - describe "button_for_remove_from_collection" do - let(:item) { double(id: 'changeme:123') } - let(:collection) { create(:collection) } - - it "generates a form that can remove the item" do - str = button_for_remove_from_collection collection, item - doc = Nokogiri::HTML(str) - form = doc.xpath('//form').first - expect(form.attr('action')).to eq hyrax.dashboard_collection_path(collection) - expect(form.css('input#collection_members[type="hidden"][value="remove"]')).not_to be_empty - expect(form.css('input[type="hidden"][name="batch_document_ids[]"][value="changeme:123"]')).not_to be_empty - end - - describe "for a collection of another name" do - before do - class OtherCollection < ActiveFedora::Base - include Hyrax::CollectionBehavior - include Hydra::Works::WorkBehavior - end - end - - let!(:collection) { OtherCollection.create!(title: ['foo']) } - - after do - Object.send(:remove_const, :OtherCollection) - end - - it "generates a form that can remove the item" do - str = button_for_remove_from_collection collection, item - doc = Nokogiri::HTML(str) - form = doc.xpath('//form').first - expect(form.attr('action')).to eq hyrax.dashboard_collection_path(collection) - expect(form.css('input#collection_members[type="hidden"][value="remove"]')).not_to be_empty - expect(form.css('input[type="hidden"][name="batch_document_ids[]"][value="changeme:123"]')).not_to be_empty - end - end - end - describe "button_for_remove_selected_from_collection" do let(:collection) { create(:collection) } diff --git a/spec/helpers/dashboard_helper_spec.rb b/spec/helpers/hyrax/dashboard_helper_behavior_spec.rb similarity index 97% rename from spec/helpers/dashboard_helper_spec.rb rename to spec/helpers/hyrax/dashboard_helper_behavior_spec.rb index 5dca704717..52e1004f09 100644 --- a/spec/helpers/dashboard_helper_spec.rb +++ b/spec/helpers/hyrax/dashboard_helper_behavior_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe DashboardHelper, type: :helper do +RSpec.describe Hyrax::DashboardHelperBehavior, type: :helper do describe "#on_the_dashboard?" do it "returns false for controllers that aren't a part of the dashboard" do allow(helper).to receive(:params).and_return(controller: "foo") diff --git a/spec/helpers/hyrax_helper_spec.rb b/spec/helpers/hyrax_helper_spec.rb index 5a42076127..9505dcca42 100644 --- a/spec/helpers/hyrax_helper_spec.rb +++ b/spec/helpers/hyrax_helper_spec.rb @@ -82,7 +82,6 @@ def new_state car_link = search_catalog_path(f: { 'vehicle_type_sim' => ['car'] }) truck_link = search_catalog_path(f: { 'vehicle_type_sim' => ['truck'] }) expect(subject).to eq "car, truck" - expect(subject).to be_a ActiveSupport::SafeBuffer expect(subject).to be_html_safe end end @@ -92,8 +91,6 @@ def new_state it "shows the default text" do expect(subject).to eq "No value entered" - expect(subject).to be_a ActiveSupport::SafeBuffer - expect(subject).to be_html_safe end end end diff --git a/spec/indexers/hyrax/generic_work_indexer_spec.rb b/spec/indexers/hyrax/generic_work_indexer_spec.rb index a5e880c122..6cfb8f24d3 100644 --- a/spec/indexers/hyrax/generic_work_indexer_spec.rb +++ b/spec/indexers/hyrax/generic_work_indexer_spec.rb @@ -98,14 +98,14 @@ end describe "with a remote resource (based near)" do - mpls = < Minneapolis -EOF +RDFXML before do allow(service).to receive(:rdf_service).and_return(Hyrax::DeepIndexingService) diff --git a/spec/javascripts/relationships_control_spec.js.coffee b/spec/javascripts/relationships_control_spec.js.coffee index 692f64e882..4b3d435660 100644 --- a/spec/javascripts/relationships_control_spec.js.coffee +++ b/spec/javascripts/relationships_control_spec.js.coffee @@ -7,7 +7,7 @@ describe 'RelationshipsControl', -> beforeEach -> fixture = setFixtures(test_fixtures.relationships_table.html) element = $("table") - target = new RelationshipsControl(element, 'work_members_attributes', 'tmpl-child-work') + target = new RelationshipsControl(element, [], 'generic_work', 'work_members_attributes', 'tmpl-child-work') jasmine.Ajax.install() afterEach -> @@ -21,7 +21,7 @@ describe 'RelationshipsControl', -> it 'creates a row when something is selected', -> spyOn(target.input, 'val').and.returnValue('123') spyOn(target, 'searchData').and.returnValue({ id: '123', text: 'foo bar' }) - expect(target.registry.nextIndex()).toEqual(0) + expect(target.registry.items.length).toEqual(0) target.attemptToAddRow() expect(target.errors).toBeNull() - expect(target.registry.nextIndex()).toEqual(1) + expect(target.registry.items.length).toEqual(1) diff --git a/spec/lib/hyrax/configuration_spec.rb b/spec/lib/hyrax/configuration_spec.rb index 1f44dc50c2..d068de67dc 100644 --- a/spec/lib/hyrax/configuration_spec.rb +++ b/spec/lib/hyrax/configuration_spec.rb @@ -42,6 +42,16 @@ it { is_expected.to respond_to(:feature_config_path) } it { is_expected.to respond_to(:google_analytics_id?) } it { is_expected.to respond_to(:google_analytics_id) } + it { is_expected.to respond_to(:iiif_image_server?) } + it { is_expected.to respond_to(:iiif_image_server=) } + it { is_expected.to respond_to(:iiif_image_url_builder) } + it { is_expected.to respond_to(:iiif_image_url_builder=) } + it { is_expected.to respond_to(:iiif_info_url_builder) } + it { is_expected.to respond_to(:iiif_info_url_builder=) } + it { is_expected.to respond_to(:iiif_image_compliance_level_uri) } + it { is_expected.to respond_to(:iiif_image_compliance_level_uri=) } + it { is_expected.to respond_to(:iiif_image_size_default) } + it { is_expected.to respond_to(:iiif_image_size_default=) } it { is_expected.to respond_to(:libreoffice_path) } it { is_expected.to respond_to(:license_service_class) } it { is_expected.to respond_to(:license_service_class=) } diff --git a/spec/models/sipity/agent_spec.rb b/spec/models/sipity/agent_spec.rb index ee2b5fd789..ed53dea261 100644 --- a/spec/models/sipity/agent_spec.rb +++ b/spec/models/sipity/agent_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe Agent, type: :model, no_clean: true do + RSpec.describe Agent, type: :model do subject { described_class } its(:column_names) { is_expected.to include("proxy_for_id") } diff --git a/spec/models/sipity/comment_spec.rb b/spec/models/sipity/comment_spec.rb index c50f965a56..4848fdc960 100644 --- a/spec/models/sipity/comment_spec.rb +++ b/spec/models/sipity/comment_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe Comment, type: :model, no_clean: true do + RSpec.describe Comment, type: :model do context 'database configuration' do subject { described_class } diff --git a/spec/models/sipity/entity_spec.rb b/spec/models/sipity/entity_spec.rb index d0b0f7f13e..fc2859d8f5 100644 --- a/spec/models/sipity/entity_spec.rb +++ b/spec/models/sipity/entity_spec.rb @@ -1,6 +1,6 @@ module Sipity RSpec.describe Entity, type: :model do - describe 'database configuration', no_clean: true do + describe 'database configuration' do subject { described_class } its(:column_names) { is_expected.to include("proxy_for_global_id") } @@ -10,7 +10,7 @@ module Sipity subject { described_class.new } - describe 'delegations', no_clean: true do + describe 'delegations' do it { is_expected.to delegate_method(:workflow_state_name).to(:workflow_state).as(:name) } it { is_expected.to delegate_method(:workflow_name).to(:workflow).as(:name) } end diff --git a/spec/models/sipity/entity_specific_responsibility_spec.rb b/spec/models/sipity/entity_specific_responsibility_spec.rb index f81b92f026..00899eddc1 100644 --- a/spec/models/sipity/entity_specific_responsibility_spec.rb +++ b/spec/models/sipity/entity_specific_responsibility_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe EntitySpecificResponsibility, type: :model, no_clean: true do + RSpec.describe EntitySpecificResponsibility, type: :model do subject { described_class } its(:column_names) { is_expected.to include('workflow_role_id') } diff --git a/spec/models/sipity/notifiable_context_spec.rb b/spec/models/sipity/notifiable_context_spec.rb index cc4db2895a..45251db396 100644 --- a/spec/models/sipity/notifiable_context_spec.rb +++ b/spec/models/sipity/notifiable_context_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe NotifiableContext, type: :model, no_clean: true do + RSpec.describe NotifiableContext, type: :model do subject { described_class.new } it "should have tests" diff --git a/spec/models/sipity/notification_recipient_spec.rb b/spec/models/sipity/notification_recipient_spec.rb index 41ec6be88b..69b594383a 100644 --- a/spec/models/sipity/notification_recipient_spec.rb +++ b/spec/models/sipity/notification_recipient_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe NotificationRecipient, type: :model, no_clean: true do + RSpec.describe NotificationRecipient, type: :model do subject { described_class.new } it 'will raise an ArgumentError if you provide an invalid recipient_strategy' do diff --git a/spec/models/sipity/notification_spec.rb b/spec/models/sipity/notification_spec.rb index 0fb8615171..f698e33cbd 100644 --- a/spec/models/sipity/notification_spec.rb +++ b/spec/models/sipity/notification_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe Notification, type: :model, no_clean: true do + RSpec.describe Notification, type: :model do subject { described_class.new } it 'will raise an ArgumentError if you provide an invalid #notification_type' do diff --git a/spec/models/sipity/role_spec.rb b/spec/models/sipity/role_spec.rb index f78cb3dafc..ed219e54df 100644 --- a/spec/models/sipity/role_spec.rb +++ b/spec/models/sipity/role_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe Role, type: :model, no_clean: true do + RSpec.describe Role, type: :model do context 'class methods' do subject { described_class } diff --git a/spec/models/sipity/workflow_action_spec.rb b/spec/models/sipity/workflow_action_spec.rb index 69d2ca8714..38d93e3074 100644 --- a/spec/models/sipity/workflow_action_spec.rb +++ b/spec/models/sipity/workflow_action_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe WorkflowAction, type: :model, no_clean: true do + RSpec.describe WorkflowAction, type: :model do context 'database configuration' do subject { described_class } diff --git a/spec/models/sipity/workflow_responsibility_spec.rb b/spec/models/sipity/workflow_responsibility_spec.rb index d35862a338..114d37806c 100644 --- a/spec/models/sipity/workflow_responsibility_spec.rb +++ b/spec/models/sipity/workflow_responsibility_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe WorkflowResponsibility, type: :model, no_clean: true do + RSpec.describe WorkflowResponsibility, type: :model do subject { described_class } its(:column_names) { is_expected.to include('agent_id') } diff --git a/spec/models/sipity/workflow_role_spec.rb b/spec/models/sipity/workflow_role_spec.rb index 8bf61b181a..9fdf2dd8fb 100644 --- a/spec/models/sipity/workflow_role_spec.rb +++ b/spec/models/sipity/workflow_role_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe WorkflowRole, type: :model, no_clean: true do + RSpec.describe WorkflowRole, type: :model do subject { described_class } its(:column_names) { is_expected.to include('workflow_id') } diff --git a/spec/models/sipity/workflow_spec.rb b/spec/models/sipity/workflow_spec.rb index a0e92ea215..45fc88882c 100644 --- a/spec/models/sipity/workflow_spec.rb +++ b/spec/models/sipity/workflow_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe Workflow, type: :model, no_clean: true do + RSpec.describe Workflow, type: :model do context 'class configuration' do subject { described_class } diff --git a/spec/models/sipity/workflow_state_action_permission_spec.rb b/spec/models/sipity/workflow_state_action_permission_spec.rb index 3a3aa74598..6b206ea5d4 100644 --- a/spec/models/sipity/workflow_state_action_permission_spec.rb +++ b/spec/models/sipity/workflow_state_action_permission_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe WorkflowStateActionPermission, type: :model, no_clean: true do + RSpec.describe WorkflowStateActionPermission, type: :model do subject { described_class } its(:column_names) { is_expected.to include("workflow_role_id") } diff --git a/spec/models/sipity/workflow_state_action_spec.rb b/spec/models/sipity/workflow_state_action_spec.rb index 4e192e7971..1fcf1ee8b8 100644 --- a/spec/models/sipity/workflow_state_action_spec.rb +++ b/spec/models/sipity/workflow_state_action_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe WorkflowStateAction, type: :model, no_clean: true do + RSpec.describe WorkflowStateAction, type: :model do subject { described_class } its(:column_names) { is_expected.to include("originating_workflow_state_id") } diff --git a/spec/models/sipity/workflow_state_spec.rb b/spec/models/sipity/workflow_state_spec.rb index b2654cddaa..c6cac46bf7 100644 --- a/spec/models/sipity/workflow_state_spec.rb +++ b/spec/models/sipity/workflow_state_spec.rb @@ -1,5 +1,5 @@ module Sipity - RSpec.describe WorkflowState, type: :model, no_clean: true do + RSpec.describe WorkflowState, type: :model do subject { described_class } its(:column_names) { is_expected.to include("workflow_id") } diff --git a/spec/models/user_mailbox_spec.rb b/spec/models/user_mailbox_spec.rb index 56a93a16cd..1701db934f 100644 --- a/spec/models/user_mailbox_spec.rb +++ b/spec/models/user_mailbox_spec.rb @@ -1,5 +1,3 @@ -# coding: utf-8 - RSpec.describe UserMailbox do let(:user) { create(:user) } let(:another_user) { create(:user) } diff --git a/spec/presenters/hyrax/collection_options_presenter_spec.rb b/spec/presenters/hyrax/collection_options_presenter_spec.rb index 242f335f09..b980c0cccc 100644 --- a/spec/presenters/hyrax/collection_options_presenter_spec.rb +++ b/spec/presenters/hyrax/collection_options_presenter_spec.rb @@ -1,4 +1,6 @@ RSpec.describe Hyrax::CollectionOptionsPresenter do + before { allow(Deprecation).to receive(:warn) } + let(:instance) { described_class.new(service) } let(:doc1) { instance_double(SolrDocument, id: 4, to_s: 'Title 1') } let(:doc2) { instance_double(SolrDocument, id: 2, to_s: 'Other Title 1') } diff --git a/spec/presenters/hyrax/file_set_presenter_spec.rb b/spec/presenters/hyrax/file_set_presenter_spec.rb index 9783dd68e9..fea1ef0668 100644 --- a/spec/presenters/hyrax/file_set_presenter_spec.rb +++ b/spec/presenters/hyrax/file_set_presenter_spec.rb @@ -1,3 +1,5 @@ +require 'iiif_manifest' + RSpec.describe Hyrax::FileSetPresenter do let(:solr_document) { SolrDocument.new(attributes) } let(:ability) { double "Ability" } @@ -265,4 +267,109 @@ end end end + + describe 'IIIF integration' do + let(:file_set) { create(:file_set) } + let(:solr_document) { SolrDocument.new(file_set.to_solr) } + let(:request) { double(base_url: 'http://test.host') } + let(:presenter) { described_class.new(solr_document, nil, request) } + let(:id) { CGI.escape(file_set.original_file.id) } + + describe "#display_image" do + subject { presenter.display_image } + + context 'without a file' do + let(:id) { 'bogus' } + + it { is_expected.to be_nil } + end + + context 'with a file' do + before do + Hydra::Works::AddFileToFileSet.call(file_set, + file_path, :original_file) + end + + context "when the file is not an image" do + let(:file_path) { File.open(fixture_path + '/hyrax_generic_stub.txt') } + + it { is_expected.to be_nil } + end + + context "when the file is an image" do + let(:file_path) { File.open(fixture_path + '/world.png') } + + it { is_expected.to be_instance_of IIIFManifest::DisplayImage } + its(:url) { is_expected.to eq "http://test.host/images/#{id}/full/600,/0/default.jpg" } + + context 'with custom image size default' do + let(:custom_image_size) { '666,' } + + around do |example| + default_image_size = Hyrax.config.iiif_image_size_default + Hyrax.config.iiif_image_size_default = custom_image_size + example.run + Hyrax.config.iiif_image_size_default = default_image_size + end + + it { is_expected.to be_instance_of IIIFManifest::DisplayImage } + its(:url) { is_expected.to eq "http://test.host/images/#{id}/full/#{custom_image_size}/0/default.jpg" } + end + + context 'with custom image url builder' do + let(:id) { file_set.original_file.id } + let(:custom_builder) do + ->(file_id, base_url, _size) { "#{base_url}/downloads/#{file_id.split('/').first}" } + end + + around do |example| + default_builder = Hyrax.config.iiif_image_url_builder + Hyrax.config.iiif_image_url_builder = custom_builder + example.run + Hyrax.config.iiif_image_url_builder = default_builder + end + + it { is_expected.to be_instance_of IIIFManifest::DisplayImage } + its(:url) { is_expected.to eq "http://test.host/downloads/#{id.split('/').first}" } + end + end + end + end + + describe "#iiif_endpoint" do + subject { presenter.send(:iiif_endpoint, file_set.original_file.id) } + + before do + allow(Hyrax.config).to receive(:iiif_image_server?).and_return(riiif_enabled) + Hydra::Works::AddFileToFileSet.call(file_set, + File.open(fixture_path + '/world.png'), :original_file) + end + + context 'with iiif_image_server enabled' do + let(:riiif_enabled) { true } + + its(:url) { is_expected.to eq "http://test.host/images/#{id}" } + its(:profile) { is_expected.to eq 'http://iiif.io/api/image/2/level2.json' } + + context 'with a custom iiif image profile' do + let(:custom_profile) { 'http://iiif.io/api/image/2/level1.json' } + + around do |example| + default_profile = Hyrax.config.iiif_image_compliance_level_uri + Hyrax.config.iiif_image_compliance_level_uri = custom_profile + example.run + Hyrax.config.iiif_image_compliance_level_uri = default_profile + end + + its(:profile) { is_expected.to eq custom_profile } + end + end + + context 'with iiif_image_server disabled' do + let(:riiif_enabled) { false } + + it { is_expected.to be nil } + end + end + end end diff --git a/spec/presenters/hyrax/inspect_work_presenter_spec.rb b/spec/presenters/hyrax/inspect_work_presenter_spec.rb index 1fe5f77d16..dde840134d 100644 --- a/spec/presenters/hyrax/inspect_work_presenter_spec.rb +++ b/spec/presenters/hyrax/inspect_work_presenter_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe Hyrax::InspectWorkPresenter, no_clean: true do +RSpec.describe Hyrax::InspectWorkPresenter do let(:solr_document) { SolrDocument.new(attributes) } let(:attributes) do { "id" => '888888', diff --git a/spec/presenters/hyrax/work_show_presenter_spec.rb b/spec/presenters/hyrax/work_show_presenter_spec.rb index 08de4ca7c3..119999b1ff 100644 --- a/spec/presenters/hyrax/work_show_presenter_spec.rb +++ b/spec/presenters/hyrax/work_show_presenter_spec.rb @@ -1,6 +1,6 @@ RSpec.describe Hyrax::WorkShowPresenter do let(:solr_document) { SolrDocument.new(attributes) } - let(:request) { double(host: 'example.org') } + let(:request) { double(host: 'example.org', base_url: 'http://example.org') } let(:user_key) { 'a_user_key' } let(:attributes) do @@ -37,6 +37,12 @@ it { is_expected.to be_kind_of ActiveModel::Name } end + describe '#manifest_url' do + subject { presenter.manifest_url } + + it { is_expected.to eq 'http://example.org/concern/generic_works/888888/manifest' } + end + describe '#stats_path' do let(:user) { 'sarah' } let(:ability) { double "Ability" } diff --git a/spec/presenters/hyrax/workflow_presenter_spec.rb b/spec/presenters/hyrax/workflow_presenter_spec.rb index b76cd4f296..4796c09a99 100644 --- a/spec/presenters/hyrax/workflow_presenter_spec.rb +++ b/spec/presenters/hyrax/workflow_presenter_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe Hyrax::WorkflowPresenter, no_clean: true do +RSpec.describe Hyrax::WorkflowPresenter do let(:solr_document) { SolrDocument.new(attributes) } let(:attributes) do { "id" => '888888', diff --git a/spec/requests/riiif_spec.rb b/spec/requests/riiif_spec.rb new file mode 100644 index 0000000000..1a678f11c4 --- /dev/null +++ b/spec/requests/riiif_spec.rb @@ -0,0 +1,32 @@ +RSpec.describe 'IIIF image API', type: :request do + let(:user) { create(:user) } + let(:work) { create(:work_with_one_file, user: user) } + let(:file_set) { work.ordered_members.to_a.first } + let(:file) { file_set.original_file } + let(:size) { '300,' } + + before do + Hydra::Works::AddFileToFileSet.call(file_set, + File.open(fixture_path + '/world.png'), + :original_file) + end + + describe 'GET /images/:id' do + context "when the user is authorized" do + it "returns an image" do + login_as user + get Riiif::Engine.routes.url_helpers.image_path(file.id, size: size) + expect(response).to have_http_status(:ok) + expect(response.content_type).to eq 'image/jpeg' + end + end + + context "when the user is not authorized" do + it "returns an image" do + get Riiif::Engine.routes.url_helpers.image_path(file.id, size: size) + expect(response).to have_http_status(:unauthorized) + expect(response.content_type).to eq 'image/jpeg' + end + end + end +end diff --git a/spec/routing/route_spec.rb b/spec/routing/route_spec.rb index 416b14de26..0a73c46c4c 100644 --- a/spec/routing/route_spec.rb +++ b/spec/routing/route_spec.rb @@ -30,16 +30,6 @@ context "main app routes" do routes { Rails.application.routes } - context "with a file_set" do - it 'routes to create' do - expect(post: '/concern/container/12/file_sets').to route_to(controller: 'hyrax/file_sets', action: 'create', parent_id: '12') - end - - it 'routes to new' do - expect(get: '/concern/container/12/file_sets/new').to route_to(controller: 'hyrax/file_sets', action: 'new', parent_id: '12') - end - end - it 'routes to edit' do expect(get: '/concern/file_sets/3/edit').to route_to(controller: 'hyrax/file_sets', action: 'edit', id: '3') end @@ -206,6 +196,10 @@ it 'routes to inspect_work' do expect(get: 'concern/generic_works/6/inspect_work').to route_to(controller: 'hyrax/generic_works', action: 'inspect_work', id: '6') end + + it 'routes to manifest' do + expect(get: 'concern/generic_works/6/manifest').to route_to(controller: 'hyrax/generic_works', action: 'manifest', id: '6') + end end end end diff --git a/spec/services/hyrax/admin_set_create_service_spec.rb b/spec/services/hyrax/admin_set_create_service_spec.rb index 64b3a2c23e..0170dbc833 100644 --- a/spec/services/hyrax/admin_set_create_service_spec.rb +++ b/spec/services/hyrax/admin_set_create_service_spec.rb @@ -105,6 +105,7 @@ it { is_expected.to be false } it 'will not call the workflow_importer' do + subject expect(workflow_importer).not_to have_received(:call) end end diff --git a/spec/services/hyrax/iiif_authorization_service_spec.rb b/spec/services/hyrax/iiif_authorization_service_spec.rb new file mode 100644 index 0000000000..9f9c228334 --- /dev/null +++ b/spec/services/hyrax/iiif_authorization_service_spec.rb @@ -0,0 +1,43 @@ +RSpec.describe Hyrax::IIIFAuthorizationService do + let(:user) { create(:user) } + let(:ability) { Ability.new(user) } + let(:controller) { double(current_ability: ability) } + let(:service) { described_class.new(controller) } + let(:file_set_id) { 'mp48sc763' } + let(:image_id) { "#{file_set_id}/files/0b957460-99b4-4c31-902f-0fc23eefb972" } + let(:image) { Riiif::Image.new(image_id) } + + describe "#can?" do + context "when the user has read access to the FileSet" do + before { allow(ability).to receive(:test_read).with(file_set_id).and_return(true) } + + context "info" do + subject { service.can?(:info, image) } + + it { is_expected.to be true } + end + + context "show" do + subject { service.can?(:show, image) } + + it { is_expected.to be true } + end + end + + context "when the user doesn't have read access to the FileSet" do + before { allow(ability).to receive(:test_read).with(file_set_id).and_return(false) } + + context "info" do + subject { service.can?(:info, image) } + + it { is_expected.to be false } + end + + context "show" do + subject { service.can?(:show, image) } + + it { is_expected.to be false } + end + end + end +end diff --git a/spec/services/hyrax/parent_service_spec.rb b/spec/services/hyrax/parent_service_spec.rb deleted file mode 100644 index 3273fe3b0d..0000000000 --- a/spec/services/hyrax/parent_service_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -RSpec.describe Hyrax::ParentService do - subject { described_class } - - describe ".parent_for" do - context "when there's no ID" do - it "is nil" do - expect(subject.parent_for(nil)).to eq nil - end - end - end -end diff --git a/spec/services/hyrax/qa_select_service_spec.rb b/spec/services/hyrax/qa_select_service_spec.rb index 86dee04a67..8b5c1bdfd4 100644 --- a/spec/services/hyrax/qa_select_service_spec.rb +++ b/spec/services/hyrax/qa_select_service_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe Hyrax::QaSelectService, no_clean: true do +RSpec.describe Hyrax::QaSelectService do let(:authority) do # Implementing an ActiveRecord interface as required for this spec # rubocop:disable RSpec/InstanceVariable diff --git a/spec/services/hyrax/workflow/notification_configuration_parameter_spec.rb b/spec/services/hyrax/workflow/notification_configuration_parameter_spec.rb index 1d4c954648..0fe007d96c 100644 --- a/spec/services/hyrax/workflow/notification_configuration_parameter_spec.rb +++ b/spec/services/hyrax/workflow/notification_configuration_parameter_spec.rb @@ -1,6 +1,6 @@ module Hyrax module Workflow - RSpec.describe NotificationConfigurationParameter, no_clean: true do + RSpec.describe NotificationConfigurationParameter do describe '#build_from_workflow_action_configuration' do let(:config) do { diff --git a/spec/services/hyrax/workflow/notification_generator_spec.rb b/spec/services/hyrax/workflow/notification_generator_spec.rb index c1c28fc0fc..783abdb317 100644 --- a/spec/services/hyrax/workflow/notification_generator_spec.rb +++ b/spec/services/hyrax/workflow/notification_generator_spec.rb @@ -1,6 +1,6 @@ module Hyrax module Workflow - RSpec.describe NotificationGenerator, no_clean: true do + RSpec.describe NotificationGenerator do let(:workflow) { create(:workflow) } let(:recipients) { { to: 'creating_user', cc: 'advising', bcc: "data_observing" } } diff --git a/spec/services/hyrax/workflow/permission_query_spec.rb b/spec/services/hyrax/workflow/permission_query_spec.rb index 4afda69961..aa1b18ff01 100644 --- a/spec/services/hyrax/workflow/permission_query_spec.rb +++ b/spec/services/hyrax/workflow/permission_query_spec.rb @@ -1,6 +1,6 @@ module Hyrax module Workflow - RSpec.describe PermissionQuery, slow_test: true, no_clean: true do + RSpec.describe PermissionQuery, slow_test: true do let(:reviewing_user) { create(:user) } let(:completing_user) { create(:user) } let(:workflow_config) do @@ -174,7 +174,7 @@ def expect_entities_for(user:, entities:) end end - describe '.scope_processing_agents_for', no_clean: true do + describe '.scope_processing_agents_for' do context 'when user is not persisted' do subject { described_class.scope_processing_agents_for(user: ::User.new) } diff --git a/spec/services/hyrax/workflow/workflow_schema_spec.rb b/spec/services/hyrax/workflow/workflow_schema_spec.rb index f7350099b0..ebb07d822d 100644 --- a/spec/services/hyrax/workflow/workflow_schema_spec.rb +++ b/spec/services/hyrax/workflow/workflow_schema_spec.rb @@ -1,6 +1,6 @@ module Hyrax module Workflow - RSpec.describe 'WorkflowSchema', no_clean: true do + RSpec.describe 'WorkflowSchema' do let(:valid_data) do { workflows: [ diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 654b1fd1b0..02cf691697 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -198,6 +198,7 @@ def main_app end config.include EngineRoutes, type: :controller + config.include Warden::Test::Helpers, type: :request config.include Warden::Test::Helpers, type: :feature config.after(:each, type: :feature) do Warden.test_reset! diff --git a/spec/support/uploaded_file_monkeypatch.rb b/spec/support/uploaded_file_monkeypatch.rb deleted file mode 100644 index c5c1aa656e..0000000000 --- a/spec/support/uploaded_file_monkeypatch.rb +++ /dev/null @@ -1,6 +0,0 @@ -# Monkey patch UploadedFile so that it responds to read (same as ActionDispatch::Http::UploadedFile). Required by RestClient when posting to fedora. -class Rack::Test::UploadedFile - def read(*args) - @tempfile.read(*args) - end -end diff --git a/spec/test_app_templates/lib/generators/test_app_generator.rb b/spec/test_app_templates/lib/generators/test_app_generator.rb index e0acfd030d..7e6ddc353e 100644 --- a/spec/test_app_templates/lib/generators/test_app_generator.rb +++ b/spec/test_app_templates/lib/generators/test_app_generator.rb @@ -9,6 +9,10 @@ def install_engine generate 'hyrax:install', '-f' end + def browse_everything_install + generate "browse_everything:install --skip-assets" + end + def create_generic_work generate 'hyrax:work GenericWork' end @@ -59,19 +63,15 @@ def new_record? end end + def banner + say_status("info", "ADDING OVERRIDES FOR TEST ENVIRONMENT", :blue) + end + def comment_out_web_console gsub_file "Gemfile", "gem 'web-console'", "# gem 'web-console'" end - def browse_everything_install - generate "browse_everything:install --skip-assets" - end - - def banner - say_status("info", "ADDING OVERRIDES FOR TEST ENVIRONMENT", :blue) - end - def add_analytics_config append_file 'config/analytics.yml' do <<-EOS.strip_heredoc @@ -87,7 +87,12 @@ def add_analytics_config def enable_analytics gsub_file "config/initializers/hyrax.rb", - "config.analytics = false", "config.analytics = true" + "# config.analytics = false", "config.analytics = true" + end + + def enable_riiif_image_server + gsub_file "config/initializers/hyrax.rb", + "# config.iiif_image_server = false", "config.iiif_image_server = true" end def enable_i18n_translation_errors diff --git a/spec/views/_flash_msg.html.erb_spec.rb b/spec/views/_flash_msg.html.erb_spec.rb index 515789abad..7c99c01bc3 100644 --- a/spec/views/_flash_msg.html.erb_spec.rb +++ b/spec/views/_flash_msg.html.erb_spec.rb @@ -1,5 +1,3 @@ -# coding: utf-8 - RSpec.describe '/_flash_msg.html.erb', type: :view do before do allow(view).to receive(:flash).and_return(flash) diff --git a/spec/views/_user_util_links.html.erb_spec.rb b/spec/views/_user_util_links.html.erb_spec.rb index 54760b015c..edc02686c2 100644 --- a/spec/views/_user_util_links.html.erb_spec.rb +++ b/spec/views/_user_util_links.html.erb_spec.rb @@ -1,5 +1,3 @@ -# coding: utf-8 - RSpec.describe '/_user_util_links.html.erb', type: :view do let(:join_date) { 5.days.ago } let(:can_create_file) { true } diff --git a/spec/views/hyrax/base/_find_work_widget.html.erb_spec.rb b/spec/views/hyrax/base/_find_work_widget.html.erb_spec.rb deleted file mode 100644 index 28f78899e4..0000000000 --- a/spec/views/hyrax/base/_find_work_widget.html.erb_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -RSpec.describe 'hyrax/base/_find_work_widget.html.erb', type: :view do - let(:work) { stub_model(GenericWork) } - let(:form) do - view.simple_form_for(work, url: '/update') do |work_form| - return work_form - end - end - - before do - allow(view).to receive(:current_user).and_return(stub_model(User)) - render 'hyrax/base/find_work_widget', - f: form, - name: 'child_work', - user_email: 'foo@bar.com', - id: '999' - end - it "has a widget" do - expect(rendered).to have_selector('input[data-autocomplete-url="/authorities/search/find_works"]') - end -end diff --git a/spec/views/hyrax/base/_form.html.erb_spec.rb b/spec/views/hyrax/base/_form.html.erb_spec.rb index 28d02cb4b3..2d9bdec46d 100644 --- a/spec/views/hyrax/base/_form.html.erb_spec.rb +++ b/spec/views/hyrax/base/_form.html.erb_spec.rb @@ -23,7 +23,6 @@ allow(controller).to receive(:repository).and_return(Hyrax::GenericWorksController.new.repository) allow(controller).to receive(:blacklight_config).and_return(Hyrax::GenericWorksController.new.blacklight_config) - allow(form).to receive(:collections_for_select).and_return([]) allow(form).to receive(:permissions).and_return([]) allow(form).to receive(:visibility).and_return('public') stub_template 'hyrax/base/_form_files.html.erb' => 'files' diff --git a/spec/views/hyrax/base/_form_child_work_relationships.html.erb_spec.rb b/spec/views/hyrax/base/_form_child_work_relationships.html.erb_spec.rb index 1d287283c5..0bb2afb2b6 100644 --- a/spec/views/hyrax/base/_form_child_work_relationships.html.erb_spec.rb +++ b/spec/views/hyrax/base/_form_child_work_relationships.html.erb_spec.rb @@ -15,11 +15,6 @@ end end - let(:page) do - render - Capybara::Node::Simple.new(rendered) - end - before do # TODO: stub_model is not stubbing new_record? correctly on ActiveFedora models. allow(work).to receive(:new_record?).and_return(false) @@ -29,7 +24,6 @@ allow(view).to receive(:f).and_return(f) allow(f).to receive(:object).and_return(form) allow(controller).to receive(:current_user).and_return(stub_model(User)) - stub_template '_find_work_widget.html.erb' => "" assign(:form, form) end @@ -37,41 +31,36 @@ context "and no children works are present" do before do allow(form).to receive(:work_members).and_return([]) + render end it "draws the page" do # remove button is not present - expect(page).not_to have_selector("[data-behavior='remove-relationship']") + expect(rendered).not_to have_selector("[data-behavior='remove-relationship']") # input with add button - expect(page).to have_selector("input.finder") - expect(page).to have_selector("[data-behavior='add-relationship']") + expect(rendered).to have_selector('input[data-autocomplete-url="/authorities/search/find_works"]') + expect(rendered).to have_selector("[data-behavior='add-relationship']") end end context "and child works are present" do - let(:work_2) do - stub_model(GenericWork, id: '567', title: ["Test Child Work"]) - end - before do - allow(form).to receive(:work_members).and_return([work_2]) + allow(form).to receive(:work_members_json).and_return('stub-data') + render end it "draws the page" do # input with add button - expect(page).to have_selector("input.finder") - expect(page).to have_selector("[data-behavior='add-relationship']") - - # an input box that is filled in with the child id - expect(page).to have_selector("input[value='#{work_2.id}']", visible: false) + expect(rendered).to have_selector('input[data-autocomplete-url="/authorities/search/find_works"]') + expect(rendered).to have_selector("[data-behavior='add-relationship']") - # generate a link for the child work's title - expect(page).to have_link("Test Child Work") + # generate the json to drive the script + expect(rendered).to have_selector('div[data-members="stub-data"]') # a remove button within "tr" do - expect(page).to have_selector("[data-behavior='remove-relationship']") + expect(rendered).to have_selector("[data-behavior='remove-relationship']") end end end diff --git a/spec/views/hyrax/base/_form_relationships.html.erb_spec.rb b/spec/views/hyrax/base/_form_relationships.html.erb_spec.rb index 3ce1430d01..8fedb8c337 100644 --- a/spec/views/hyrax/base/_form_relationships.html.erb_spec.rb +++ b/spec/views/hyrax/base/_form_relationships.html.erb_spec.rb @@ -21,7 +21,6 @@ end before do - allow(form).to receive(:collections_for_select).and_return([]) allow(view).to receive(:action_name).and_return('new') allow(Hyrax::AdminSetService).to receive(:new).with(controller).and_return(service) allow(Hyrax::AdminSetOptionsPresenter).to receive(:new).with(service).and_return(presenter) diff --git a/spec/views/hyrax/dashboard/collections/_work_action_menu.html.erb_spec.rb b/spec/views/hyrax/dashboard/collections/_work_action_menu.html.erb_spec.rb new file mode 100644 index 0000000000..fe9cf8c5d0 --- /dev/null +++ b/spec/views/hyrax/dashboard/collections/_work_action_menu.html.erb_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +RSpec.describe 'hyrax/dashboard/collections/_work_action_menu.html.erb', type: :view do + let(:item) { stub_model(GenericWork) } + let(:collection) { stub_model(Collection) } + + before do + allow(view).to receive(:display_trophy_link) + assign(:collection, collection) + render 'hyrax/dashboard/collections/work_action_menu', document: item + end + + it "generates a form that can remove the item" do + expect(rendered).to have_selector("form[action=\"#{hyrax.dashboard_collection_path(collection)}\"]") + expect(rendered).to have_selector('input#collection_members[type="hidden"][value="remove"]', visible: false) + expect(rendered).to have_selector("input[type='hidden'][name='batch_document_ids[]'][value='#{item.id}']", visible: false) + end +end diff --git a/spec/views/hyrax/users/_user_info.html.erb_spec.rb b/spec/views/hyrax/users/_user_info.html.erb_spec.rb index b7e3fbea6b..fa9286b9b7 100644 --- a/spec/views/hyrax/users/_user_info.html.erb_spec.rb +++ b/spec/views/hyrax/users/_user_info.html.erb_spec.rb @@ -1,27 +1,25 @@ RSpec.describe 'hyrax/users/_user_info.html.erb', type: :view do - let(:user) { stub_model(User, user_key: 'jdoe42') } + let(:user) { stub_model(User, user_key: 'jdoe42', orcid: '000-000', zotero_userid: 'jdoe42zotero') } + let(:arkivo_api) { true } - context 'with Zotero disabled' do - before do - allow(Hyrax.config).to receive(:arkivo_api?) { false } - allow(user).to receive(:zotero_userid).and_raise(NoMethodError) - render "hyrax/users/user_info", user: user - end + before do + allow(Hyrax.config).to receive(:arkivo_api?).and_return(arkivo_api) + render "hyrax/users/user_info", user: user + end - it 'does not display a Zotero profile link' do - expect(rendered).not_to match(/Zotero Profile/) - end + it 'displays the orcid' do + expect(rendered).to have_link '000-000' end - context 'with Zotero enabled' do - before do - allow(Hyrax.config).to receive(:arkivo_api?) { true } - allow(user).to receive(:zotero_userid) { 'jdoe42zotero' } - render "hyrax/users/user_info", user: user - end + it 'displays a Zotero profile link' do + expect(rendered).to match(/Zotero Profile/) + end + + context 'with Zotero disabled' do + let(:arkivo_api) { false } - it 'displays a Zotero profile link' do - expect(rendered).to match(/Zotero Profile/) + it 'does not display a Zotero profile link' do + expect(rendered).not_to match(/Zotero Profile/) end end end diff --git a/tasks/hyrax_dev.rake b/tasks/hyrax_dev.rake index 64e4da5018..3253a0fb81 100644 --- a/tasks/hyrax_dev.rake +++ b/tasks/hyrax_dev.rake @@ -22,7 +22,9 @@ end desc "Sort locales keys in alphabetic order." task :i18n_sorter do require 'i18n_yaml_sorter' - locales = Dir.glob(File.expand_path('../../config/locales/**/*.yml', __FILE__)) + locales = Dir.glob(File.expand_path('../../config/locales/**/*.yml', __FILE__)) + + Dir.glob(File.expand_path('../../lib/generators/hyrax/templates/config/locales/**/*.yml', __FILE__)) + + Dir.glob(File.expand_path('../../lib/generators/hyrax/work/templates//locale.*.yml.erb', __FILE__)) locales.each do |locale_path| sorted_contents = File.open(locale_path) { |f| I18nYamlSorter::Sorter.new(f).sort } File.open(locale_path, 'w') { |f| f << sorted_contents }