diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6ef83030dc4..002b4c06268 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,7 @@ jobs: tests: runs-on: ubuntu-latest env: + CI: true # Set at job level # The ci step will test the dspace-angular code against DSpace REST. # Direct that step to utilize a DSpace REST service that has been started in docker. # NOTE: These settings should be kept in sync with those in [src]/docker/docker-compose-ci.yml @@ -59,6 +60,7 @@ jobs: with: node-version: ${{ matrix.node-version }} + # If CHROME_VERSION env variable specified above, then pin to that version. # Otherwise, just install latest version of Chrome. - name: Install Chrome (for e2e tests) @@ -108,6 +110,8 @@ jobs: - name: Run specs (unit tests) run: yarn run test:headless + env: + CI: true # Using "docker compose" start backend using CI configuration @@ -118,6 +122,65 @@ jobs: docker compose -f ./docker/cli.yml -f ./docker/cli.assetstore.yml run --rm dspace-cli docker container ls + - name: Create Test Data + id: testdata + run: | + chmod +x scripts/create-test-data.sh + ./scripts/create-test-data.sh + env: + DSPACE_REST_URL: http://localhost:8080/server + DSPACE_ADMIN_EMAIL: dspacedemo+admin@gmail.com + DSPACE_ADMIN_PASS: dspace + + - name: Update Field Config with Collection Handles + run: | + echo "Updating item-field-config.ts with actual handles..." + + # Get all collection handles from test data output + JONES_HANDLE="${{ steps.testdata.outputs.jones_collection_handle }}" + RELICS_HANDLE="${{ steps.testdata.outputs.relics_collection_handle }}" + LAEFER_HANDLE="${{ steps.testdata.outputs.laefer_collection_handle }}" + TANDON_HANDLE="${{ steps.testdata.outputs.tandon_collection_handle }}" + TANDONCAPSTONE_HANDLE="${{ steps.testdata.outputs.tandoncapstone_collection_handle }}" + DNP_HANDLE="${{ steps.testdata.outputs.dnp_collection_handle }}" + CALABASH_HANDLE="${{ steps.testdata.outputs.calabash_collection_handle }}" + OPENSCHOLARSHIP_HANDLE="${{ steps.testdata.outputs.openscholarship_collection_handle }}" + SYLLABI_HANDLE="${{ steps.testdata.outputs.syllabi_collection_handle }}" + + echo "Jones Handle: $JONES_HANDLE" + echo "Relics Handle: $RELICS_HANDLE" + + # Update the config file with actual handles + sed -i "s|handles: \['JONES_HANDLE'\]|handles: ['$JONES_HANDLE']|g" \ + src/themes/fda/app/item-page/field-config/item-field-config.ts + + sed -i "s|handles: \['RELICS_HANDLE'\]|handles: ['$RELICS_HANDLE']|g" \ + src/themes/fda/app/item-page/field-config/item-field-config.ts + + sed -i "s|handles: \['LAEFER_HANDLE'\]|handles: ['$LAEFER_HANDLE']|g" \ + src/themes/fda/app/item-page/field-config/item-field-config.ts + + sed -i "s|handles: \['TANDON_HANDLE'\]|handles: ['$TANDON_HANDLE']|g" \ + src/themes/fda/app/item-page/field-config/item-field-config.ts + + sed -i "s|handles: \['TANDONCAPSTONE_HANDLE'\]|handles: ['$TANDONCAPSTONE_HANDLE']|g" \ + src/themes/fda/app/item-page/field-config/item-field-config.ts + + sed -i "s|handles: \['DNP_HANDLE'\]|handles: ['$DNP_HANDLE']|g" \ + src/themes/fda/app/item-page/field-config/item-field-config.ts + + sed -i "s|handles: \['CALABASH_HANDLE'\]|handles: ['$CALABASH_HANDLE']|g" \ + src/themes/fda/app/item-page/field-config/item-field-config.ts + + sed -i "s|handles: \['OPENSCHOLARSHIP_HANDLE'\]|handles: ['$OPENSCHOLARSHIP_HANDLE']|g" \ + src/themes/fda/app/item-page/field-config/item-field-config.ts + + sed -i "s|handles: \['SYLLABI_HANDLE'\]|handles: ['$SYLLABI_HANDLE']|g" \ + src/themes/fda/app/item-page/field-config/item-field-config.ts + + echo "Updated config file:" + grep -A2 "handles:" src/themes/fda/app/item-page/field-config/item-field-config.ts || true + # Run integration tests via Cypress.io # https://github.com/cypress-io/github-action # (NOTE: to run these e2e tests locally, just use 'ng e2e') @@ -133,15 +196,20 @@ jobs: wait-on: http://127.0.0.1:8080/server/api/core/sites, http://127.0.0.1:4000 # Wait for 2 mins max for everything to respond wait-on-timeout: 120 - - # Cypress always creates a video of all e2e tests (whether they succeeded or failed) - # Save those in an Artifact - - name: Upload e2e test videos to Artifacts - uses: actions/upload-artifact@v4 - if: always() - with: - name: e2e-test-videos-${{ matrix.node-version }} - path: cypress/videos + # Don't reinstall dependencies + install: false + env: + # Pass test data UUIDs to Cypress + CYPRESS_DSPACE_TEST_FDA_DEFAULT_ITEM: ${{ steps.testdata.outputs.default_item_uuid }} + CYPRESS_DSPACE_TEST_JONES_ITEM: ${{ steps.testdata.outputs.jones_item_uuid }} + CYPRESS_DSPACE_TEST_RELICS_ITEM: ${{ steps.testdata.outputs.relics_item_uuid }} + CYPRESS_DSPACE_TEST_LAEFER_ITEM: ${{ steps.testdata.outputs.laefer_item_uuid }} + CYPRESS_DSPACE_TEST_TANDON_ITEM: ${{ steps.testdata.outputs.tandon_item_uuid }} + CYPRESS_DSPACE_TEST_TANDONCAPSTONE_ITEM: ${{ steps.testdata.outputs.tandoncapstone_item_uuid }} + CYPRESS_DSPACE_TEST_DNP_ITEM: ${{ steps.testdata.outputs.dnp_item_uuid }} + CYPRESS_DSPACE_TEST_CALABASH_ITEM: ${{ steps.testdata.outputs.calabash_item_uuid }} + CYPRESS_DSPACE_TEST_OPENSCHOLARSHIP_ITEM: ${{ steps.testdata.outputs.openscholarship_item_uuid }} + CYPRESS_DSPACE_TEST_SYLLABI_ITEM: ${{ steps.testdata.outputs.syllabi_item_uuid }} # If e2e tests fail, Cypress creates a screenshot of what happened # Save those in an Artifact diff --git a/.github/workflows/codescan.yml b/.github/workflows/codescan.yml index d96e786cc37..1e16f8fcf86 100644 --- a/.github/workflows/codescan.yml +++ b/.github/workflows/codescan.yml @@ -50,4 +50,4 @@ jobs: # Perform GitHub Code Scanning. - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 \ No newline at end of file + uses: github/codeql-action/analyze@v2 diff --git a/angular.json b/angular.json index f7711638b07..e417c29e533 100644 --- a/angular.json +++ b/angular.json @@ -63,11 +63,6 @@ "input": "src/themes/fda/styles/theme.scss", "inject": false, "bundleName": "fda-theme" - }, - { - "input": "src/themes/calabash/styles/theme.scss", - "inject": false, - "bundleName": "calabash-theme" } ], "scripts": [], diff --git a/config/config.dev.yml b/config/config.dev.yml index 0272227083c..67f73715bc1 100644 --- a/config/config.dev.yml +++ b/config/config.dev.yml @@ -439,9 +439,6 @@ themes: extends: fda handle: 2451/34841 - - name: calabash - extends: fda - handle: 2451/62242 - name: fda headTags: diff --git a/config/config.yml b/config/config.yml index 109db60ca92..a2e5bf8f1aa 100644 --- a/config/config.yml +++ b/config/config.yml @@ -3,3 +3,17 @@ rest: host: sandbox.dspace.org port: 443 nameSpace: /server + +themes: + - name: gallatin-syllabi + extends: fda + handle: 2451/34841 + + + - name: fda + headTags: + - tagName: link + attributes: + rel: icon + href: https://cdn.library.nyu.edu/images/favicon.ico # Path to your favicon file + sizes: any diff --git a/cypress.config.ts b/cypress.config.ts index 36d8120342a..83ac50c5a57 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -41,6 +41,22 @@ export default defineConfig({ e2e: { // Setup our plugins for e2e tests setupNodeEvents(on, config) { + // Configure Chrome launch args FIRST + on('before:browser:launch', (browser, launchOptions) => { + if (browser.name === 'chrome' || browser.family === 'chromium') { + launchOptions.args.push('--no-sandbox'); + launchOptions.args.push('--disable-setuid-sandbox'); + launchOptions.args.push('--disable-dev-shm-usage'); + launchOptions.args.push('--disable-gpu'); + + if (process.env.CI) { + launchOptions.args.push('--disable-software-rasterizer'); + launchOptions.args.push('--disable-extensions'); + } + } + + return launchOptions; + }); return require('./cypress/plugins/index.ts')(on, config); }, // This is the base URL that Cypress will run all tests against diff --git a/cypress/e2e/header.cy.ts b/cypress/e2e/header.cy.ts index aa65aee570e..0b69f069388 100644 --- a/cypress/e2e/header.cy.ts +++ b/cypress/e2e/header.cy.ts @@ -11,7 +11,7 @@ describe('Header', () => { testA11y('ds-header'); }); - it('should allow for changing language to German (for example)', () => { + it.skip('skip as we do not allow for changing language to German (for example)', () => { cy.visit('/'); // Click the language switcher (globe) in header diff --git a/cypress/e2e/item-page-collection-config.cy.ts b/cypress/e2e/item-page-collection-config.cy.ts new file mode 100644 index 00000000000..5e745c6a2a7 --- /dev/null +++ b/cypress/e2e/item-page-collection-config.cy.ts @@ -0,0 +1,408 @@ +// cypress/e2e/item-page-collection-config.cy.ts + +import { testA11y } from 'cypress/support/utils'; + +describe('Item Page Collection Configuration', () => { + + // Test item UUIDs from environment + const DEFAULT_ITEM = '/items/'.concat(Cypress.env('DSPACE_TEST_FDA_DEFAULT_ITEM')); + const JONES_ITEM = '/items/'.concat(Cypress.env('DSPACE_TEST_FDA_JONES_ITEM') || 'skip'); + const RELICS_ITEM = '/items/'.concat(Cypress.env('DSPACE_TEST_FDA_RELICS_ITEM') || 'skip'); + const LAEFER_ITEM = '/items/'.concat(Cypress.env('DSPACE_TEST_FDA_LAEFER_ITEM') || 'skip'); + const TANDON_ITEM = '/items/'.concat(Cypress.env('DSPACE_TEST_FDA_TANDON_ITEM') || 'skip'); + const TANDONCAPSTONE_ITEM = '/items/'.concat(Cypress.env('DSPACE_TEST_FDA_TANDONCAPSTONE_ITEM') || 'skip'); + const DNP_ITEM = '/items/'.concat(Cypress.env('DSPACE_TEST_FDA_DNP_ITEM') || 'skip'); + const CALABASH_ITEM = '/items/'.concat(Cypress.env('DSPACE_TEST_FDA_CALABASH_ITEM') || 'skip'); + const OPENSCHOLARSHIP_ITEM = '/items/'.concat(Cypress.env('DSPACE_TEST_FDA_OPENSCHOLARSHIP_ITEM') || 'skip'); + const SYLLABI_ITEM = '/items/'.concat(Cypress.env('DSPACE_TEST_FDA_SYLLABI_ITEM') || 'skip'); + + describe('Default Collection Item', () => { + beforeEach(() => { + cy.visit(DEFAULT_ITEM); + }); + + it('should load the item page', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + }); + + it('should display default fields', () => { + cy.get('.itemDisplayTable').should('be.visible'); + cy.get('th.metadataFieldLabel, td.metadataFieldLabel, .itemDisplayTable th').should('have.length.greaterThan', 0); + }); + + it('should display title as h1', () => { + cy.get('h1.page-title, h1').should('be.visible'); + }); + + it('should have authors as links separated by semicolons', () => { + cy.get('.itemDisplayTable').then($table => { + const html = $table.html(); + // Check for various author field labels + if (html.includes('Author') || html.includes('Creator') || html.includes('Contributor')) { + cy.get('.itemDisplayTable').contains(/Author|Creator|Contributor/i).parents('tr').within(() => { + cy.get('a').should('exist'); // Authors should be links + }); + } else { + cy.log('No author field found - skipping test'); + this.skip(); + } + }); + }); + + it('should display files section', () => { + // Check for multiple possible file section selectors + cy.get('body').then($body => { + const hasFiles = + $body.find('.panel-info').length > 0 || + $body.find('.file-section').length > 0 || + $body.find('ds-item-page-file-section').length > 0 || + $body.find('[class*="file"]').length > 0; + + if (hasFiles) { + cy.get('.panel-info, .file-section, ds-item-page-file-section, [class*="file"]').should('exist'); + } else { + cy.log('No files section found - item may not have files'); + } + }); + }); + + it('should have working full item link', () => { + cy.get('body').then($body => { + if ($body.find('a[href*="/full"]').length > 0) { + cy.get('a[href*="/full"]').first().click(); + cy.url().should('include', '/full'); + } else { + cy.log('No full item link found - skipping'); + } + }); + }); + + it('should pass accessibility tests', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + testA11y('.item-page-content'); + }); + }); + + // Jones Collection Tests + describe('Jones Collection Item', () => { + beforeEach(function() { + if (!Cypress.env('DSPACE_TEST_FDA_JONES_ITEM')) { + this.skip(); + } + cy.visit(JONES_ITEM); + }); + + it('should load with Jones configuration', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + cy.get('.itemDisplayTable').should('be.visible'); + }); + + it('should display Jones-specific labels', () => { + cy.get('.itemDisplayTable').then($table => { + const text = $table.text(); + const hasJonesLabels = + text.includes('Date of digital object') || + text.includes('Date of object depicted') || + text.includes('Technical designation') || + text.includes('Technical specifications'); + + if (hasJonesLabels) { + cy.log('Jones-specific labels found'); + const result = expect(hasJonesLabels).to.be.true; + cy.wrap(result); + } else { + cy.log('No Jones-specific labels found'); + } + }); + }); + + it('should pass accessibility tests', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + testA11y('.item-page-content'); + }); + }); + + // Relics Collection Tests + describe('Relics Collection Item', () => { + beforeEach(function() { + if (!Cypress.env('DSPACE_TEST_FDA_RELICS_ITEM')) { + this.skip(); + } + cy.visit(RELICS_ITEM); + }); + + it('should load with Relics configuration', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + cy.get('.itemDisplayTable').should('be.visible'); + }); + + it('should display Relics-specific fields', () => { + cy.get('.itemDisplayTable').then($table => { + const text = $table.text(); + const hasRelicsFields = + text.includes('Country') || + text.includes('Source'); + + if (hasRelicsFields) { + cy.log('Relics-specific fields found'); + const result = expect(hasRelicsFields).to.be.true; + cy.wrap(result); + } + }); + }); + + it('should pass accessibility tests', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + testA11y('.item-page-content'); + }); + }); + + // Laefer Collection Tests + describe('Laefer Collection Item', () => { + beforeEach(function() { + if (!Cypress.env('DSPACE_TEST_FDA_LAEFER_ITEM')) { + this.skip(); + } + cy.visit(LAEFER_ITEM); + }); + + it('should load with Laefer configuration', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + cy.get('.itemDisplayTable').should('be.visible'); + }); + + it('should pass accessibility tests', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + testA11y('.item-page-content'); + }); + }); + + // Tandon Collection Tests + describe('Tandon Collection Item', () => { + beforeEach(function() { + if (!Cypress.env('DSPACE_TEST_FDA_TANDON_ITEM')) { + this.skip(); + } + cy.visit(TANDON_ITEM); + }); + + it('should load with Tandon configuration', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + cy.get('.itemDisplayTable').should('be.visible'); + }); + + it('should display page number fields', () => { + cy.get('.itemDisplayTable').then($table => { + const text = $table.text(); + const hasPageFields = + text.includes('First Page') || + text.includes('Last Page'); + + if (hasPageFields) { + cy.log('Page number fields found'); + } + }); + }); + + it('should pass accessibility tests', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + testA11y('.item-page-content'); + }); + }); + + // Tandon Capstone Collection Tests + describe('Tandon Capstone Collection Item', () => { + beforeEach(function() { + if (!Cypress.env('DSPACE_TEST_FDA_TANDONCAPSTONE_ITEM')) { + this.skip(); + } + cy.visit(TANDONCAPSTONE_ITEM); + }); + + it('should load with Tandon Capstone configuration', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + cy.get('.itemDisplayTable').should('be.visible'); + }); + + it('should display advisor field', () => { + cy.get('.itemDisplayTable').then($table => { + const text = $table.text(); + if (text.includes('Capstone Professor') || text.includes('Advisor')) { + cy.log('Advisor field found'); + } + }); + }); + + it('should pass accessibility tests', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + testA11y('.item-page-content'); + }); + }); + + // DNP Collection Tests + describe('DNP Collection Item', () => { + beforeEach(function() { + if (!Cypress.env('DSPACE_TEST_FDA_DNP_ITEM')) { + this.skip(); + } + cy.visit(DNP_ITEM); + }); + + it('should load with DNP configuration', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + cy.get('.itemDisplayTable').should('be.visible'); + }); + + it('should display thesis-specific fields', () => { + cy.get('.itemDisplayTable').then($table => { + const text = $table.text(); + const hasThesisFields = + text.includes('Degree') || + text.includes('Grantor') || + text.includes('MeSH term') || + text.includes('DNP Project Team Member'); + + if (hasThesisFields) { + cy.log('Thesis-specific fields found'); + } + }); + }); + + it('should pass accessibility tests', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + testA11y('.item-page-content'); + }); + }); + + // Calabash Collection Tests + describe('Calabash Collection Item', () => { + beforeEach(function() { + if (!Cypress.env('DSPACE_TEST_FDA_CALABASH_ITEM')) { + this.skip(); + } + cy.visit(CALABASH_ITEM); + }); + + it('should load with Calabash configuration', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + cy.get('.itemDisplayTable').should('be.visible'); + }); + + it('should display journal-specific fields', () => { + cy.get('.itemDisplayTable').then($table => { + const text = $table.text(); + const hasJournalFields = + text.includes('Journal Title') || + text.includes('Volume') || + text.includes('Issue') || + text.includes('Translator'); + + if (hasJournalFields) { + cy.log('Journal-specific fields found'); + } + }); + }); + + it('should pass accessibility tests', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + testA11y('.item-page-content'); + }); + }); + + // OpenScholarship Collection Tests + describe('OpenScholarship Collection Item', () => { + beforeEach(function() { + if (!Cypress.env('DSPACE_TEST_FDA_OPENSCHOLARSHIP_ITEM')) { + this.skip(); + } + cy.visit(OPENSCHOLARSHIP_ITEM); + }); + + it('should load with OpenScholarship configuration', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + cy.get('.itemDisplayTable').should('be.visible'); + }); + + it('should display sponsorship field', () => { + cy.get('.itemDisplayTable').then($table => { + const text = $table.text(); + if (text.includes('Sponsorship')) { + cy.log('Sponsorship field found'); + } + }); + }); + + it('should pass accessibility tests', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + testA11y('.item-page-content'); + }); + }); + + // Syllabi Collection Tests + describe('Syllabi Collection Item', () => { + beforeEach(function() { + if (!Cypress.env('DSPACE_TEST_FDA_SYLLABI_ITEM')) { + this.skip(); + } + cy.visit(SYLLABI_ITEM); + }); + + it('should load with Syllabi configuration', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + cy.get('.itemDisplayTable').should('be.visible'); + }); + + it('should display syllabi-specific fields', () => { + cy.get('.itemDisplayTable').then($table => { + const text = $table.text(); + const hasSyllabiFields = + text.includes('Instructor') || + text.includes('Course Number') || + text.includes('Term'); + + if (hasSyllabiFields) { + cy.log('Syllabi-specific fields found'); + } + }); + }); + + it('should pass accessibility tests', () => { + cy.get('.item-page-content', { timeout: 10000 }).should('be.visible'); + testA11y('.item-page-content'); + }); + }); + + // Common tests + describe('Common Item Page Features', () => { + + it('should have correct table structure', () => { + cy.visit(DEFAULT_ITEM); + cy.get('.itemDisplayTable tbody tr, .itemDisplayTable tr').should('have.length.greaterThan', 0); + }); + + it('should display collections link', () => { + cy.visit(DEFAULT_ITEM); + cy.get('.itemDisplayTable').then($table => { + const text = $table.text(); + if (text.includes('Collection') || text.includes('Part of')) { + cy.log('Collections field found'); + } + }); + }); + + it('should display copyright notice', () => { + cy.visit(DEFAULT_ITEM); + cy.get('footer').should('be.visible'); + }); + + it('should have accessible links', () => { + cy.visit(DEFAULT_ITEM); + cy.get('a').each(($link) => { + const text = $link.text().trim(); + const ariaLabel = $link.attr('aria-label'); + const hasAccessibleText = text.length > 0 || !!ariaLabel; + cy.wrap(hasAccessibleText, 'Link should have text or aria-label').should('be.true'); + }); + }); + }); +}); diff --git a/karma.conf.js b/karma.conf.js index f96558bfaff..27490d33e2b 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,6 +1,8 @@ // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html +// karma.conf.js + module.exports = function (config) { config.set({ basePath: '', @@ -35,7 +37,29 @@ module.exports = function (config) { logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], + + // Override ChromeHeadless to always include --no-sandbox + customLaunchers: { + ChromeHeadlessNoSandBox: { + base: 'ChromeHeadless', + flags: [ + '--no-sandbox', + '--disable-setuid-sandbox', + '--disable-gpu', + '--disable-dev-shm-usage', + '--disable-software-rasterizer', + '--disable-extensions' + ] + } + }, + singleRun: false, - restartOnFileChange: true + restartOnFileChange: true, + + // Increase timeouts + browserNoActivityTimeout: 60000, + browserDisconnectTimeout: 10000, + browserDisconnectTolerance: 3, + captureTimeout: 210000 }); }; diff --git a/package.json b/package.json index 997a8bb85df..085c7a9ad84 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "build:lint": "rimraf 'lint/dist/**/*.js' 'lint/dist/**/*.js.map' && tsc -b lint/tsconfig.json", "test": "ng test --source-map=true --watch=false --configuration test", "test:watch": "nodemon --exec \"ng test --source-map=true --watch=true --configuration test\"", - "test:headless": "ng test --source-map=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage", + "test:headless": "ng test --source-map=true --watch=false --configuration test --browsers=ChromeHeadlessNoSandBox --code-coverage", "test:lint": "yarn build:lint && yarn test:lint:nobuild", "test:lint:nobuild": "jasmine --config=lint/jasmine.json", "lint": "yarn build:lint && yarn lint:nobuild", diff --git a/scripts/create-test-data.sh b/scripts/create-test-data.sh new file mode 100755 index 00000000000..f9a23ccafb8 --- /dev/null +++ b/scripts/create-test-data.sh @@ -0,0 +1,312 @@ +#!/bin/bash +# scripts/create-test-data.sh + +set -e + +DSPACE_REST_URL=${DSPACE_REST_URL:-http://localhost:8080/server} +DSPACE_ADMIN_EMAIL=${DSPACE_ADMIN_EMAIL:-dspacedemo+admin@gmail.com} +DSPACE_ADMIN_PASS=${DSPACE_ADMIN_PASS:-dspace} + +echo "DSpace REST API: $DSPACE_REST_URL" + +# Wait for DSpace to be ready +echo "Waiting for DSpace API..." +max_attempts=30 +attempt=0 +while [ $attempt -lt $max_attempts ]; do + if curl -s "${DSPACE_REST_URL}/api" > /dev/null 2>&1; then + echo "DSpace is ready!" + break + fi + attempt=$((attempt + 1)) + echo "Attempt $attempt/$max_attempts..." + sleep 10 +done + +# Clean up old cookies +rm -f /tmp/dspace-cookies.txt + +# Login +echo "Authenticating..." + +curl -s "${DSPACE_REST_URL}/api/authn/status" \ + -c /tmp/dspace-cookies.txt \ + > /dev/null + +CSRF_TOKEN=$(grep "DSPACE-XSRF-COOKIE" /tmp/dspace-cookies.txt | awk '{print $NF}') + +if [ -z "$CSRF_TOKEN" ]; then + echo "❌ Failed to get CSRF token" + exit 1 +fi + +echo "Logging in as $DSPACE_ADMIN_EMAIL..." + +LOGIN_RESPONSE=$(curl -s -i -X POST "${DSPACE_REST_URL}/api/authn/login" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -H "X-XSRF-TOKEN: ${CSRF_TOKEN}" \ + -b /tmp/dspace-cookies.txt \ + -c /tmp/dspace-cookies.txt \ + --data-urlencode "user=${DSPACE_ADMIN_EMAIL}" \ + --data-urlencode "password=${DSPACE_ADMIN_PASS}") + +if ! echo "$LOGIN_RESPONSE" | grep -q "HTTP/1.1 200"; then + echo "❌ Login failed" + exit 1 +fi + +AUTH_TOKEN=$(echo "$LOGIN_RESPONSE" | grep -i "^Authorization:" | sed 's/Authorization: //' | tr -d '\r\n') + +if [ -z "$AUTH_TOKEN" ]; then + echo "❌ Failed to get authorization token" + exit 1 +fi + +echo "✅ Authenticated" + +# Function to get current CSRF token +get_current_csrf() { + grep "DSPACE-XSRF-COOKIE" /tmp/dspace-cookies.txt | awk '{print $NF}' +} + +# Function to make authenticated API requests +api_request() { + local method=$1 + local endpoint=$2 + local data=$3 + + local csrf=$(get_current_csrf) + + if [ -z "$csrf" ]; then + echo "❌ No CSRF token" >&2 + return 1 + fi + + local temp_file=$(mktemp) + local http_code + + if [ -n "$data" ]; then + http_code=$(curl -s -w "%{http_code}" -o "$temp_file" \ + -X "$method" "${DSPACE_REST_URL}${endpoint}" \ + -H "Content-Type: application/json" \ + -H "Authorization: ${AUTH_TOKEN}" \ + -H "X-XSRF-TOKEN: ${csrf}" \ + -b /tmp/dspace-cookies.txt \ + -c /tmp/dspace-cookies.txt \ + -d "$data") + else + http_code=$(curl -s -w "%{http_code}" -o "$temp_file" \ + -X "$method" "${DSPACE_REST_URL}${endpoint}" \ + -H "Authorization: ${AUTH_TOKEN}" \ + -H "X-XSRF-TOKEN: ${csrf}" \ + -b /tmp/dspace-cookies.txt \ + -c /tmp/dspace-cookies.txt) + fi + + local response=$(cat "$temp_file") + rm "$temp_file" + + if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then + echo "$response" + return 0 + else + echo "❌ HTTP $http_code: $response" >&2 + return 1 + fi +} + +# Create test community +echo "" +echo "=== Creating Test Community ===" + +COMMUNITY_RESPONSE=$(api_request POST "/api/core/communities" '{ + "name": "Test Community", + "metadata": { + "dc.title": [{"value": "Test Community for FDA Theme"}] + } +}') + +if [ $? -ne 0 ]; then + exit 1 +fi + +COMMUNITY_UUID=$(echo "$COMMUNITY_RESPONSE" | jq -r '.uuid') +COMMUNITY_HANDLE=$(echo "$COMMUNITY_RESPONSE" | jq -r '.handle') + +if [ -z "$COMMUNITY_UUID" ] || [ "$COMMUNITY_UUID" = "null" ]; then + echo "❌ Failed to extract community UUID" + exit 1 +fi + +echo "✅ Community created (UUID: $COMMUNITY_UUID, Handle: $COMMUNITY_HANDLE)" + +# Function to create a collection - FIXED to send messages to stderr +create_collection() { + local name=$1 + local community_uuid=$2 + + echo "Creating $name..." >&2 # Send to stderr + + local response=$(api_request POST "/api/core/collections?parent=${community_uuid}" '{ + "name": "'"$name"'", + "metadata": { + "dc.title": [{"value": "'"$name"'"}] + } + }') + + if [ $? -ne 0 ]; then + return 1 + fi + + local uuid=$(echo "$response" | jq -r '.uuid') + local handle=$(echo "$response" | jq -r '.handle') + + if [ -z "$uuid" ] || [ "$uuid" = "null" ]; then + echo "❌ Failed to extract UUID" >&2 + return 1 + fi + + echo "✅ $name created (UUID: $uuid, Handle: $handle)" >&2 # Send to stderr + + # Only output the data to stdout (no extra text) + echo "$uuid|$handle" +} + +# Create collections +echo "" +echo "=== Creating Collections ===" + +DEFAULT_RESULT=$(create_collection "Default Test Collection" "$COMMUNITY_UUID") +DEFAULT_COLLECTION_UUID=$(echo "$DEFAULT_RESULT" | cut -d'|' -f1) +DEFAULT_COLLECTION_HANDLE=$(echo "$DEFAULT_RESULT" | cut -d'|' -f2) + +JONES_RESULT=$(create_collection "Jones Test Collection" "$COMMUNITY_UUID") +JONES_COLLECTION_UUID=$(echo "$JONES_RESULT" | cut -d'|' -f1) +JONES_COLLECTION_HANDLE=$(echo "$JONES_RESULT" | cut -d'|' -f2) + +RELICS_RESULT=$(create_collection "Relics Test Collection" "$COMMUNITY_UUID") +RELICS_COLLECTION_UUID=$(echo "$RELICS_RESULT" | cut -d'|' -f1) +RELICS_COLLECTION_HANDLE=$(echo "$RELICS_RESULT" | cut -d'|' -f2) + +LAEFER_RESULT=$(create_collection "Laefer Test Collection" "$COMMUNITY_UUID") +LAEFER_COLLECTION_UUID=$(echo "$LAEFER_RESULT" | cut -d'|' -f1) +LAEFER_COLLECTION_HANDLE=$(echo "$LAEFER_RESULT" | cut -d'|' -f2) + +TANDON_RESULT=$(create_collection "Tandon Test Collection" "$COMMUNITY_UUID") +TANDON_COLLECTION_UUID=$(echo "$TANDON_RESULT" | cut -d'|' -f1) +TANDON_COLLECTION_HANDLE=$(echo "$TANDON_RESULT" | cut -d'|' -f2) + +TANDONCAPSTONE_RESULT=$(create_collection "Tandon Capstone Test Collection" "$COMMUNITY_UUID") +TANDONCAPSTONE_COLLECTION_UUID=$(echo "$TANDONCAPSTONE_RESULT" | cut -d'|' -f1) +TANDONCAPSTONE_COLLECTION_HANDLE=$(echo "$TANDONCAPSTONE_RESULT" | cut -d'|' -f2) + +DNP_RESULT=$(create_collection "DNP Test Collection" "$COMMUNITY_UUID") +DNP_COLLECTION_UUID=$(echo "$DNP_RESULT" | cut -d'|' -f1) +DNP_COLLECTION_HANDLE=$(echo "$DNP_RESULT" | cut -d'|' -f2) + +CALABASH_RESULT=$(create_collection "Calabash Test Collection" "$COMMUNITY_UUID") +CALABASH_COLLECTION_UUID=$(echo "$CALABASH_RESULT" | cut -d'|' -f1) +CALABASH_COLLECTION_HANDLE=$(echo "$CALABASH_RESULT" | cut -d'|' -f2) + +OPENSCHOLARSHIP_RESULT=$(create_collection "OpenScholarship Test Collection" "$COMMUNITY_UUID") +OPENSCHOLARSHIP_COLLECTION_UUID=$(echo "$OPENSCHOLARSHIP_RESULT" | cut -d'|' -f1) +OPENSCHOLARSHIP_COLLECTION_HANDLE=$(echo "$OPENSCHOLARSHIP_RESULT" | cut -d'|' -f2) + +SYLLABI_RESULT=$(create_collection "Syllabi Test Collection" "$COMMUNITY_UUID") +SYLLABI_COLLECTION_UUID=$(echo "$SYLLABI_RESULT" | cut -d'|' -f1) +SYLLABI_COLLECTION_HANDLE=$(echo "$SYLLABI_RESULT" | cut -d'|' -f2) + +# Function to create an item - FIXED to send messages to stderr +create_item() { + local collection_uuid=$1 + local title=$2 + local author=$3 + + echo "Creating $title..." >&2 # Send to stderr + + local response=$(api_request POST "/api/core/items?owningCollection=${collection_uuid}" '{ + "name": "'"$title"'", + "metadata": { + "dc.title": [{"value": "'"$title"'"}], + "dc.contributor.author": [{"value": "'"$author"'"}], + "dc.description.abstract": [{"value": "Test abstract"}] + }, + "inArchive": true, + "discoverable": true + }') + + if [ $? -ne 0 ]; then + return 1 + fi + + local uuid=$(echo "$response" | jq -r '.uuid') + + if [ -z "$uuid" ] || [ "$uuid" = "null" ]; then + echo "❌ Failed to extract UUID" >&2 + return 1 + fi + + echo "✅ $title created (UUID: $uuid)" >&2 # Send to stderr + + # Only output UUID to stdout (no extra text) + echo "$uuid" +} + +# Create items +echo "" +echo "=== Creating Test Items ===" + +DEFAULT_ITEM_UUID=$(create_item "$DEFAULT_COLLECTION_UUID" "Default Test Publication" "Test Author") +JONES_ITEM_UUID=$(create_item "$JONES_COLLECTION_UUID" "Jones Test Item" "Jones Author") +RELICS_ITEM_UUID=$(create_item "$RELICS_COLLECTION_UUID" "Relics Test Item" "Relics Author") +LAEFER_ITEM_UUID=$(create_item "$LAEFER_COLLECTION_UUID" "Laefer Test Publication" "Laefer Author") +TANDON_ITEM_UUID=$(create_item "$TANDON_COLLECTION_UUID" "Tandon Test Article" "Tandon Author") +TANDONCAPSTONE_ITEM_UUID=$(create_item "$TANDONCAPSTONE_COLLECTION_UUID" "Tandon Capstone Project" "Capstone Student") +DNP_ITEM_UUID=$(create_item "$DNP_COLLECTION_UUID" "DNP Test Thesis" "DNP Student") +CALABASH_ITEM_UUID=$(create_item "$CALABASH_COLLECTION_UUID" "Calabash Test Article" "Calabash Author") +OPENSCHOLARSHIP_ITEM_UUID=$(create_item "$OPENSCHOLARSHIP_COLLECTION_UUID" "OpenScholarship Test Paper" "Scholar") +SYLLABI_ITEM_UUID=$(create_item "$SYLLABI_COLLECTION_UUID" "Test Syllabus" "Test Instructor") + +# Create cypress.env.json - USE DIFFERENT VARIABLE NAMES +cat > cypress.env.json << EOF +{ + "DSPACE_TEST_FDA_DEFAULT_ITEM": "$DEFAULT_ITEM_UUID", + "DSPACE_TEST_FDA_JONES_ITEM": "$JONES_ITEM_UUID", + "DSPACE_TEST_FDA_RELICS_ITEM": "$RELICS_ITEM_UUID", + "DSPACE_TEST_FDA_LAEFER_ITEM": "$LAEFER_ITEM_UUID", + "DSPACE_TEST_FDA_TANDON_ITEM": "$TANDON_ITEM_UUID", + "DSPACE_TEST_FDA_TANDONCAPSTONE_ITEM": "$TANDONCAPSTONE_ITEM_UUID", + "DSPACE_TEST_FDA_DNP_ITEM": "$DNP_ITEM_UUID", + "DSPACE_TEST_FDA_CALABASH_ITEM": "$CALABASH_ITEM_UUID", + "DSPACE_TEST_FDA_OPENSCHOLARSHIP_ITEM": "$OPENSCHOLARSHIP_ITEM_UUID", + "DSPACE_TEST_FDA_SYLLABI_ITEM": "$SYLLABI_ITEM_UUID" +} +EOF + +echo "" +echo "✅ Test data created successfully!" + +# Export for GitHub Actions - CLEAN FORMAT +if [ -n "$GITHUB_OUTPUT" ]; then + { + echo "default_item_uuid=$DEFAULT_ITEM_UUID" + echo "jones_item_uuid=$JONES_ITEM_UUID" + echo "relics_item_uuid=$RELICS_ITEM_UUID" + echo "laefer_item_uuid=$LAEFER_ITEM_UUID" + echo "tandon_item_uuid=$TANDON_ITEM_UUID" + echo "tandoncapstone_item_uuid=$TANDONCAPSTONE_ITEM_UUID" + echo "dnp_item_uuid=$DNP_ITEM_UUID" + echo "calabash_item_uuid=$CALABASH_ITEM_UUID" + echo "openscholarship_item_uuid=$OPENSCHOLARSHIP_ITEM_UUID" + echo "syllabi_item_uuid=$SYLLABI_ITEM_UUID" + echo "jones_collection_handle=$JONES_COLLECTION_HANDLE" + echo "relics_collection_handle=$RELICS_COLLECTION_HANDLE" + echo "laefer_collection_handle=$LAEFER_COLLECTION_HANDLE" + echo "tandon_collection_handle=$TANDON_COLLECTION_HANDLE" + echo "tandoncapstone_collection_handle=$TANDONCAPSTONE_COLLECTION_HANDLE" + echo "dnp_collection_handle=$DNP_COLLECTION_HANDLE" + echo "calabash_collection_handle=$CALABASH_COLLECTION_HANDLE" + echo "openscholarship_collection_handle=$OPENSCHOLARSHIP_COLLECTION_HANDLE" + echo "syllabi_collection_handle=$SYLLABI_COLLECTION_HANDLE" + } >> "$GITHUB_OUTPUT" +fi diff --git a/scripts/cypress.env.json b/scripts/cypress.env.json new file mode 100644 index 00000000000..4d8c08ac5b4 --- /dev/null +++ b/scripts/cypress.env.json @@ -0,0 +1,12 @@ +{ + "DSPACE_TEST_FDA_DEFAULT_ITEM": "450ce8b2-46b4-4490-9a34-2d29d6027b9e", + "DSPACE_TEST_JONES_ITEM": "5fce9399-152f-4f32-a8f5-0cd77c307a50", + "DSPACE_TEST_RELICS_ITEM": "cbf6502f-382b-4a62-a9fd-a50ee6bc461c", + "DSPACE_TEST_LAEFER_ITEM": "c00bc7bd-ad28-4fee-bbe9-2ac90451297a", + "DSPACE_TEST_TANDON_ITEM": "ffbe5a48-23c2-432d-b0b1-d12e8fe370a6", + "DSPACE_TEST_TANDONCAPSTONE_ITEM": "ef71c4de-68af-4e5d-88c7-e4304988dc4c", + "DSPACE_TEST_DNP_ITEM": "25fc7653-0d94-4330-a6d6-d52ba45a0826", + "DSPACE_TEST_CALABASH_ITEM": "291e765c-356c-4861-99f8-dc6556fcd01b", + "DSPACE_TEST_OPENSCHOLARSHIP_ITEM": "c7641ccb-9a20-4ba3-b521-3ddd036de76f", + "DSPACE_TEST_SYLLABI_ITEM": "58c853b0-c428-43e1-bf30-c6669205093b" +} diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index dd5f0db6db6..acd26323d92 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1,4 +1,5 @@ { + "401.help": "You're not authorized to access this page. You can use the button below to get back to the home page.", "401.link.home-page": "Take me to the home page", @@ -2803,7 +2804,7 @@ "item.page.citation": "Citation", - "item.page.collections": "Collections", + "item.page.collections": "Appears in Collections", "item.page.collections.loading": "Loading...", @@ -2813,7 +2814,7 @@ "item.page.edit": "Edit this item", - "item.page.files": "Files", + "item.page.files": "Files in This Item", "item.page.filesection.description": "Description:", @@ -2827,7 +2828,7 @@ "item.page.journal.search.title": "Articles in this journal", - "item.page.link.full": "Full item page", + "item.page.link.full": "Show full item record", "item.page.link.simple": "Simple item page", @@ -2887,7 +2888,7 @@ "item.preview.dc.description.abstract": "Abstract:", - "item.preview.dc.identifier.other": "Other identifier:", + "item.preview.dc.identifier.other": "Other Identifier", "item.preview.dc.language.iso": "Language:", @@ -2907,8 +2908,6 @@ "item.preview.dc.rights": "Rights", - "item.preview.dc.identifier.other": "Other Identifier", - "item.preview.dc.relation.issn": "ISSN", "item.preview.dc.identifier.isbn": "ISBN", @@ -4093,7 +4092,6 @@ "syllabus.search.title": "Syllabus Search", - "media-viewer.next": "Next", "media-viewer.previous": "Previous", @@ -5720,7 +5718,7 @@ "thumbnail.person.placeholder": "No Profile Picture Available", - "title": "FDA title", + title: "FDA title", "vocabulary-treeview.header": "Hierarchical tree view", @@ -6255,152 +6253,263 @@ "service.overview.delete.header": "Delete Service", "ldn-registered-services.title": "Registered Services", + "ldn-registered-services.table.name": "Name", + "ldn-registered-services.table.description": "Description", + "ldn-registered-services.table.status": "Status", + "ldn-registered-services.table.action": "Action", + "ldn-registered-services.new": "NEW", + "ldn-registered-services.new.breadcrumbs": "Registered Services", "ldn-service.overview.table.enabled": "Enabled", + "ldn-service.overview.table.disabled": "Disabled", + "ldn-service.overview.table.clickToEnable": "Click to enable", + "ldn-service.overview.table.clickToDisable": "Click to disable", "ldn-edit-registered-service.title": "Edit Service", + "ldn-create-service.title": "Create service", + "service.overview.create.modal": "Create Service", + "service.overview.create.body": "Please confirm the creation of this service.", + "ldn-service-status": "Status", + "service.confirm.create": "Create", + "service.refuse.create": "Cancel", + "ldn-register-new-service.title": "Register a new service", + "ldn-new-service.form.label.submit": "Save", + "ldn-new-service.form.label.name": "Name", + "ldn-new-service.form.label.description": "Description", + "ldn-new-service.form.label.url": "Service URL", + "ldn-new-service.form.label.ip-range": "Service IP range", + "ldn-new-service.form.label.score": "Level of trust", + "ldn-new-service.form.label.ldnUrl": "LDN Inbox URL", + "ldn-new-service.form.placeholder.name": "Please provide service name", + "ldn-new-service.form.placeholder.description": "Please provide a description regarding your service", + "ldn-new-service.form.placeholder.url": "Please input the URL for users to check out more information about the service", + "ldn-new-service.form.placeholder.lowerIp": "IPv4 range lower bound", + "ldn-new-service.form.placeholder.upperIp": "IPv4 range upper bound", + "ldn-new-service.form.placeholder.ldnUrl": "Please specify the URL of the LDN Inbox", + "ldn-new-service.form.placeholder.score": "Please enter a value between 0 and 1. Use the “.” as decimal separator", + "ldn-service.form.label.placeholder.default-select": "Select a pattern", "ldn-service.form.pattern.ack-accept.label": "Acknowledge and Accept", + "ldn-service.form.pattern.ack-accept.description": "This pattern is used to acknowledge and accept a request (offer). It implies an intention to act on the request.", + "ldn-service.form.pattern.ack-accept.category": "Acknowledgements", "ldn-service.form.pattern.ack-reject.label": "Acknowledge and Reject", + "ldn-service.form.pattern.ack-reject.description": "This pattern is used to acknowledge and reject a request (offer). It signifies no further action regarding the request.", + "ldn-service.form.pattern.ack-reject.category": "Acknowledgements", "ldn-service.form.pattern.ack-tentative-accept.label": "Acknowledge and Tentatively Accept", + "ldn-service.form.pattern.ack-tentative-accept.description": "This pattern is used to acknowledge and tentatively accept a request (offer). It implies an intention to act, which may change.", + "ldn-service.form.pattern.ack-tentative-accept.category": "Acknowledgements", "ldn-service.form.pattern.ack-tentative-reject.label": "Acknowledge and Tentatively Reject", + "ldn-service.form.pattern.ack-tentative-reject.description": "This pattern is used to acknowledge and tentatively reject a request (offer). It signifies no further action, subject to change.", + "ldn-service.form.pattern.ack-tentative-reject.category": "Acknowledgements", "ldn-service.form.pattern.announce-endorsement.label": "Announce Endorsement", + "ldn-service.form.pattern.announce-endorsement.description": "This pattern is used to announce the existence of an endorsement, referencing the endorsed resource.", + "ldn-service.form.pattern.announce-endorsement.category": "Announcements", "ldn-service.form.pattern.announce-ingest.label": "Announce Ingest", + "ldn-service.form.pattern.announce-ingest.description": "This pattern is used to announce that a resource has been ingested.", + "ldn-service.form.pattern.announce-ingest.category": "Announcements", "ldn-service.form.pattern.announce-relationship.label": "Announce Relationship", + "ldn-service.form.pattern.announce-relationship.description": "This pattern is used to announce a relationship between two resources.", + "ldn-service.form.pattern.announce-relationship.category": "Announcements", "ldn-service.form.pattern.announce-review.label": "Announce Review", + "ldn-service.form.pattern.announce-review.description": "This pattern is used to announce the existence of a review, referencing the reviewed resource.", + "ldn-service.form.pattern.announce-review.category": "Announcements", "ldn-service.form.pattern.announce-service-result.label": "Announce Service Result", + "ldn-service.form.pattern.announce-service-result.description": "This pattern is used to announce the existence of a 'service result', referencing the relevant resource.", + "ldn-service.form.pattern.announce-service-result.category": "Announcements", "ldn-service.form.pattern.request-endorsement.label": "Request Endorsement", + "ldn-service.form.pattern.request-endorsement.description": "This pattern is used to request endorsement of a resource owned by the origin system.", + "ldn-service.form.pattern.request-endorsement.category": "Requests", "ldn-service.form.pattern.request-ingest.label": "Request Ingest", + "ldn-service.form.pattern.request-ingest.description": "This pattern is used to request that the target system ingest a resource.", + "ldn-service.form.pattern.request-ingest.category": "Requests", "ldn-service.form.pattern.request-review.label": "Request Review", + "ldn-service.form.pattern.request-review.description": "This pattern is used to request a review of a resource owned by the origin system.", + "ldn-service.form.pattern.request-review.category": "Requests", "ldn-service.form.pattern.undo-offer.label": "Undo Offer", + "ldn-service.form.pattern.undo-offer.description": "This pattern is used to undo (retract) an offer previously made.", + "ldn-service.form.pattern.undo-offer.category": "Undo", "ldn-new-service.form.label.placeholder.selectedItemFilter": "No Item Filter Selected", + "ldn-new-service.form.label.ItemFilter": "Item Filter", + "ldn-new-service.form.label.automatic": "Automatic", + "ldn-new-service.form.error.name": "Name is required", + "ldn-new-service.form.error.url": "URL is required", + "ldn-new-service.form.error.ipRange": "Please enter a valid IP range", + "ldn-new-service.form.hint.ipRange": "Please enter a valid IpV4 in both range bounds (note: for single IP, please enter the same value in both fields)", + "ldn-new-service.form.error.ldnurl": "LDN URL is required", + "ldn-new-service.form.error.patterns": "At least a pattern is required", + "ldn-new-service.form.error.score": "Please enter a valid score (between 0 and 1). Use the “.” as decimal separator", "ldn-new-service.form.label.inboundPattern": "Supported Pattern", + "ldn-new-service.form.label.addPattern": "+ Add more", + "ldn-new-service.form.label.removeItemFilter": "Remove", + "ldn-register-new-service.breadcrumbs": "New Service", + "service.overview.delete.body": "Are you sure you want to delete this service?", + "service.overview.edit.body": "Do you confirm the changes?", + "service.overview.edit.modal": "Edit Service", + "service.detail.update": "Confirm", + "service.detail.return": "Cancel", + "service.overview.reset-form.body": "Are you sure you want to discard the changes and leave?", + "service.overview.reset-form.modal": "Discard Changes", + "service.overview.reset-form.reset-confirm": "Discard", + "admin.registries.services-formats.modify.success.head": "Successful Edit", + "admin.registries.services-formats.modify.success.content": "The service has been edited", + "admin.registries.services-formats.modify.failure.head": "Failed Edit", + "admin.registries.services-formats.modify.failure.content": "The service has not been edited", + "ldn-service-notification.created.success.title": "Successful Create", + "ldn-service-notification.created.success.body": "The service has been created", + "ldn-service-notification.created.failure.title": "Failed Create", + "ldn-service-notification.created.failure.body": "The service has not been created", + "ldn-service-notification.created.warning.title": "Please select at least one Inbound Pattern", + "ldn-enable-service.notification.success.title": "Successful status updated", + "ldn-enable-service.notification.success.content": "The service status has been updated", + "ldn-service-delete.notification.success.title": "Successful Deletion", + "ldn-service-delete.notification.success.content": "The service has been deleted", + "ldn-service-delete.notification.error.title": "Failed Deletion", + "ldn-service-delete.notification.error.content": "The service has not been deleted", + "service.overview.reset-form.reset-return": "Cancel", + "service.overview.delete": "Delete service", + "ldn-edit-service.title": "Edit service", + "ldn-edit-service.form.label.name": "Name", + "ldn-edit-service.form.label.description": "Description", + "ldn-edit-service.form.label.url": "Service URL", + "ldn-edit-service.form.label.ldnUrl": "LDN Inbox URL", + "ldn-edit-service.form.label.inboundPattern": "Inbound Pattern", + "ldn-edit-service.form.label.noInboundPatternSelected": "No Inbound Pattern", + "ldn-edit-service.form.label.selectedItemFilter": "Selected Item Filter", + "ldn-edit-service.form.label.selectItemFilter": "No Item Filter", + "ldn-edit-service.form.label.automatic": "Automatic", + "ldn-edit-service.form.label.addInboundPattern": "+ Add more", + "ldn-edit-service.form.label.submit": "Save", + "ldn-edit-service.breadcrumbs": "Edit Service", + "ldn-service.control-constaint-select-none": "Select none", "ldn-register-new-service.notification.error.title": "Error", + "ldn-register-new-service.notification.error.content": "An error occurred while creating this process", + "ldn-register-new-service.notification.success.title": "Success", + "ldn-register-new-service.notification.success.content": "The process was successfully created", "submission.sections.notify.info": "The selected service is compatible with the item according to its current status. {{ service.name }}: {{ service.description }}", @@ -6420,6 +6529,7 @@ "menu.section.services_new": "LDN Service", "quality-assurance.topics.description-with-target": "Below you can see all the topics received from the subscriptions to {{source}} in regards to the", + "quality-assurance.events.description": "Below the list of all the suggestions for the selected topic {{topic}}, related to {{source}}.", "quality-assurance.events.description-with-topic-and-target": "Below the list of all the suggestions for the selected topic {{topic}}, related to {{source}} and ", @@ -6929,4 +7039,304 @@ "item.page.relation.ispartofseries": "Series/Report no.", "item.page.copyright": "Items in FDA are protected by copyright, with all rights reserved, unless otherwise indicated.", -} + + "jones.item.page.title": "Title", + + "jones.item.page.date.issued": "Date of digital object", + + "jones.item.page.date.created": "Date of object depicted", + + "jones.item.page.contributor.*": "Authors", + + "jones.item.page.description": "Description of particular view", + + "jones.item.page.identifier.other": "Technical designation of object", + + "jones.item.page.description.equipment": "Technical specifications", + + "jones.item.page.description.additional": "Additional description", + + "relics.item.page.title": "Title", + + "relics.item.page.contributor.*": "Authors", + + "relics.item.page.coverage.spatial": "Country", + + "relics.item.page.date.issued": "Date", + + "jones.item.page.language.iso": "Language", + + "jones.item.page.format.mimetype": "File format", + + "jones.item.page.relation.isreferencedby": "Bibliographic citation", + + "jones.item.page.relation.uri": "Citation link", + + "jones.item.page.rights": "Rights", + + "jones.item.page.subject": "Subject Keywords", + + "relics.item.page.publisher": "Publisher", + + "relics.item.page.type": "Type", + + "relics.item.page.source": "Source", + + "relics.item.page.description": "Description", + + "relics.item.page.rights": "Rights", + + "tandon.item.page.title": "Title", + + "tandon.item.page.contributor.*": "Authors", + + "tandon.item.page.date.issued": "Date Issued", + + "tandon.item.page.citation": "Citation", + + "tandon.item.page.abstract": "Abstract", + + "tandon.item.page.description.firstPage": "First Page", + + "tandon.item.page.description.lastPage": "Last Page", + + "tandon.item.page.doi": "DOI", + + "tandon.item.page.type": "Type", + + "tandoncapstone.item.page.title": "Title", + + "tandoncapstone.item.page.contributor.author": "Authors", + + "tandoncapstone.item.page.contributor.advisor": "Capstone Professor", + + "tandoncapstone.item.page.date.issued": "Date Issued", + + "tandoncapstone.item.page.language.iso": "Language", + + "tandoncapstone.item.page.abstract": "Abstract", + + "tandoncapstone.item.page.type": "Type", + + "tandoncapstone.item.page.format.medium": "Medium", + + "tandoncapstone.item.page.format.Extent": "Extent", + + "tandoncapstone.item.page.uri": "URI", + + "dnp.item.page.title": "Title", + + "dnp.item.page.title.alternative": "Alternate Title", + + "dnp.item.page.contributor.author": "Author", + + "dnp.item.page.contributor.advisor": "DNP Project Team Member", + + "dnp.item.page.date.issued": "Degree Year", + + "dnp.item.page.description": "Description", + + "dnp.item.page.abstract": "Abstract", + + "dnp.item.page.description.sponsorship": "Sponsorship", + + "dnp.item.page.uri": "FDA Handle", + + "dnp.item.page.subject": "Keyword", + + "dnp.item.page.subject.mesh": "MeSH term", + + "dnp.item.page.subject.cinahl": "CINAHL term", + + "dnp.item.page.subject.apa": "APA Thesaurus term", + + "dnp.item.page.rights": "Rights", + + "dnp.item.page.type": "Type", + + "dnp.item.page.format.medium": "Medium", + + "dnp.item.page.language.iso": "Language", + + "dnp.item.page.thesis.degree.name": "Degree", + + "dnp.item.page.thesis.degree.level": "Degree Level", + + "dnp.item.page.thesis.degree.discipline": "Discipline", + + "dnp.item.page.thesis.degree.grantor": "Grantor", + + "openscholarship.item.page.title": "Title", + + "openscholarship.item.page.contributor.author": "Authors", + + "openscholarship.item.page.date.issued": "Date Issued", + + "openscholarship.item.page.abstract": "Abstract", + + "openscholarship.item.page.description.sponsorship": "Sponsorship", + + "openscholarship.item.page.doi": "DOI", + + "openscholarship.item.page.rights": "Rights", + + "openscholarship.item.page.subject": "Subject", + + "laefer.item.page.title": "Title", + + "laefer.item.page.contributor.*": "Authors", + + "laefer.item.page.contributor.author": "Authors", + + "laefer.item.page.contributor.editor": "Editors", + + "laefer.item.page.date.issued": "Issue Date", + + "laefer.item.page.description": "Description", + + "laefer.item.page.abstract": "Abstract", + + "laefer.item.page.identifier": "Other Identifiers", + + "laefer.item.page.citation": "Citation", + + "laefer.item.page.identifier.govdoc": "Gov't Doc #", + + "laefer.item.page.isbn": "ISBN", + + "laefer.item.page.ismn": "ISMN", + + "laefer.item.page.issn": "ISSN", + + "laefer.item.page.uri": "URI", + + "laefer.item.page.publisher": "Publisher", + + "laefer.item.page.relation.ispartofseries": "Series/Report no.", + + "laefer.item.page.subject": "Keywords", + + "laefer.item.page.title.alternative": "Other Titles", + + "laefer.item.page.rights": "Rights", + + "calabash.item.page.title": "Title", + + "calabash.item.page.contributor.*": "Authors", + + "calabash.item.page.contributor.author": "Authors", + + "calabash.item.page.contributor.translator": "Translator", + + "calabash.item.page.date.issued": "Issue Date", + + "calabash.item.page.description": "Description", + + "calabash.item.page.abstract": "Abstract", + + "calabash.item.page.identifier": "Other Identifiers", + + "calabash.item.page.citation": "Citation", + + "calabash.item.page.issn": "ISSN", + + "calabash.item.page.uri": "URI", + + "calabash.item.page.doi": "DOI", + + "calabash.item.page.publisher": "Publisher", + + "calabash.item.page.subject": "Subject Keywords", + + "calabash.item.page.title.alternative": "Other Titles", + + "calabash.item.page.rights": "Rights", + + "calabash.item.page.type": "Type", + + "calabash.item.page.prism.endingPage": "Last Page", + + "calabash.item.page.prism.issueIdentifier": "Issue", + + "calabash.item.page.prism.publicationName": "Journal Title", + + "calabash.item.page.prism.startingPage": "First Page", + + "calabash.item.page.prism.volume": "Volume", + + "syllabi.item.page.title": "Title", + + "syllabi.item.page.contributor.instructor": "Instructor(s)", + + "syllabi.item.page.identifier.coursenumber": "Course Number", + + "syllabi.item.page.description.semester": "Term", + + "syllabi.item.page.subject": "Keywords", + + "syllabi.item.page.uri": "URI", + + "syllabi.item.page.date.issued": "Date", + + "syllabi.item.page.rights": "Rights", + + "item.page.type": "Type", + + "item.page.source": "Source", + + "item.page.coverage.spatial": "Location", + + "item.page.identifier.other": "Other Identifier", + + "item.page.language.iso": "Language", + + "item.page.format.mimetype": "Format", + + "item.page.description.equipment": "Equipment", + + "item.page.description.additional": "Additional Information", + + "item.page.relation.isreferencedby": "Referenced By", + + "item.page.relation.uri": "Related Link", + + "item.page.description.firstPage": "First Page", + + "item.page.description.lastPage": "Last Page", + + "item.page.contributor.advisor": "Advisor", + + "item.page.contributor.instructor": "Instructor", + + "item.page.thesis.degree.name": "Degree", + + "item.page.thesis.degree.level": "Degree Level", + + "item.page.thesis.degree.discipline": "Discipline", + + "item.page.thesis.degree.grantor": "Grantor", + + "item.page.subject.mesh": "MeSH Terms", + + "item.page.subject.cinahl": "CINAHL Terms", + + "item.page.subject.apa": "APA Thesaurus Terms", + + "item.page.description.sponsorship": "Sponsorship", + + "item.page.format.medium": "Medium", + + "item.page.prism.publicationName": "Publication Name", + + "item.page.prism.issueIdentifier": "Issue", + + "item.page.prism.volume": "Volume", + + "item.page.prism.startingPage": "Starting Page", + + "item.page.prism.endingPage": "Ending Page", + + "item.page.identifier.coursenumber": "Course Number", + + "item.page.description.semester": "Semester", +} \ No newline at end of file diff --git a/src/themes/calabash/app/item-page/simple/item-types/untyped-item/untyped-item.component.html b/src/themes/calabash/app/item-page/simple/item-types/untyped-item/untyped-item.component.html deleted file mode 100644 index 2359e3324be..00000000000 --- a/src/themes/calabash/app/item-page/simple/item-types/untyped-item/untyped-item.component.html +++ /dev/null @@ -1,155 +0,0 @@ - - -
| {{'item.page.title' | translate}}: | -
-
-
- |
-
| {{'item.page.title.alternative' | translate}}: | -
- |
-
| {{'item.page.contributor.*' | translate}}: | -
- |
-
| {{'item.page.subject' | translate}}: | -- {{object.allMetadataValues('dc.subject').join('; ')}} - | -
| {{'item.page.date.issued' | translate}}: | -- {{object.allMetadataValues('dc.date.issued')}} - | -
| {{'item.page.publisher' | translate}}: | -
- |
-
| {{'item.page.citation' | translate}}: | -
- |
-
| {{'item.page.relation.ispartofseries' | translate}}: | -
- |
-
| {{'item.page.abstract' | translate}}: | -
- |
-
| {{'item.page.description' | translate}}: | -
- |
-
| {{'item.page.identifier.govdoc' | translate}}: | -
- |
-
| {{'item.page.uri' | translate}}: | -
- |
-
| {{'item.page.isbn' | translate}}: | -
- |
-
| {{'item.page.issn' | translate}}: | -
- |
-
| {{'item.page.doi' | translate}}: | -
- |
-
| {{'item.page.ismn' | translate}}: | -
- |
-
| {{'item.page.identifier' | translate}}: | -
- |
-
| {{'item.page.rights' | translate}}: | -
- |
-
| {{getI18nKey(fieldConfig.labelKey) | translate}}: | +
+ {{getFirstFieldValue(fieldConfig)}}+ |
+
| + {{getI18nKey(fieldConfig.labelKey) | translate}}: + | +
+ |
+
| {{getI18nKey(fieldConfig.labelKey) | translate}}: | ++ + {{getFirstFieldValue(fieldConfig)}} + + | +
| {{getI18nKey(fieldConfig.labelKey) | translate}}: | +
+ |
+
| {{getI18nKey(fieldConfig.labelKey) | translate}}: | ++ {{getFirstFieldValue(fieldConfig)}} + | +