Add orcid sub-property type to compound metadata#7484
Conversation
CompoundSchema now threads a sub-property's badge_for: declaration through into its normalized spec hash, alongside the existing type, authority, index_keys, etc. Consumers can read both type: orcid and the badge_for: sibling reference straight off the spec without per-field Ruby. The schema service stays the single source of truth for sub-property shape; later commits read this through to render and validate.
orcid_badge(value, name:) returns a small linked ORCID iD badge targeting https://orcid.org/<id>. It extracts the bare iD from either the bare form or the URL form via the existing Hyrax::OrcidValidator, and folds the sibling's name into the accessibility label so screen readers and tooltips say "ORCID iD for <name>". The helper carries explicit i18n defaults so it works before its locale keys are defined, keeping each commit self-contained.
CompoundAttributeRenderer now consumes a type: orcid sub-property whose badge_for: names a sibling that has a value in the same row, appending the linked ORCID badge inside the sibling's value markup and omitting the orcid as its own labeled row. An orcid with no badge_for: (or whose target is blank in that row) falls back to rendering standalone via the value_markup case. This matches Zenodo's "name [iD]" treatment exactly while leaving the existing sub-property dispatch and the surrounding compound markup untouched.
A new when 'orcid' case in the shared compound row partial renders a text input with the bare-iD placeholder, accepting either the 0000-0000-0000-0000 form or the full https://orcid.org/ URL. The subsequent validator normalizes either form on save. The else branch (string default) is unchanged; orcid plugs in through the same per-type dispatch as the other sub-property types.
Hyrax::OrcidSubpropertyValidator is a record-level validator that walks every compound row on a resource form, finds the type: orcid sub-properties off the schema, and rejects any non-blank value that does not match Hyrax::OrcidValidator::ORCID_REGEXP. Blank values are allowed (required-ness lives in CompoundEntryValidator). Errors land on :base with the same compound-naming convention CompoundEntryValidator uses, so work and collection forms render them cleanly. The validator is wired into ResourceForm alongside CompoundEntryValidator.
CompoundValidator now rejects type: orcid sub-properties that also declare authority: or values: (orcid is a free-text iD, not a controlled vocabulary), and any badge_for: that points at itself or at a property that is not a sibling sub-property of the same compound parent. A misconfigured m3 profile now fails at save with a clear message instead of producing a misrouted or unrenderable badge.
The shipped participants compound now declares a type: orcid sub-property bound to participant_name via badge_for:, so the sample demonstrates the inline-badge treatment out of the box. Mirrored in the non-flex schema (config/metadata/compound_metadata.yaml), the central m3 profile, and the koppie m3 profile — the three places PR #7479 ships participants. Dassie does not carry the participants sample and is left untouched.
The badge helper, validator messages, form placeholder, and the
m3 profile compound validator now read through Hyrax's existing
i18n keys. This commit adds the English copies:
- hyrax.compound_fields.orcid.{badge_alt,badge_aria,
invalid,placeholder}
- hyrax.flexible_schema_validators.compound_validator.errors.
{orcid_with_option_source,orcid_badge_for_self,
orcid_badge_for_unknown_sibling}
The participant_orcid sub-property label is intentionally left to
the humanized fallback ("Participant orcid"), matching the
treatment of the existing participant_title sub-property.
de, es, fr, it, pt-BR, and zh each pick up a compound_fields.orcid block with locale-appropriate translations for badge_alt, badge_aria, invalid, and placeholder. The non-English locales otherwise carry no compound_fields keys (PR #7479's add to those locales is still pending), so this commit adds the parent path alongside the orcid sub-keys. participant_orcid is intentionally not overridden; it humanizes to "Participant orcid" in every locale.
The compound_fields documentation now lists orcid as a supported sub-property type alongside string / url / work_or_url / controlled, and adds a worked example section explaining the badge_for: sibling binding, the show-page behavior, the standalone fallback, the m3 profile validator rules, and the validators on the persisted row.
orcid_badge previously called the bare image_tag helper. That runs against whichever self the method is invoked on; from a view it has the asset-pipeline context, but when included into CompoundAttributeRenderer (a non-view Ruby object) it falls back to a path that does not route through Sprockets and the badge image 404s on the work show page. ActionController::Base.helpers.image_tag returns a HelperProxy that carries a full view-equivalent context, so the asset URL resolves to /assets/orcid-<digest>.png regardless of caller. The helper spec now requires a rooted asset path so the regression is locked down.
A string sub-property can now declare `search_field: <solr_field>`; on show, CompoundAttributeRenderer wraps the value in a link to the catalog search filtered on that Solr field, matching the scalar `render_as: faceted` affordance for compound rows. The sample participants compound sets `search_field: participant_name_sim` on participant_name, so clicking the displayed name runs a search by that participant — the Zenodo treatment now lines up: the name links to a search and the inline ORCID badge links to the iD profile.
The m3 loader matches `available_on.class` by literal class name — it does not expand `Hyrax::Work` to subclasses. As a result, the sample participants compound was reaching CollectionResource and GenericWork in koppie, but not Monograph (or Hyrax::Work-based subclasses in the central profile). Adding the concrete work classes to participants.available_on so the sample compound (and its new orcid sub-property) reaches a work in flex mode the same way it already does in non-flex.
There was a problem hiding this comment.
Pull request overview
Adds first-class support for type: orcid (and related display/validation options) to Hyrax compound metadata, enabling inline ORCID iD badges and optional faceted linking for sibling string sub-properties.
Changes:
- Introduces
type: orcidsub-properties with save-time format validation and show-page ORCID badge rendering (optionally inline viabadge_for:). - Adds
search_field:for string sub-properties to render show-page values as facet-filtered catalog links. - Updates sample compound metadata, m3 profile configuration, docs, styling, locales, and adds/extends specs for schema normalization, profile validation, rendering, and helpers.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/validators/hyrax/orcid_subproperty_validator_spec.rb | Adds unit coverage for compound ORCID sub-property validation behavior. |
| spec/services/hyrax/flexible_schema_validators/compound_validator_spec.rb | Extends flexible profile validation specs for type: orcid + badge_for: rules. |
| spec/services/hyrax/compound_schema_spec.rb | Verifies schema normalization preserves badge_for and search_field. |
| spec/renderers/hyrax/renderers/compound_attribute_renderer_spec.rb | Adds rendering expectations for ORCID badges and search_field facet links. |
| spec/helpers/hyrax/compound_fields_helper_spec.rb | Tests orcid_badge helper output and accessibility/security attributes. |
| documentation/compound_fields.md | Documents type: orcid, badge_for:, and search_field: semantics. |
| config/metadata/compound_metadata.yaml | Wires ORCID + faceted participant name into the sample participants compound. |
| config/metadata_profiles/m3_profile.yaml | Updates shipped m3 profile sample to include ORCID + search_field and class availability. |
| config/locales/hyrax.zh.yml | Adds locale keys for ORCID badge/validation strings. |
| config/locales/hyrax.pt-BR.yml | Adds locale keys for ORCID badge/validation strings. |
| config/locales/hyrax.it.yml | Adds locale keys for ORCID badge/validation strings. |
| config/locales/hyrax.fr.yml | Adds locale keys for ORCID badge/validation strings. |
| config/locales/hyrax.es.yml | Adds locale keys for ORCID badge/validation strings. |
| config/locales/hyrax.en.yml | Adds English locale keys for ORCID badge/validation strings and related compound messaging. |
| config/locales/hyrax.de.yml | Adds locale keys for ORCID badge/validation strings. |
| app/views/hyrax/compounds/_compound_row.html.erb | Adds form input rendering for type: orcid with placeholder. |
| app/validators/hyrax/orcid_subproperty_validator.rb | Implements schema-driven validation of type: orcid sub-properties across compound rows. |
| app/services/hyrax/flexible_schema_validators/compound_validator.rb | Adds flexible-profile validation for type: orcid option constraints and badge_for sibling rules. |
| app/services/hyrax/compound_schema.rb | Extends normalized sub-property spec to include badge_for and search_field. |
| app/renderers/hyrax/renderers/compound_attribute_renderer.rb | Renders ORCID badges (inline or standalone) and search_field facet links on show pages. |
| app/helpers/hyrax/compound_fields_helper.rb | Adds orcid_badge helper to generate accessible, safe ORCID badge links. |
| app/forms/hyrax/forms/resource_form.rb | Hooks the new ORCID sub-property validator into resource form validation. |
| app/assets/stylesheets/hyrax/_compound_metadata.scss | Adds styling for the inline ORCID badge. |
| .koppie/config/metadata_profiles/m3_profile.yaml | Mirrors m3 profile sample changes for the Koppie configuration set. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| label: Participants | ||
| title: Title | ||
| participant_name: Name | ||
| participant_role: Role | ||
| orcid: |
There was a problem hiding this comment.
Holding on this one. It was intentional decision to NOT add a hyrax.compound_fields.participants.participant_orcid locale override — letting the value humanize to "Participant orcid" in the rare standalone-fallback case (when badge_for: is omitted or the sibling has no value). The common case displays the inline green badge with no row label. Happy to add the label if reviewers prefer the explicit "ORCID iD" copy.
The sample participants compound now declares four sub-properties (title, name, role, orcid), so the populator emits a four-key hash per row — including participant_orcid: nil when the user did not supply one. The validate + sync coverage in compound_metadata_form_spec was written against the three-key shape; update the expectations to include participant_orcid: nil so the assertions match the populator output again.
Previously the renderer attached the badge whenever the badge_for sibling had a value, even when the stored orcid value did not parse to a bare iD. In that case orcid_badge returned nil and the orcid sub-property was suppressed from its own row too, so the stored value silently disappeared from the show page. badge_attachments now requires Hyrax::OrcidValidator.extract_bare_orcid to succeed before consuming the orcid row, and value_markup falls back to escaped text when the badge cannot be built. Stored malformed values surface in a fallback row instead of vanishing.
The previous comment claimed Hyrax::OrcidSubpropertyValidator normalizes the value on save, but the validator only validates the format. The show renderer is what derives the bare iD via Hyrax::OrcidValidator at display time; the stored value is kept as the user typed it. Update the form-row comment to match.
The central m3 profile uses GenericWorkResource for every other available_on entry; the recently-added participants entry typed GenericWork by mistake, leaving the sample compound unreachable on the intended work class in flex mode.
hound flagged the new .hyrax-compound-orcid-badge img rule for declaring height, width, vertical-align out of alphabetical order. Reorder the trio.
Two style-only changes flagged on the inherited compound-metadata JS by houndci, addressed here as a courtesy so this PR's CI is clean: - Wrap the IIFE invocation inside the outer parens (`}())` instead of `})()`), matching the `wrap-iife` style rule. - Split the click handler into `handleRemoveClick` and `handleAddClick` and dispatch from a tiny outer listener, so no single function trips the cyclomatic-complexity rule. Behavior is unchanged: the same remove-row and add-row gestures fire the same DOM mutations, and the same `bindWorkOrUrlInputs` runs on any new row.
houndci flagged property ordering on the inherited compound metadata rules. Reorder the three blocks (.hyrax-compound-values, the subproperty group, the entry-divider rule) alphabetically so the warnings clear. The !important markers are intentional per the file header comment (they override _collections.scss's descendant flex/border styles), so they stay.
|
houndci feedback addressed:
Leaving alone:
( |
Test Results 17 files ± 0 17 suites ±0 3h 39m 13s ⏱️ - 5m 12s Results for commit 4d37dde. ± Comparison against base commit 20b843b. This pull request removes 449 and adds 485 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
dassie + sirenia run Rails 6.1, where `link_to ... do ... end` calls `capture(&block)` and requires `output_buffer=` on the receiver. The renderer that includes `Hyrax::CompoundFieldsHelper` is a plain Ruby object with no `output_buffer`, so the block form raises `NoMethodError: undefined method output_buffer=`. (Rails 7 silently tolerates the missing setter, which is why koppie and allinson were green.) Switch to `ActionController::Base.helpers.link_to(content, url, opts)` — a view-equivalent proxy that already carries `output_buffer`, used in the non-block form so no `capture` runs. `image_tag` already used the same proxy for asset path resolution, so the helper is now uniformly view-context-free.
Summary
ORCID iD sub-property type for compound metadata. The iD renders as an inline
green badge next to a sibling sub-property (matching Zenodo); the sibling's
value links to a participant-name facet search.
Depends on #7479 (compound metadata foundation).
Issue:
Details
type: orcidrecognized by the schema, form, show renderer, and m3profile validator. No per-field Ruby.
badge_for: <sibling>attaches the iD inline next to that sibling;falls back to a standalone labeled row when omitted or when the target is
blank in the row.
search_field:on string sub-properties wraps the show-page valuein a catalog facet link (compound analogue of
render_as: faceted).Hyrax::OrcidSubpropertyValidatorrejects malformed values on save; reusesthe existing
Hyrax::OrcidValidatorregex.participantscompound (name + ORCID, namefacet-linked).
HYRAX_FLEXIBLE=falseand=true.Testing
On a work or collection show page with a populated participant + ORCID:
Show page — work (HYRAX_FLEXIBLE=false, koppie)
Show page — work (HYRAX_FLEXIBLE=true, allinson)
Click the name → participant-name facet search
Click the badge → ORCID profile
🤖 Assisted by Claude Code