mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Resolved conflicts
This commit is contained in:
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
@@ -43,11 +43,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# https://github.com/actions/checkout
|
# https://github.com/actions/checkout
|
||||||
- name: Checkout codebase
|
- name: Checkout codebase
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# https://github.com/actions/setup-node
|
# https://github.com/actions/setup-node
|
||||||
- name: Install Node.js ${{ matrix.node-version }}
|
- name: Install Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ jobs:
|
|||||||
# https://github.com/cypress-io/github-action
|
# https://github.com/cypress-io/github-action
|
||||||
# (NOTE: to run these e2e tests locally, just use 'ng e2e')
|
# (NOTE: to run these e2e tests locally, just use 'ng e2e')
|
||||||
- name: Run e2e tests (integration tests)
|
- name: Run e2e tests (integration tests)
|
||||||
uses: cypress-io/github-action@v5
|
uses: cypress-io/github-action@v6
|
||||||
with:
|
with:
|
||||||
# Run tests in Chrome, headless mode (default)
|
# Run tests in Chrome, headless mode (default)
|
||||||
browser: chrome
|
browser: chrome
|
||||||
@@ -191,7 +191,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Download artifacts from previous 'tests' job
|
# Download artifacts from previous 'tests' job
|
||||||
- name: Download coverage artifacts
|
- name: Download coverage artifacts
|
||||||
@@ -203,10 +203,14 @@ jobs:
|
|||||||
# Retry action: https://github.com/marketplace/actions/retry-action
|
# Retry action: https://github.com/marketplace/actions/retry-action
|
||||||
# Codecov action: https://github.com/codecov/codecov-action
|
# Codecov action: https://github.com/codecov/codecov-action
|
||||||
- name: Upload coverage to Codecov.io
|
- name: Upload coverage to Codecov.io
|
||||||
uses: Wandalen/wretry.action@v1.0.36
|
uses: Wandalen/wretry.action@v1.3.0
|
||||||
with:
|
with:
|
||||||
action: codecov/codecov-action@v3
|
action: codecov/codecov-action@v3
|
||||||
# Try upload 5 times max
|
# Ensure codecov-action throws an error when it fails to upload
|
||||||
|
# This allows us to auto-restart the action if an error is thrown
|
||||||
|
with: |
|
||||||
|
fail_ci_if_error: true
|
||||||
|
# Try re-running action 5 times max
|
||||||
attempt_limit: 5
|
attempt_limit: 5
|
||||||
# Run again in 30 seconds
|
# Run again in 30 seconds
|
||||||
attempt_delay: 30000
|
attempt_delay: 30000
|
||||||
|
2
.github/workflows/codescan.yml
vendored
2
.github/workflows/codescan.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# https://github.com/actions/checkout
|
# https://github.com/actions/checkout
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
# https://github.com/github/codeql-action
|
# https://github.com/github/codeql-action
|
||||||
|
303
.github/workflows/docker.yml
vendored
303
.github/workflows/docker.yml
vendored
@@ -3,6 +3,9 @@ name: Docker images
|
|||||||
|
|
||||||
# Run this Build for all pushes to 'main' or maintenance branches, or tagged releases.
|
# Run this Build for all pushes to 'main' or maintenance branches, or tagged releases.
|
||||||
# Also run for PRs to ensure PR doesn't break Docker build process
|
# Also run for PRs to ensure PR doesn't break Docker build process
|
||||||
|
# NOTE: uses "reusable-docker-build.yml" in DSpace/DSpace to actually build each of the Docker images
|
||||||
|
# https://github.com/DSpace/DSpace/blob/main/.github/workflows/reusable-docker-build.yml
|
||||||
|
#
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@@ -15,288 +18,42 @@ on:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read # to fetch code (actions/checkout)
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY_IMAGE: dspace/dspace-angular
|
|
||||||
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
|
|
||||||
# For a new commit on default branch (main), use the literal tag 'latest' on Docker image.
|
|
||||||
# For a new commit on other branches, use the branch name as the tag for Docker image.
|
|
||||||
# For a new tag, copy that tag name as the tag for Docker image.
|
|
||||||
IMAGE_TAGS: |
|
|
||||||
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
|
|
||||||
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }}
|
|
||||||
type=ref,event=tag
|
|
||||||
# Define default tag "flavor" for docker/metadata-action per
|
|
||||||
# https://github.com/docker/metadata-action#flavor-input
|
|
||||||
# We manage the 'latest' tag ourselves to the 'main' branch (see settings above)
|
|
||||||
TAGS_FLAVOR: |
|
|
||||||
latest=false
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
#############################################################
|
#############################################################
|
||||||
# Build/Push the '${{ env.REGISTRY_IMAGE }}' image
|
# Build/Push the 'dspace/dspace-angular' image
|
||||||
#############################################################
|
#############################################################
|
||||||
dspace-angular:
|
dspace-angular:
|
||||||
# Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace-angular'
|
# Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace-angular'
|
||||||
if: github.repository == 'dspace/dspace-angular'
|
if: github.repository == 'dspace/dspace-angular'
|
||||||
|
# Use the reusable-docker-build.yml script from DSpace/DSpace repo to build our Docker image
|
||||||
strategy:
|
uses: DSpace/DSpace/.github/workflows/reusable-docker-build.yml@main
|
||||||
matrix:
|
with:
|
||||||
# Architectures / Platforms for which we will build Docker images
|
build_id: dspace-angular
|
||||||
arch: ['linux/amd64', 'linux/arm64']
|
image_name: dspace/dspace-angular
|
||||||
os: [ubuntu-latest]
|
dockerfile_path: ./Dockerfile
|
||||||
isPr:
|
secrets:
|
||||||
- ${{ github.event_name == 'pull_request' }}
|
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||||
# If this is a PR, we ONLY build for AMD64. For PRs we only do a sanity check test to ensure Docker builds work.
|
DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
# The below exclude therefore ensures we do NOT build ARM64 for PRs.
|
|
||||||
exclude:
|
|
||||||
- isPr: true
|
|
||||||
os: ubuntu-latest
|
|
||||||
arch: linux/arm64
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
# https://github.com/actions/checkout
|
|
||||||
- name: Checkout codebase
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# https://github.com/docker/setup-buildx-action
|
|
||||||
- name: Setup Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
# https://github.com/docker/setup-qemu-action
|
|
||||||
- name: Set up QEMU emulation to build for multiple architectures
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
# https://github.com/docker/login-action
|
|
||||||
- name: Login to DockerHub
|
|
||||||
# Only login if not a PR, as PRs only trigger a Docker build and not a push
|
|
||||||
if: ${{ ! matrix.isPr }}
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
# https://github.com/docker/metadata-action
|
|
||||||
# Get Metadata for docker_build step below
|
|
||||||
- name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-angular' image
|
|
||||||
id: meta_build
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY_IMAGE }}
|
|
||||||
tags: ${{ env.IMAGE_TAGS }}
|
|
||||||
flavor: ${{ env.TAGS_FLAVOR }}
|
|
||||||
|
|
||||||
# https://github.com/docker/build-push-action
|
|
||||||
- name: Build and push 'dspace-angular' image
|
|
||||||
id: docker_build
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
platforms: ${{ matrix.arch }}
|
|
||||||
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
|
|
||||||
# but we ONLY do an image push to DockerHub if it's NOT a PR
|
|
||||||
push: ${{ ! matrix.isPr }}
|
|
||||||
# Use tags / labels provided by 'docker/metadata-action' above
|
|
||||||
tags: ${{ steps.meta_build.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta_build.outputs.labels }}
|
|
||||||
|
|
||||||
# Export the digest of Docker build locally (for non PRs only)
|
|
||||||
- name: Export digest
|
|
||||||
if: ${{ ! matrix.isPr }}
|
|
||||||
run: |
|
|
||||||
mkdir -p /tmp/digests
|
|
||||||
digest="${{ steps.docker_build.outputs.digest }}"
|
|
||||||
touch "/tmp/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
# Upload digest to an artifact, so that it can be used in manifest below
|
|
||||||
- name: Upload digest
|
|
||||||
if: ${{ ! matrix.isPr }}
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: digests
|
|
||||||
path: /tmp/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
# Merge digests into a manifest.
|
|
||||||
# This runs after all Docker builds complete above, and it tells hub.docker.com
|
|
||||||
# that these builds should be all included in the manifest for this tag.
|
|
||||||
# (e.g. AMD64 and ARM64 should be listed as options under the same tagged Docker image)
|
|
||||||
# Borrowed from https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners
|
|
||||||
dspace-angular_manifest:
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- dspace-angular
|
|
||||||
steps:
|
|
||||||
- name: Download digests
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: digests
|
|
||||||
path: /tmp/digests
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Add Docker metadata for image
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY_IMAGE }}
|
|
||||||
tags: ${{ env.IMAGE_TAGS }}
|
|
||||||
flavor: ${{ env.TAGS_FLAVOR }}
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
- name: Create manifest list from digests and push
|
|
||||||
working-directory: /tmp/digests
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
|
||||||
|
|
||||||
- name: Inspect image
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
|
|
||||||
|
|
||||||
#############################################################
|
#############################################################
|
||||||
# Build/Push the '${{ env.REGISTRY_IMAGE }}' image ('-dist' tag)
|
# Build/Push the 'dspace/dspace-angular' image ('-dist' tag)
|
||||||
#############################################################
|
#############################################################
|
||||||
dspace-angular-dist:
|
dspace-angular-dist:
|
||||||
# Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace-angular'
|
# Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace-angular'
|
||||||
if: github.repository == 'dspace/dspace-angular'
|
if: github.repository == 'dspace/dspace-angular'
|
||||||
|
# Use the reusable-docker-build.yml script from DSpace/DSpace repo to build our Docker image
|
||||||
strategy:
|
uses: DSpace/DSpace/.github/workflows/reusable-docker-build.yml@main
|
||||||
matrix:
|
with:
|
||||||
# Architectures / Platforms for which we will build Docker images
|
build_id: dspace-angular-dist
|
||||||
arch: ['linux/amd64', 'linux/arm64']
|
image_name: dspace/dspace-angular
|
||||||
os: [ubuntu-latest]
|
dockerfile_path: ./Dockerfile.dist
|
||||||
isPr:
|
# As this is a "dist" image, its tags are all suffixed with "-dist". Otherwise, it uses the same
|
||||||
- ${{ github.event_name == 'pull_request' }}
|
# tagging logic as the primary 'dspace/dspace-angular' image above.
|
||||||
# If this is a PR, we ONLY build for AMD64. For PRs we only do a sanity check test to ensure Docker builds work.
|
tags_flavor: suffix=-dist
|
||||||
# The below exclude therefore ensures we do NOT build ARM64 for PRs.
|
secrets:
|
||||||
exclude:
|
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||||
- isPr: true
|
DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
os: ubuntu-latest
|
# Enable redeploy of sandbox & demo if the branch for this image matches the deployment branch of
|
||||||
arch: linux/arm64
|
# these sites as specified in reusable-docker-build.xml
|
||||||
|
REDEPLOY_SANDBOX_URL: ${{ secrets.REDEPLOY_SANDBOX_URL }}
|
||||||
runs-on: ${{ matrix.os }}
|
REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }}
|
||||||
steps:
|
|
||||||
# https://github.com/actions/checkout
|
|
||||||
- name: Checkout codebase
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# https://github.com/docker/setup-buildx-action
|
|
||||||
- name: Setup Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
# https://github.com/docker/setup-qemu-action
|
|
||||||
- name: Set up QEMU emulation to build for multiple architectures
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
# https://github.com/docker/login-action
|
|
||||||
- name: Login to DockerHub
|
|
||||||
# Only login if not a PR, as PRs only trigger a Docker build and not a push
|
|
||||||
if: ${{ ! matrix.isPr }}
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
# https://github.com/docker/metadata-action
|
|
||||||
# Get Metadata for docker_build_dist step below
|
|
||||||
- name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-angular-dist' image
|
|
||||||
id: meta_build_dist
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY_IMAGE }}
|
|
||||||
tags: ${{ env.IMAGE_TAGS }}
|
|
||||||
# As this is a "dist" image, its tags are all suffixed with "-dist". Otherwise, it uses the same
|
|
||||||
# tagging logic as the primary '${{ env.REGISTRY_IMAGE }}' image above.
|
|
||||||
flavor: ${{ env.TAGS_FLAVOR }}
|
|
||||||
suffix=-dist
|
|
||||||
|
|
||||||
- name: Build and push 'dspace-angular-dist' image
|
|
||||||
id: docker_build_dist
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile.dist
|
|
||||||
platforms: ${{ matrix.arch }}
|
|
||||||
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
|
|
||||||
# but we ONLY do an image push to DockerHub if it's NOT a PR
|
|
||||||
push: ${{ ! matrix.isPr }}
|
|
||||||
# Use tags / labels provided by 'docker/metadata-action' above
|
|
||||||
tags: ${{ steps.meta_build_dist.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta_build_dist.outputs.labels }}
|
|
||||||
|
|
||||||
# Export the digest of Docker build locally (for non PRs only)
|
|
||||||
- name: Export digest
|
|
||||||
if: ${{ ! matrix.isPr }}
|
|
||||||
run: |
|
|
||||||
mkdir -p /tmp/digests
|
|
||||||
digest="${{ steps.docker_build_dist.outputs.digest }}"
|
|
||||||
touch "/tmp/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
# Upload Digest to an artifact, so that it can be used in manifest below
|
|
||||||
- name: Upload digest
|
|
||||||
if: ${{ ! matrix.isPr }}
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
# NOTE: It's important that this artifact has a unique name so that two
|
|
||||||
# image builds don't upload digests to the same artifact.
|
|
||||||
name: digests-dist
|
|
||||||
path: /tmp/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
# Merge *-dist digests into a manifest.
|
|
||||||
# This runs after all Docker builds complete above, and it tells hub.docker.com
|
|
||||||
# that these builds should be all included in the manifest for this tag.
|
|
||||||
# (e.g. AMD64 and ARM64 should be listed as options under the same tagged Docker image)
|
|
||||||
dspace-angular-dist_manifest:
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- dspace-angular-dist
|
|
||||||
steps:
|
|
||||||
- name: Download digests for -dist builds
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: digests-dist
|
|
||||||
path: /tmp/digests
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Add Docker metadata for image
|
|
||||||
id: meta_dist
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.REGISTRY_IMAGE }}
|
|
||||||
tags: ${{ env.IMAGE_TAGS }}
|
|
||||||
# As this is a "dist" image, its tags are all suffixed with "-dist". Otherwise, it uses the same
|
|
||||||
# tagging logic as the primary '${{ env.REGISTRY_IMAGE }}' image above.
|
|
||||||
flavor: ${{ env.TAGS_FLAVOR }}
|
|
||||||
suffix=-dist
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
- name: Create manifest list from digests and push
|
|
||||||
working-directory: /tmp/digests
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
|
||||||
|
|
||||||
- name: Inspect image
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta_dist.outputs.version }}
|
|
@@ -23,11 +23,11 @@ jobs:
|
|||||||
if: github.event.pull_request.merged
|
if: github.event.pull_request.merged
|
||||||
steps:
|
steps:
|
||||||
# Checkout code
|
# Checkout code
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
# Port PR to other branch (ONLY if labeled with "port to")
|
# Port PR to other branch (ONLY if labeled with "port to")
|
||||||
# See https://github.com/korthout/backport-action
|
# See https://github.com/korthout/backport-action
|
||||||
- name: Create backport pull requests
|
- name: Create backport pull requests
|
||||||
uses: korthout/backport-action@v1
|
uses: korthout/backport-action@v2
|
||||||
with:
|
with:
|
||||||
# Trigger based on a "port to [branch]" label on PR
|
# Trigger based on a "port to [branch]" label on PR
|
||||||
# (This label must specify the branch name to port to)
|
# (This label must specify the branch name to port to)
|
||||||
|
2
.github/workflows/pull_request_opened.yml
vendored
2
.github/workflows/pull_request_opened.yml
vendored
@@ -21,4 +21,4 @@ jobs:
|
|||||||
# Assign the PR to whomever created it. This is useful for visualizing assignments on project boards
|
# Assign the PR to whomever created it. This is useful for visualizing assignments on project boards
|
||||||
# See https://github.com/toshimaru/auto-author-assign
|
# See https://github.com/toshimaru/auto-author-assign
|
||||||
- name: Assign PR to creator
|
- name: Assign PR to creator
|
||||||
uses: toshimaru/auto-author-assign@v1.6.2
|
uses: toshimaru/auto-author-assign@v2.0.1
|
||||||
|
@@ -8,11 +8,6 @@ describe('Header', () => {
|
|||||||
cy.get('ds-header').should('be.visible');
|
cy.get('ds-header').should('be.visible');
|
||||||
|
|
||||||
// Analyze <ds-header> for accessibility
|
// Analyze <ds-header> for accessibility
|
||||||
testA11y({
|
testA11y('ds-header');
|
||||||
include: ['ds-header'],
|
|
||||||
exclude: [
|
|
||||||
['#search-navbar-container'] // search in navbar has duplicative ID. Will be fixed in #1174
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -121,7 +121,7 @@
|
|||||||
"ngx-infinite-scroll": "^15.0.0",
|
"ngx-infinite-scroll": "^15.0.0",
|
||||||
"ngx-pagination": "6.0.3",
|
"ngx-pagination": "6.0.3",
|
||||||
"ngx-sortablejs": "^11.1.0",
|
"ngx-sortablejs": "^11.1.0",
|
||||||
"ngx-ui-switch": "^14.0.3",
|
"ngx-ui-switch": "^14.1.0",
|
||||||
"nouislider": "^15.7.1",
|
"nouislider": "^15.7.1",
|
||||||
"pem": "1.14.7",
|
"pem": "1.14.7",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
<ngb-accordion #acc="ngbAccordion" [activeIds]="'browse'">
|
<ngb-accordion #acc="ngbAccordion" [activeIds]="'browse'">
|
||||||
<ngb-panel [id]="'browse'">
|
<ngb-panel [id]="'browse'">
|
||||||
<ng-template ngbPanelHeader>
|
<ng-template ngbPanelHeader>
|
||||||
<div class="w-100 d-flex justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('browse')"
|
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('browse')"
|
||||||
data-test="browse">
|
data-test="browse">
|
||||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()"
|
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()"
|
||||||
[attr.aria-expanded]="!acc.isExpanded('browse')"
|
[attr.aria-expanded]="acc.isExpanded('browse')"
|
||||||
aria-controls="collapsePanels">
|
aria-controls="bulk-access-browse-panel-content">
|
||||||
{{ 'admin.access-control.bulk-access-browse.header' | translate }}
|
{{ 'admin.access-control.bulk-access-browse.header' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<div class="text-right d-flex">
|
<div class="text-right d-flex gap-2">
|
||||||
<div class="ml-3 d-inline-block">
|
<div class="d-flex my-auto">
|
||||||
<span *ngIf="acc.isExpanded('browse')" class="fas fa-chevron-up fa-fw"></span>
|
<span *ngIf="acc.isExpanded('browse')" class="fas fa-chevron-up fa-fw"></span>
|
||||||
<span *ngIf="!acc.isExpanded('browse')" class="fas fa-chevron-down fa-fw"></span>
|
<span *ngIf="!acc.isExpanded('browse')" class="fas fa-chevron-down fa-fw"></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,51 +17,53 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template ngbPanelContent>
|
<ng-template ngbPanelContent>
|
||||||
<ul ngbNav #nav="ngbNav" [(activeId)]="activateId" class="nav-pills">
|
<div id="bulk-access-browse-panel-content">
|
||||||
<li [ngbNavItem]="'search'">
|
<ul ngbNav #nav="ngbNav" [(activeId)]="activateId" class="nav-pills">
|
||||||
<a ngbNavLink>{{'admin.access-control.bulk-access-browse.search.header' | translate}}</a>
|
<li [ngbNavItem]="'search'" role="presentation">
|
||||||
<ng-template ngbNavContent>
|
<a ngbNavLink>{{'admin.access-control.bulk-access-browse.search.header' | translate}}</a>
|
||||||
<div class="mx-n3">
|
<ng-template ngbNavContent>
|
||||||
<ds-themed-search [configuration]="'administrativeBulkAccess'"
|
<div class="mx-n3">
|
||||||
[selectable]="true"
|
<ds-themed-search [configuration]="'administrativeBulkAccess'"
|
||||||
[selectionConfig]="{ repeatable: true, listId: listId }"
|
[selectable]="true"
|
||||||
[showThumbnails]="false"></ds-themed-search>
|
[selectionConfig]="{ repeatable: true, listId: listId }"
|
||||||
</div>
|
[showThumbnails]="false"></ds-themed-search>
|
||||||
</ng-template>
|
</div>
|
||||||
</li>
|
</ng-template>
|
||||||
<li [ngbNavItem]="'selected'">
|
</li>
|
||||||
<a ngbNavLink>
|
<li [ngbNavItem]="'selected'" role="presentation">
|
||||||
{{'admin.access-control.bulk-access-browse.selected.header' | translate: {number: ((objectsSelected$ | async)?.payload?.totalElements) ? (objectsSelected$ | async)?.payload?.totalElements : '0'} }}
|
<a ngbNavLink>
|
||||||
</a>
|
{{'admin.access-control.bulk-access-browse.selected.header' | translate: {number: ((objectsSelected$ | async)?.payload?.totalElements) ? (objectsSelected$ | async)?.payload?.totalElements : '0'} }}
|
||||||
<ng-template ngbNavContent>
|
</a>
|
||||||
<ds-pagination
|
<ng-template ngbNavContent>
|
||||||
[paginationOptions]="(paginationOptions$ | async)"
|
<ds-pagination
|
||||||
[pageInfoState]="(objectsSelected$|async)?.payload.pageInfo"
|
[paginationOptions]="(paginationOptions$ | async)"
|
||||||
[collectionSize]="(objectsSelected$|async)?.payload?.totalElements"
|
[pageInfoState]="(objectsSelected$|async)?.payload.pageInfo"
|
||||||
[objects]="(objectsSelected$|async)"
|
[collectionSize]="(objectsSelected$|async)?.payload?.totalElements"
|
||||||
[showPaginator]="false"
|
[objects]="(objectsSelected$|async)"
|
||||||
(prev)="pagePrev()"
|
[showPaginator]="false"
|
||||||
(next)="pageNext()">
|
(prev)="pagePrev()"
|
||||||
<ul *ngIf="(objectsSelected$|async)?.hasSucceeded" class="list-unstyled ml-4">
|
(next)="pageNext()">
|
||||||
<li *ngFor='let object of (objectsSelected$|async)?.payload?.page | paginate: { itemsPerPage: (paginationOptions$ | async).pageSize,
|
<ul *ngIf="(objectsSelected$|async)?.hasSucceeded" class="list-unstyled ml-4">
|
||||||
currentPage: (paginationOptions$ | async).currentPage, totalItems: (objectsSelected$|async)?.payload?.page.length }; let i = index; let last = last '
|
<li *ngFor='let object of (objectsSelected$|async)?.payload?.page | paginate: { itemsPerPage: (paginationOptions$ | async).pageSize,
|
||||||
class="mt-4 mb-4 d-flex"
|
currentPage: (paginationOptions$ | async).currentPage, totalItems: (objectsSelected$|async)?.payload?.page.length }; let i = index; let last = last '
|
||||||
[attr.data-test]="'list-object' | dsBrowserOnly">
|
class="mt-4 mb-4 d-flex"
|
||||||
<ds-selectable-list-item-control [index]="i"
|
[attr.data-test]="'list-object' | dsBrowserOnly">
|
||||||
[object]="object"
|
<ds-selectable-list-item-control [index]="i"
|
||||||
[selectionConfig]="{ repeatable: true, listId: listId }"></ds-selectable-list-item-control>
|
[object]="object"
|
||||||
<ds-listable-object-component-loader [listID]="listId"
|
[selectionConfig]="{ repeatable: true, listId: listId }"></ds-selectable-list-item-control>
|
||||||
[index]="i"
|
<ds-listable-object-component-loader [listID]="listId"
|
||||||
[object]="object"
|
[index]="i"
|
||||||
[showThumbnails]="false"
|
[object]="object"
|
||||||
[viewMode]="'list'"></ds-listable-object-component-loader>
|
[showThumbnails]="false"
|
||||||
</li>
|
[viewMode]="'list'"></ds-listable-object-component-loader>
|
||||||
</ul>
|
</li>
|
||||||
</ds-pagination>
|
</ul>
|
||||||
</ng-template>
|
</ds-pagination>
|
||||||
</li>
|
</ng-template>
|
||||||
</ul>
|
</li>
|
||||||
<div [ngbNavOutlet]="nav" class="mt-5"></div>
|
</ul>
|
||||||
|
<div [ngbNavOutlet]="nav" class="mt-5"></div>
|
||||||
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ngb-panel>
|
</ngb-panel>
|
||||||
</ngb-accordion>
|
</ngb-accordion>
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
<ngb-accordion #acc="ngbAccordion" [activeIds]="'settings'">
|
<ngb-accordion #acc="ngbAccordion" [activeIds]="'settings'">
|
||||||
<ngb-panel [id]="'settings'">
|
<ngb-panel [id]="'settings'">
|
||||||
<ng-template ngbPanelHeader>
|
<ng-template ngbPanelHeader>
|
||||||
<div class="w-100 d-flex justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('settings')" data-test="settings">
|
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('settings')" data-test="settings">
|
||||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!acc.isExpanded('browse')"
|
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded('settings')"
|
||||||
aria-controls="collapsePanels">
|
aria-controls="bulk-access-settings-panel-content">
|
||||||
{{ 'admin.access-control.bulk-access-settings.header' | translate }}
|
{{ 'admin.access-control.bulk-access-settings.header' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<div class="text-right d-flex">
|
<div class="text-right d-flex gap-2">
|
||||||
<div class="ml-3 d-inline-block">
|
<div class="d-flex my-auto">
|
||||||
<span *ngIf="acc.isExpanded('settings')" class="fas fa-chevron-up fa-fw"></span>
|
<span *ngIf="acc.isExpanded('settings')" class="fas fa-chevron-up fa-fw"></span>
|
||||||
<span *ngIf="!acc.isExpanded('settings')" class="fas fa-chevron-down fa-fw"></span>
|
<span *ngIf="!acc.isExpanded('settings')" class="fas fa-chevron-down fa-fw"></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template ngbPanelContent>
|
<ng-template ngbPanelContent>
|
||||||
<ds-access-control-form-container #dsAccessControlForm [showSubmit]="false"></ds-access-control-form-container>
|
<ds-access-control-form-container id="bulk-access-settings-panel-content" #dsAccessControlForm [showSubmit]="false"></ds-access-control-form-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ngb-panel>
|
</ngb-panel>
|
||||||
</ngb-accordion>
|
</ngb-accordion>
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<div class="epeople-registry row">
|
<div class="epeople-registry row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-flex justify-content-between border-bottom mb-3">
|
<div class="d-flex justify-content-between border-bottom mb-3">
|
||||||
<h2 id="header" class="pb-2">{{labelPrefix + 'head' | translate}}</h2>
|
<h1 id="header" class="pb-2">{{labelPrefix + 'head' | translate}}</h1>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button class="mr-auto btn btn-success addEPerson-button"
|
<button class="mr-auto btn btn-success addEPerson-button"
|
||||||
@@ -13,9 +13,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 id="search" class="border-bottom pb-2">{{labelPrefix + 'search.head' | translate}}
|
<h2 id="search" class="border-bottom pb-2">
|
||||||
|
{{labelPrefix + 'search.head' | translate}}
|
||||||
</h3>
|
</h2>
|
||||||
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
||||||
<div>
|
<div>
|
||||||
<select name="scope" id="scope" formControlName="scope" class="form-control" aria-label="Search scope">
|
<select name="scope" id="scope" formControlName="scope" class="form-control" aria-label="Search scope">
|
||||||
|
@@ -5,11 +5,11 @@
|
|||||||
<div *ngIf="epersonService.getActiveEPerson() | async; then editHeader; else createHeader"></div>
|
<div *ngIf="epersonService.getActiveEPerson() | async; then editHeader; else createHeader"></div>
|
||||||
|
|
||||||
<ng-template #createHeader>
|
<ng-template #createHeader>
|
||||||
<h2 class="border-bottom pb-2">{{messagePrefix + '.create' | translate}}</h2>
|
<h1 class="border-bottom pb-2">{{messagePrefix + '.create' | translate}}</h1>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #editHeader>
|
<ng-template #editHeader>
|
||||||
<h2 class="border-bottom pb-2">{{messagePrefix + '.edit' | translate}}</h2>
|
<h1 class="border-bottom pb-2">{{messagePrefix + '.edit' | translate}}</h1>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ds-form [formId]="formId"
|
<ds-form [formId]="formId"
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<ds-themed-loading [showMessage]="false" *ngIf="!formGroup"></ds-themed-loading>
|
<ds-themed-loading [showMessage]="false" *ngIf="!formGroup"></ds-themed-loading>
|
||||||
|
|
||||||
<div *ngIf="epersonService.getActiveEPerson() | async">
|
<div *ngIf="epersonService.getActiveEPerson() | async">
|
||||||
<h5>{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}</h5>
|
<h2>{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}</h2>
|
||||||
|
|
||||||
<ds-themed-loading [showMessage]="false" *ngIf="!(groups | async)"></ds-themed-loading>
|
<ds-themed-loading [showMessage]="false" *ngIf="!(groups | async)"></ds-themed-loading>
|
||||||
|
|
||||||
|
@@ -5,11 +5,11 @@
|
|||||||
<div *ngIf="groupDataService.getActiveGroup() | async; then editHeader; else createHeader"></div>
|
<div *ngIf="groupDataService.getActiveGroup() | async; then editHeader; else createHeader"></div>
|
||||||
|
|
||||||
<ng-template #createHeader>
|
<ng-template #createHeader>
|
||||||
<h2 class="border-bottom pb-2">{{messagePrefix + '.head.create' | translate}}</h2>
|
<h1 class="border-bottom pb-2">{{messagePrefix + '.head.create' | translate}}</h1>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #editHeader>
|
<ng-template #editHeader>
|
||||||
<h2 class="border-bottom pb-2">
|
<h1 class="border-bottom pb-2">
|
||||||
<span
|
<span
|
||||||
*dsContextHelp="{
|
*dsContextHelp="{
|
||||||
content: 'admin.access-control.groups.form.tooltip.editGroupPage',
|
content: 'admin.access-control.groups.form.tooltip.editGroupPage',
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
>
|
>
|
||||||
{{messagePrefix + '.head.edit' | translate}}
|
{{messagePrefix + '.head.edit' | translate}}
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</h1>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ds-alert *ngIf="groupBeingEdited?.permanent" [type]="AlertTypeEnum.Warning"
|
<ds-alert *ngIf="groupBeingEdited?.permanent" [type]="AlertTypeEnum.Warning"
|
||||||
@@ -39,9 +39,8 @@
|
|||||||
<button (click)="onCancel()" type="button"
|
<button (click)="onCancel()" type="button"
|
||||||
class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}}</button>
|
class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<div after *ngIf="(canEdit$ | async) && !groupBeingEdited.permanent" class="btn-group">
|
<div after *ngIf="(canEdit$ | async) && !groupBeingEdited?.permanent" class="btn-group">
|
||||||
<button class="btn btn-danger delete-button" [disabled]="!(canEdit$ | async) || groupBeingEdited.permanent"
|
<button (click)="delete()" class="btn btn-danger delete-button" type="button">
|
||||||
(click)="delete()" type="button">
|
|
||||||
<i class="fa fa-trash"></i> {{ messagePrefix + '.actions.delete' | translate}}
|
<i class="fa fa-trash"></i> {{ messagePrefix + '.actions.delete' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<ng-container>
|
<ng-container>
|
||||||
<h3 class="border-bottom pb-2">{{messagePrefix + '.head' | translate}}</h3>
|
<h2 class="border-bottom pb-2">{{messagePrefix + '.head' | translate}}</h2>
|
||||||
|
|
||||||
<h4>{{messagePrefix + '.headMembers' | translate}}</h4>
|
<h3>{{messagePrefix + '.headMembers' | translate}}</h3>
|
||||||
|
|
||||||
<ds-pagination *ngIf="(ePeopleMembersOfGroup | async)?.totalElements > 0"
|
<ds-pagination *ngIf="(ePeopleMembersOfGroup | async)?.totalElements > 0"
|
||||||
[paginationOptions]="config"
|
[paginationOptions]="config"
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
{{messagePrefix + '.no-members-yet' | translate}}
|
{{messagePrefix + '.no-members-yet' | translate}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 id="search" class="border-bottom pb-2">
|
<h3 id="search" class="border-bottom pb-2">
|
||||||
<span
|
<span
|
||||||
*dsContextHelp="{
|
*dsContextHelp="{
|
||||||
content: 'admin.access-control.groups.form.tooltip.editGroup.addEpeople',
|
content: 'admin.access-control.groups.form.tooltip.editGroup.addEpeople',
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
>
|
>
|
||||||
{{messagePrefix + '.search.head' | translate}}
|
{{messagePrefix + '.search.head' | translate}}
|
||||||
</span>
|
</span>
|
||||||
</h4>
|
</h3>
|
||||||
|
|
||||||
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
||||||
<div class="flex-grow-1 mr-3">
|
<div class="flex-grow-1 mr-3">
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<div class="groups-registry row">
|
<div class="groups-registry row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-flex justify-content-between border-bottom mb-3">
|
<div class="d-flex justify-content-between border-bottom mb-3">
|
||||||
<h2 id="header" class="pb-2">{{messagePrefix + 'head' | translate}}</h2>
|
<h1 id="header" class="pb-2">{{messagePrefix + 'head' | translate}}</h1>
|
||||||
<div>
|
<div>
|
||||||
<button class="mr-auto btn btn-success"
|
<button class="mr-auto btn btn-success"
|
||||||
[routerLink]="'create'">
|
[routerLink]="'create'">
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 id="search" class="border-bottom pb-2">{{messagePrefix + 'search.head' | translate}}</h3>
|
<h2 id="search" class="border-bottom pb-2">{{messagePrefix + 'search.head' | translate}}</h2>
|
||||||
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
|
||||||
<div class="flex-grow-1 mr-3">
|
<div class="flex-grow-1 mr-3">
|
||||||
<div class="form-group input-group">
|
<div class="form-group input-group">
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h2>{{'admin.curation-tasks.header' |translate }}</h2>
|
<h1>{{'admin.curation-tasks.header' |translate }}</h1>
|
||||||
<ds-curation-form></ds-curation-form>
|
<ds-curation-form></ds-curation-form>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h2 id="header">{{'admin.metadata-import.page.header' | translate}}</h2>
|
<h1 id="header">{{'admin.metadata-import.page.header' | translate}}</h1>
|
||||||
<p>{{'admin.metadata-import.page.help' | translate}}</p>
|
<p>{{'admin.metadata-import.page.help' | translate}}</p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||||
|
import { getNotificationsModuleRoute } from '../admin-routing-paths';
|
||||||
|
|
||||||
|
export const QUALITY_ASSURANCE_EDIT_PATH = 'quality-assurance';
|
||||||
|
|
||||||
|
export function getQualityAssuranceRoute(id: string) {
|
||||||
|
return new URLCombiner(getNotificationsModuleRoute(), QUALITY_ASSURANCE_EDIT_PATH, id).toString();
|
||||||
|
}
|
@@ -0,0 +1,87 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
import { AuthenticatedGuard } from '../../core/auth/authenticated.guard';
|
||||||
|
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
|
import { I18nBreadcrumbsService } from '../../core/breadcrumbs/i18n-breadcrumbs.service';
|
||||||
|
import { QUALITY_ASSURANCE_EDIT_PATH } from './admin-notifications-routing-paths';
|
||||||
|
import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page.component';
|
||||||
|
import { AdminQualityAssuranceEventsPageComponent } from './admin-quality-assurance-events-page/admin-quality-assurance-events-page.component';
|
||||||
|
import { AdminQualityAssuranceTopicsPageResolver } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page-resolver.service';
|
||||||
|
import { AdminQualityAssuranceEventsPageResolver } from './admin-quality-assurance-events-page/admin-quality-assurance-events-page.resolver';
|
||||||
|
import { AdminQualityAssuranceSourcePageComponent } from './admin-quality-assurance-source-page-component/admin-quality-assurance-source-page.component';
|
||||||
|
import { AdminQualityAssuranceSourcePageResolver } from './admin-quality-assurance-source-page-component/admin-quality-assurance-source-page-resolver.service';
|
||||||
|
import { QualityAssuranceBreadcrumbResolver } from '../../core/breadcrumbs/quality-assurance-breadcrumb.resolver';
|
||||||
|
import { QualityAssuranceBreadcrumbService } from '../../core/breadcrumbs/quality-assurance-breadcrumb.service';
|
||||||
|
import {
|
||||||
|
SourceDataResolver
|
||||||
|
} from './admin-quality-assurance-source-page-component/admin-quality-assurance-source-data.resolver';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild([
|
||||||
|
{
|
||||||
|
canActivate: [ AuthenticatedGuard ],
|
||||||
|
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId`,
|
||||||
|
component: AdminQualityAssuranceTopicsPageComponent,
|
||||||
|
pathMatch: 'full',
|
||||||
|
resolve: {
|
||||||
|
breadcrumb: QualityAssuranceBreadcrumbResolver,
|
||||||
|
openaireQualityAssuranceTopicsParams: AdminQualityAssuranceTopicsPageResolver
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'admin.quality-assurance.page.title',
|
||||||
|
breadcrumbKey: 'admin.quality-assurance',
|
||||||
|
showBreadcrumbsFluid: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
canActivate: [ AuthenticatedGuard ],
|
||||||
|
path: `${QUALITY_ASSURANCE_EDIT_PATH}`,
|
||||||
|
component: AdminQualityAssuranceSourcePageComponent,
|
||||||
|
pathMatch: 'full',
|
||||||
|
resolve: {
|
||||||
|
breadcrumb: I18nBreadcrumbResolver,
|
||||||
|
openaireQualityAssuranceSourceParams: AdminQualityAssuranceSourcePageResolver,
|
||||||
|
sourceData: SourceDataResolver
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'admin.notifications.source.breadcrumbs',
|
||||||
|
breadcrumbKey: 'admin.notifications.source',
|
||||||
|
showBreadcrumbsFluid: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
canActivate: [ AuthenticatedGuard ],
|
||||||
|
path: `${QUALITY_ASSURANCE_EDIT_PATH}/:sourceId/:topicId`,
|
||||||
|
component: AdminQualityAssuranceEventsPageComponent,
|
||||||
|
pathMatch: 'full',
|
||||||
|
resolve: {
|
||||||
|
breadcrumb: QualityAssuranceBreadcrumbResolver,
|
||||||
|
openaireQualityAssuranceEventsParams: AdminQualityAssuranceEventsPageResolver
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: 'admin.notifications.event.page.title',
|
||||||
|
breadcrumbKey: 'admin.notifications.event',
|
||||||
|
showBreadcrumbsFluid: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
I18nBreadcrumbResolver,
|
||||||
|
I18nBreadcrumbsService,
|
||||||
|
SourceDataResolver,
|
||||||
|
AdminQualityAssuranceTopicsPageResolver,
|
||||||
|
AdminQualityAssuranceEventsPageResolver,
|
||||||
|
AdminQualityAssuranceSourcePageResolver,
|
||||||
|
QualityAssuranceBreadcrumbResolver,
|
||||||
|
QualityAssuranceBreadcrumbService
|
||||||
|
]
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* Routing module for the Notifications section of the admin sidebar
|
||||||
|
*/
|
||||||
|
export class AdminNotificationsRoutingModule {
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CoreModule } from '../../core/core.module';
|
||||||
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
|
import { AdminNotificationsRoutingModule } from './admin-notifications-routing.module';
|
||||||
|
import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page/admin-quality-assurance-topics-page.component';
|
||||||
|
import { AdminQualityAssuranceEventsPageComponent } from './admin-quality-assurance-events-page/admin-quality-assurance-events-page.component';
|
||||||
|
import { AdminQualityAssuranceSourcePageComponent } from './admin-quality-assurance-source-page-component/admin-quality-assurance-source-page.component';
|
||||||
|
import {NotificationsModule} from '../../notifications/notifications.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SharedModule,
|
||||||
|
CoreModule.forRoot(),
|
||||||
|
AdminNotificationsRoutingModule,
|
||||||
|
NotificationsModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AdminQualityAssuranceTopicsPageComponent,
|
||||||
|
AdminQualityAssuranceEventsPageComponent,
|
||||||
|
AdminQualityAssuranceSourcePageComponent
|
||||||
|
],
|
||||||
|
entryComponents: []
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* This module handles all components related to the notifications pages
|
||||||
|
*/
|
||||||
|
export class AdminNotificationsModule {
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
<ds-quality-assurance-events></ds-quality-assurance-events>
|
@@ -0,0 +1,26 @@
|
|||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { AdminQualityAssuranceEventsPageComponent } from './admin-quality-assurance-events-page.component';
|
||||||
|
|
||||||
|
describe('AdminQualityAssuranceEventsPageComponent', () => {
|
||||||
|
let component: AdminQualityAssuranceEventsPageComponent;
|
||||||
|
let fixture: ComponentFixture<AdminQualityAssuranceEventsPageComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ AdminQualityAssuranceEventsPageComponent ],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AdminQualityAssuranceEventsPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create AdminQualityAssuranceEventsPageComponent', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component for the page that show the QA events related to a specific topic.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-quality-assurance-events-page',
|
||||||
|
templateUrl: './admin-quality-assurance-events-page.component.html'
|
||||||
|
})
|
||||||
|
export class AdminQualityAssuranceEventsPageComponent {
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for the route parameters.
|
||||||
|
*/
|
||||||
|
export interface AdminQualityAssuranceEventsPageParams {
|
||||||
|
pageId?: string;
|
||||||
|
pageSize?: number;
|
||||||
|
currentPage?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a resolver that retrieve the route data before the route is activated.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AdminQualityAssuranceEventsPageResolver implements Resolve<AdminQualityAssuranceEventsPageParams> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for resolving the parameters in the current route.
|
||||||
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @returns AdminQualityAssuranceEventsPageParams Emits the route parameters
|
||||||
|
*/
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): AdminQualityAssuranceEventsPageParams {
|
||||||
|
return {
|
||||||
|
pageId: route.queryParams.pageId,
|
||||||
|
pageSize: parseInt(route.queryParams.pageSize, 10),
|
||||||
|
currentPage: parseInt(route.queryParams.page, 10)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,47 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot, Router } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||||
|
import { QualityAssuranceSourceObject } from '../../../core/notifications/qa/models/quality-assurance-source.model';
|
||||||
|
import { QualityAssuranceSourceService } from '../../../notifications/qa/source/quality-assurance-source.service';
|
||||||
|
import {environment} from '../../../../environments/environment';
|
||||||
|
/**
|
||||||
|
* This class represents a resolver that retrieve the route data before the route is activated.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class SourceDataResolver implements Resolve<Observable<QualityAssuranceSourceObject[]>> {
|
||||||
|
private pageSize = environment.qualityAssuranceConfig.pageSize;
|
||||||
|
/**
|
||||||
|
* Initialize the effect class variables.
|
||||||
|
* @param {QualityAssuranceSourceService} qualityAssuranceSourceService
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private qualityAssuranceSourceService: QualityAssuranceSourceService,
|
||||||
|
private router: Router
|
||||||
|
) { }
|
||||||
|
/**
|
||||||
|
* Method for resolving the parameters in the current route.
|
||||||
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @returns Observable<QualityAssuranceSourceObject[]>
|
||||||
|
*/
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<QualityAssuranceSourceObject[]> {
|
||||||
|
return this.qualityAssuranceSourceService.getSources(this.pageSize, 0).pipe(
|
||||||
|
map((sources: PaginatedList<QualityAssuranceSourceObject>) => {
|
||||||
|
if (sources.page.length === 1) {
|
||||||
|
this.router.navigate([this.getResolvedUrl(route) + '/' + sources.page[0].id]);
|
||||||
|
}
|
||||||
|
return sources.page;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param route url path
|
||||||
|
* @returns url path
|
||||||
|
*/
|
||||||
|
getResolvedUrl(route: ActivatedRouteSnapshot): string {
|
||||||
|
return route.pathFromRoot.map(v => v.url.map(segment => segment.toString()).join('/')).join('/');
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for the route parameters.
|
||||||
|
*/
|
||||||
|
export interface AdminQualityAssuranceSourcePageParams {
|
||||||
|
pageId?: string;
|
||||||
|
pageSize?: number;
|
||||||
|
currentPage?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a resolver that retrieve the route data before the route is activated.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AdminQualityAssuranceSourcePageResolver implements Resolve<AdminQualityAssuranceSourcePageParams> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for resolving the parameters in the current route.
|
||||||
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @returns AdminQualityAssuranceSourcePageParams Emits the route parameters
|
||||||
|
*/
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): AdminQualityAssuranceSourcePageParams {
|
||||||
|
return {
|
||||||
|
pageId: route.queryParams.pageId,
|
||||||
|
pageSize: parseInt(route.queryParams.pageSize, 10),
|
||||||
|
currentPage: parseInt(route.queryParams.page, 10)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
<ds-quality-assurance-source></ds-quality-assurance-source>
|
@@ -0,0 +1,27 @@
|
|||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AdminQualityAssuranceSourcePageComponent } from './admin-quality-assurance-source-page.component';
|
||||||
|
|
||||||
|
describe('AdminQualityAssuranceSourcePageComponent', () => {
|
||||||
|
let component: AdminQualityAssuranceSourcePageComponent;
|
||||||
|
let fixture: ComponentFixture<AdminQualityAssuranceSourcePageComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ AdminQualityAssuranceSourcePageComponent ],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AdminQualityAssuranceSourcePageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create AdminQualityAssuranceSourcePageComponent', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component for the page that show the QA sources.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-admin-quality-assurance-source-page-component',
|
||||||
|
templateUrl: './admin-quality-assurance-source-page.component.html',
|
||||||
|
})
|
||||||
|
export class AdminQualityAssuranceSourcePageComponent {}
|
@@ -0,0 +1,32 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for the route parameters.
|
||||||
|
*/
|
||||||
|
export interface AdminQualityAssuranceTopicsPageParams {
|
||||||
|
pageId?: string;
|
||||||
|
pageSize?: number;
|
||||||
|
currentPage?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a resolver that retrieve the route data before the route is activated.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AdminQualityAssuranceTopicsPageResolver implements Resolve<AdminQualityAssuranceTopicsPageParams> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for resolving the parameters in the current route.
|
||||||
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @returns AdminQualityAssuranceTopicsPageParams Emits the route parameters
|
||||||
|
*/
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): AdminQualityAssuranceTopicsPageParams {
|
||||||
|
return {
|
||||||
|
pageId: route.queryParams.pageId,
|
||||||
|
pageSize: parseInt(route.queryParams.pageSize, 10),
|
||||||
|
currentPage: parseInt(route.queryParams.page, 10)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
<ds-quality-assurance-topic></ds-quality-assurance-topic>
|
@@ -0,0 +1,26 @@
|
|||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { AdminQualityAssuranceTopicsPageComponent } from './admin-quality-assurance-topics-page.component';
|
||||||
|
|
||||||
|
describe('AdminQualityAssuranceTopicsPageComponent', () => {
|
||||||
|
let component: AdminQualityAssuranceTopicsPageComponent;
|
||||||
|
let fixture: ComponentFixture<AdminQualityAssuranceTopicsPageComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ AdminQualityAssuranceTopicsPageComponent ],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AdminQualityAssuranceTopicsPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create AdminQualityAssuranceTopicsPageComponent', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component for the page that show the QA topics related to a specific source.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-notification-qa-page',
|
||||||
|
templateUrl: './admin-quality-assurance-topics-page.component.html'
|
||||||
|
})
|
||||||
|
export class AdminQualityAssuranceTopicsPageComponent {
|
||||||
|
|
||||||
|
}
|
@@ -1,11 +1,11 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 mb-4">
|
<div class="col-12 mb-4">
|
||||||
<h2 id="sub-header"
|
<h1 id="sub-header"
|
||||||
class="border-bottom mb-2">{{ 'admin.registries.bitstream-formats.create.new' | translate }}</h2>
|
class="border-bottom pb-2">{{ 'admin.registries.bitstream-formats.create.new' | translate }}</h1>
|
||||||
|
|
||||||
<ds-bitstream-format-form (updatedFormat)="createBitstreamFormat($event)"></ds-bitstream-format-form>
|
<ds-bitstream-format-form (updatedFormat)="createBitstreamFormat($event)"></ds-bitstream-format-form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<div class="bitstream-formats row">
|
<div class="bitstream-formats row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
|
||||||
<h2 id="header" class="border-bottom pb-2 ">{{'admin.registries.bitstream-formats.head' | translate}}</h2>
|
<h1 id="header" class="border-bottom pb-2">{{'admin.registries.bitstream-formats.head' | translate}}</h1>
|
||||||
|
|
||||||
<p id="description">{{'admin.registries.bitstream-formats.description' | translate}}</p>
|
<p id="description">{{'admin.registries.bitstream-formats.description' | translate}}</p>
|
||||||
<p id="create-new" class="mb-2"><a [routerLink]="'add'" class="btn btn-success">{{'admin.registries.bitstream-formats.create.new' | translate}}</a></p>
|
<p id="create-new" class="mb-2"><a [routerLink]="'add'" class="btn btn-success">{{'admin.registries.bitstream-formats.create.new' | translate}}</a></p>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<table id="formats" class="table table-striped table-hover">
|
<table id="formats" class="table table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col"></th>
|
<th scope="col" [attr.aria-label]="'admin.registries.bitstream-formats.select' | translate"></th>
|
||||||
<th scope="col">{{'admin.registries.bitstream-formats.table.id' | translate}}</th>
|
<th scope="col">{{'admin.registries.bitstream-formats.table.id' | translate}}</th>
|
||||||
<th scope="col">{{'admin.registries.bitstream-formats.table.name' | translate}}</th>
|
<th scope="col">{{'admin.registries.bitstream-formats.table.name' | translate}}</th>
|
||||||
<th scope="col">{{'admin.registries.bitstream-formats.table.mimetype' | translate}}</th>
|
<th scope="col">{{'admin.registries.bitstream-formats.table.mimetype' | translate}}</th>
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<label class="mb-0">
|
<label class="mb-0">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
|
[attr.aria-label]="'admin.registries.bitstream-formats.select' | translate"
|
||||||
[checked]="isSelected(bitstreamFormat) | async"
|
[checked]="isSelected(bitstreamFormat) | async"
|
||||||
(change)="selectBitStreamFormat(bitstreamFormat, $event)"
|
(change)="selectBitStreamFormat(bitstreamFormat, $event)"
|
||||||
>
|
>
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 mb-4">
|
<div class="col-12 mb-4">
|
||||||
<h2 id="sub-header"
|
<h1 id="sub-header"
|
||||||
class="border-bottom mb-2">{{'admin.registries.bitstream-formats.edit.head' | translate:{format: (bitstreamFormatRD$ | async)?.payload.shortDescription} }}</h2>
|
class="border-bottom pb-2">{{'admin.registries.bitstream-formats.edit.head' | translate:{format: (bitstreamFormatRD$ | async)?.payload.shortDescription} }}</h1>
|
||||||
|
|
||||||
<ds-bitstream-format-form [bitstreamFormat]="(bitstreamFormatRD$ | async)?.payload" (updatedFormat)="updateFormat($event)"></ds-bitstream-format-form>
|
<ds-bitstream-format-form [bitstreamFormat]="(bitstreamFormatRD$ | async)?.payload" (updatedFormat)="updateFormat($event)"></ds-bitstream-format-form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { RegistryService } from '../../../core/registry/registry.service';
|
import { RegistryService } from '../../../core/registry/registry.service';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import {
|
import {
|
||||||
BehaviorSubject,
|
BehaviorSubject,
|
||||||
combineLatest as observableCombineLatest,
|
combineLatest as observableCombineLatest,
|
||||||
@@ -32,7 +32,7 @@ import { PaginationService } from '../../../core/pagination/pagination.service';
|
|||||||
* A component used for managing all existing metadata fields within the current metadata schema.
|
* A component used for managing all existing metadata fields within the current metadata schema.
|
||||||
* The admin can create, edit or delete metadata fields here.
|
* The admin can create, edit or delete metadata fields here.
|
||||||
*/
|
*/
|
||||||
export class MetadataSchemaComponent implements OnInit {
|
export class MetadataSchemaComponent implements OnInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* The metadata schema
|
* The metadata schema
|
||||||
*/
|
*/
|
||||||
@@ -60,7 +60,6 @@ export class MetadataSchemaComponent implements OnInit {
|
|||||||
constructor(private registryService: RegistryService,
|
constructor(private registryService: RegistryService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
private router: Router,
|
|
||||||
private paginationService: PaginationService,
|
private paginationService: PaginationService,
|
||||||
private translateService: TranslateService) {
|
private translateService: TranslateService) {
|
||||||
|
|
||||||
@@ -86,7 +85,7 @@ export class MetadataSchemaComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
private updateFields() {
|
private updateFields() {
|
||||||
this.metadataFields$ = this.paginationService.getCurrentPagination(this.config.id, this.config).pipe(
|
this.metadataFields$ = this.paginationService.getCurrentPagination(this.config.id, this.config).pipe(
|
||||||
switchMap((currentPagination) => combineLatest(this.metadataSchema$, this.needsUpdate$, observableOf(currentPagination))),
|
switchMap((currentPagination) => combineLatest([this.metadataSchema$, this.needsUpdate$, observableOf(currentPagination)])),
|
||||||
switchMap(([schema, update, currentPagination]: [MetadataSchema, boolean, PaginationComponentOptions]) => {
|
switchMap(([schema, update, currentPagination]: [MetadataSchema, boolean, PaginationComponentOptions]) => {
|
||||||
if (update) {
|
if (update) {
|
||||||
this.needsUpdate$.next(false);
|
this.needsUpdate$.next(false);
|
||||||
@@ -193,10 +192,10 @@ export class MetadataSchemaComponent implements OnInit {
|
|||||||
showNotification(success: boolean, amount: number) {
|
showNotification(success: boolean, amount: number) {
|
||||||
const prefix = 'admin.registries.schema.notification';
|
const prefix = 'admin.registries.schema.notification';
|
||||||
const suffix = success ? 'success' : 'failure';
|
const suffix = success ? 'success' : 'failure';
|
||||||
const messages = observableCombineLatest(
|
const messages = observableCombineLatest([
|
||||||
this.translateService.get(success ? `${prefix}.${suffix}` : `${prefix}.${suffix}`),
|
this.translateService.get(success ? `${prefix}.${suffix}` : `${prefix}.${suffix}`),
|
||||||
this.translateService.get(`${prefix}.field.deleted.${suffix}`, { amount: amount })
|
this.translateService.get(`${prefix}.field.deleted.${suffix}`, { amount: amount })
|
||||||
);
|
]);
|
||||||
messages.subscribe(([head, content]) => {
|
messages.subscribe(([head, content]) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
this.notificationsService.success(head, content);
|
this.notificationsService.success(head, content);
|
||||||
@@ -207,6 +206,7 @@ export class MetadataSchemaComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.paginationService.clearPagination(this.config.id);
|
this.paginationService.clearPagination(this.config.id);
|
||||||
|
this.registryService.deselectAllMetadataField();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,11 +2,15 @@ import { URLCombiner } from '../core/url-combiner/url-combiner';
|
|||||||
import { getAdminModuleRoute } from '../app-routing-paths';
|
import { getAdminModuleRoute } from '../app-routing-paths';
|
||||||
|
|
||||||
export const REGISTRIES_MODULE_PATH = 'registries';
|
export const REGISTRIES_MODULE_PATH = 'registries';
|
||||||
|
export const NOTIFICATIONS_MODULE_PATH = 'notifications';
|
||||||
|
|
||||||
export function getRegistriesModuleRoute() {
|
export function getRegistriesModuleRoute() {
|
||||||
return new URLCombiner(getAdminModuleRoute(), REGISTRIES_MODULE_PATH).toString();
|
return new URLCombiner(getAdminModuleRoute(), REGISTRIES_MODULE_PATH).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNotificationsModuleRoute() {
|
||||||
|
return new URLCombiner(getAdminModuleRoute(), NOTIFICATIONS_MODULE_PATH).toString();
|
||||||
|
|
||||||
export const REPORTS_MODULE_PATH = 'reports';
|
export const REPORTS_MODULE_PATH = 'reports';
|
||||||
|
|
||||||
export function getReportsModuleRoute() {
|
export function getReportsModuleRoute() {
|
||||||
|
@@ -6,12 +6,17 @@ import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso
|
|||||||
import { AdminWorkflowPageComponent } from './admin-workflow-page/admin-workflow-page.component';
|
import { AdminWorkflowPageComponent } from './admin-workflow-page/admin-workflow-page.component';
|
||||||
import { I18nBreadcrumbsService } from '../core/breadcrumbs/i18n-breadcrumbs.service';
|
import { I18nBreadcrumbsService } from '../core/breadcrumbs/i18n-breadcrumbs.service';
|
||||||
import { AdminCurationTasksComponent } from './admin-curation-tasks/admin-curation-tasks.component';
|
import { AdminCurationTasksComponent } from './admin-curation-tasks/admin-curation-tasks.component';
|
||||||
import { REGISTRIES_MODULE_PATH, REPORTS_MODULE_PATH } from './admin-routing-paths';
|
import { REGISTRIES_MODULE_PATH, NOTIFICATIONS_MODULE_PATH, REPORTS_MODULE_PATH } from './admin-routing-paths';
|
||||||
import { BatchImportPageComponent } from './admin-import-batch-page/batch-import-page.component';
|
import { BatchImportPageComponent } from './admin-import-batch-page/batch-import-page.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
|
{
|
||||||
|
path: NOTIFICATIONS_MODULE_PATH,
|
||||||
|
loadChildren: () => import('./admin-notifications/admin-notifications.module')
|
||||||
|
.then((m) => m.AdminNotificationsModule),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: REGISTRIES_MODULE_PATH,
|
path: REGISTRIES_MODULE_PATH,
|
||||||
loadChildren: () => import('./admin-registries/admin-registries.module')
|
loadChildren: () => import('./admin-registries/admin-registries.module')
|
||||||
|
@@ -34,22 +34,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="navbar-nav">
|
<div class="navbar-nav">
|
||||||
<div class="sidebar-section" id="sidebar-collapse-toggle">
|
<div class="sidebar-section" id="sidebar-collapse-toggle">
|
||||||
<a class="nav-item nav-link sidebar-section d-flex flex-row flex-nowrap"
|
<button class="nav-item nav-link sidebar-section d-flex flex-row flex-nowrap border-0" type="button"
|
||||||
href="javascript:void(0);"
|
|
||||||
(click)="toggle($event)"
|
(click)="toggle($event)"
|
||||||
(keyup.space)="toggle($event)"
|
(keyup.space)="toggle($event)"
|
||||||
>
|
>
|
||||||
<div class="shortcut-icon">
|
<span class="shortcut-icon">
|
||||||
<i *ngIf="(menuCollapsed | async)" class="fas fa-fw fa-angle-double-right"
|
<i *ngIf="(menuCollapsed | async)" class="fas fa-fw fa-angle-double-right"
|
||||||
[title]="'menu.section.icon.pin' | translate"></i>
|
[title]="'menu.section.icon.pin' | translate"></i>
|
||||||
<i *ngIf="!(menuCollapsed | async)" class="fas fa-fw fa-angle-double-left"
|
<i *ngIf="!(menuCollapsed | async)" class="fas fa-fw fa-angle-double-left"
|
||||||
[title]="'menu.section.icon.unpin' | translate"></i>
|
[title]="'menu.section.icon.unpin' | translate"></i>
|
||||||
</div>
|
</span>
|
||||||
<div class="sidebar-collapsible">
|
<span class="sidebar-collapsible text-left">
|
||||||
<span *ngIf="menuCollapsed | async" class="section-header-text">{{'menu.section.pin' | translate }}</span>
|
<span *ngIf="menuCollapsed | async" class="section-header-text">{{'menu.section.pin' | translate }}</span>
|
||||||
<span *ngIf="!(menuCollapsed | async)" class="section-header-text">{{'menu.section.unpin' | translate }}</span>
|
<span *ngIf="!(menuCollapsed | async)" class="section-header-text">{{'menu.section.unpin' | translate }}</span>
|
||||||
</div>
|
</span>
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
@@ -143,7 +143,7 @@ describe('AdminSidebarComponent', () => {
|
|||||||
describe('when the collapse link is clicked', () => {
|
describe('when the collapse link is clicked', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(menuService, 'toggleMenu');
|
spyOn(menuService, 'toggleMenu');
|
||||||
const sidebarToggler = fixture.debugElement.query(By.css('#sidebar-collapse-toggle > a'));
|
const sidebarToggler = fixture.debugElement.query(By.css('#sidebar-collapse-toggle > button'));
|
||||||
sidebarToggler.triggerEventHandler('click', {
|
sidebarToggler.triggerEventHandler('click', {
|
||||||
preventDefault: () => {/**/
|
preventDefault: () => {/**/
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h3>{{'bitstream.download.page' | translate:{ bitstream: dsoNameService.getName((bitstream$ | async)) } }}</h3>
|
<h1 class="h2">{{'bitstream.download.page' | translate:{ bitstream: dsoNameService.getName((bitstream$ | async)) } }}</h1>
|
||||||
<div class="pt-3">
|
<div class="pt-3">
|
||||||
<button (click)="back()" class="btn btn-outline-secondary">
|
<button (click)="back()" class="btn btn-outline-secondary">
|
||||||
<i class="fas fa-arrow-left"></i> {{'bitstream.download.page.back' | translate}}
|
<i class="fas fa-arrow-left"></i> {{'bitstream.download.page.back' | translate}}
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h3>{{dsoNameService.getName(bitstreamRD?.payload)}} <span class="text-muted">({{bitstreamRD?.payload?.sizeBytes | dsFileSize}})</span></h3>
|
<h1 class="h2">{{dsoNameService.getName(bitstreamRD?.payload)}} <span class="text-muted">({{bitstreamRD?.payload?.sizeBytes | dsFileSize}})</span></h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
|
<h1>{{ ('browse.taxonomy_' + vocabularyName + '.title') | translate }}</h1>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<ds-vocabulary-treeview [vocabularyOptions]=vocabularyOptions
|
<ds-vocabulary-treeview [vocabularyOptions]=vocabularyOptions
|
||||||
[multiSelect]="true"
|
[multiSelect]="true"
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
<p>{{'collection.edit.item-mapper.description' | translate}}</p>
|
<p>{{'collection.edit.item-mapper.description' | translate}}</p>
|
||||||
|
|
||||||
<ul ngbNav (navChange)="tabChange($event)" [destroyOnHide]="true" #tabs="ngbNav" class="nav-tabs">
|
<ul ngbNav (navChange)="tabChange($event)" [destroyOnHide]="true" #tabs="ngbNav" class="nav-tabs">
|
||||||
<li [ngbNavItem]="'browseTab'">
|
<li [ngbNavItem]="'browseTab'" role="presentation">
|
||||||
<a ngbNavLink>{{'collection.edit.item-mapper.tabs.browse' | translate}}</a>
|
<a ngbNavLink>{{'collection.edit.item-mapper.tabs.browse' | translate}}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li [ngbNavItem]="'mapTab'">
|
<li [ngbNavItem]="'mapTab'" role="presentation">
|
||||||
<a ngbNavLink>{{'collection.edit.item-mapper.tabs.map' | translate}}</a>
|
<a ngbNavLink>{{'collection.edit.item-mapper.tabs.map' | translate}}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<ng-container *ngVar="(dsoRD$ | async)?.payload as dso">
|
<ng-container *ngVar="(dsoRD$ | async)?.payload as dso">
|
||||||
<div class="col-12 pb-4">
|
<div class="col-12 pb-4">
|
||||||
<h2 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h2>
|
<h1 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h1>
|
||||||
<p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
|
<p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col text-right space-children-mr">
|
<div class="col text-right space-children-mr">
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h3>{{'collection.curate.header' |translate:{collection: (collectionName$ |async)} }}</h3>
|
<h2>{{'collection.curate.header' |translate:{collection: (collectionName$ |async)} }}</h2>
|
||||||
<ds-curation-form
|
<ds-curation-form
|
||||||
[dsoHandle]="(dsoRD$|async)?.payload.handle"
|
[dsoHandle]="(dsoRD$|async)?.payload.handle"
|
||||||
></ds-curation-form>
|
></ds-curation-form>
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.save-button" | translate}}</span>
|
<span class="d-none d-sm-inline"> {{"item.edit.metadata.save-button" | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h4>{{ 'collection.edit.tabs.source.head' | translate }}</h4>
|
<h2>{{ 'collection.edit.tabs.source.head' | translate }}</h2>
|
||||||
<div *ngIf="contentSource" class="form-check mb-4">
|
<div *ngIf="contentSource" class="form-check mb-4">
|
||||||
<input type="checkbox" class="form-check-input" id="externalSourceCheck"
|
<input type="checkbox" class="form-check-input" id="externalSourceCheck"
|
||||||
[checked]="(contentSource?.harvestType !== harvestTypeNone)" (change)="changeExternalSource()">
|
[checked]="(contentSource?.harvestType !== harvestTypeNone)" (change)="changeExternalSource()">
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12" *ngVar="(itemRD$ | async) as itemRD">
|
<div class="col-12" *ngVar="(itemRD$ | async) as itemRD">
|
||||||
<ng-container *ngIf="itemRD?.hasSucceeded">
|
<ng-container *ngIf="itemRD?.hasSucceeded">
|
||||||
<h2 class="border-bottom">{{ 'collection.edit.template.head' | translate:{ collection: dsoNameService.getName(collection) } }}</h2>
|
<h1 class="border-bottom">{{ 'collection.edit.template.head' | translate:{ collection: dsoNameService.getName(collection) } }}</h1>
|
||||||
<ds-themed-dso-edit-metadata [updateDataService]="itemTemplateService" [dso]="itemRD?.payload"></ds-themed-dso-edit-metadata>
|
<ds-themed-dso-edit-metadata [updateDataService]="itemTemplateService" [dso]="itemRD?.payload"></ds-themed-dso-edit-metadata>
|
||||||
<button [routerLink]="getCollectionEditUrl(collection)" class="btn btn-outline-secondary">{{ 'collection.edit.template.cancel' | translate }}</button>
|
<button [routerLink]="getCollectionEditUrl(collection)" class="btn btn-outline-secondary">{{ 'collection.edit.template.cancel' | translate }}</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<ng-container *ngVar="(dsoRD$ | async)?.payload as dso">
|
<ng-container *ngVar="(dsoRD$ | async)?.payload as dso">
|
||||||
<div class="col-12 pb-4">
|
<div class="col-12 pb-4">
|
||||||
<h2 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h2>
|
<h1 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h1>
|
||||||
<p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
|
<p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col text-right space-children-mr">
|
<div class="col text-right space-children-mr">
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h3>{{'community.curate.header' |translate:{community: (communityName$ |async)} }}</h3>
|
<h2>{{'community.curate.header' |translate:{community: (communityName$ |async)} }}</h2>
|
||||||
<ds-curation-form
|
<ds-curation-form
|
||||||
[dsoHandle]="(dsoRD$|async)?.payload.handle"
|
[dsoHandle]="(dsoRD$|async)?.payload.handle"
|
||||||
></ds-curation-form>
|
></ds-curation-form>
|
||||||
|
@@ -50,7 +50,7 @@ export class DSONameService {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
OrgUnit: (dso: DSpaceObject): string => {
|
OrgUnit: (dso: DSpaceObject): string => {
|
||||||
return dso.firstMetadataValue('organization.legalName');
|
return dso.firstMetadataValue('organization.legalName') || this.translateService.instant('dso.name.untitled');
|
||||||
},
|
},
|
||||||
Default: (dso: DSpaceObject): string => {
|
Default: (dso: DSpaceObject): string => {
|
||||||
// If object doesn't have dc.title metadata use name property
|
// If object doesn't have dc.title metadata use name property
|
||||||
@@ -106,7 +106,7 @@ export class DSONameService {
|
|||||||
}
|
}
|
||||||
return `${familyName}, ${givenName}`;
|
return `${familyName}, ${givenName}`;
|
||||||
} else if (entityType === 'OrgUnit') {
|
} else if (entityType === 'OrgUnit') {
|
||||||
return this.firstMetadataValue(object, dso, 'organization.legalName');
|
return this.firstMetadataValue(object, dso, 'organization.legalName') || this.translateService.instant('dso.name.untitled');
|
||||||
}
|
}
|
||||||
return this.firstMetadataValue(object, dso, 'dc.title') || dso.name || this.translateService.instant('dso.name.untitled');
|
return this.firstMetadataValue(object, dso, 'dc.title') || dso.name || this.translateService.instant('dso.name.untitled');
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,31 @@
|
|||||||
|
import {QualityAssuranceBreadcrumbResolver} from './quality-assurance-breadcrumb.resolver';
|
||||||
|
|
||||||
|
describe('QualityAssuranceBreadcrumbResolver', () => {
|
||||||
|
describe('resolve', () => {
|
||||||
|
let resolver: QualityAssuranceBreadcrumbResolver;
|
||||||
|
let qualityAssuranceBreadcrumbService: any;
|
||||||
|
let route: any;
|
||||||
|
const fullPath = '/test/quality-assurance/';
|
||||||
|
const expectedKey = 'testSourceId:testTopicId';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
route = {
|
||||||
|
paramMap: {
|
||||||
|
get: function (param) {
|
||||||
|
return this[param];
|
||||||
|
},
|
||||||
|
sourceId: 'testSourceId',
|
||||||
|
topicId: 'testTopicId'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
qualityAssuranceBreadcrumbService = {};
|
||||||
|
resolver = new QualityAssuranceBreadcrumbResolver(qualityAssuranceBreadcrumbService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve the breadcrumb config', () => {
|
||||||
|
const resolvedConfig = resolver.resolve(route as any, {url: fullPath + 'testSourceId'} as any);
|
||||||
|
const expectedConfig = { provider: qualityAssuranceBreadcrumbService, key: expectedKey, url: fullPath };
|
||||||
|
expect(resolvedConfig).toEqual(expectedConfig);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,32 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {QualityAssuranceBreadcrumbService} from './quality-assurance-breadcrumb.service';
|
||||||
|
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router';
|
||||||
|
import {BreadcrumbConfig} from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class QualityAssuranceBreadcrumbResolver implements Resolve<BreadcrumbConfig<string>> {
|
||||||
|
constructor(protected breadcrumbService: QualityAssuranceBreadcrumbService) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that resolve QA item into a breadcrumb
|
||||||
|
* The parameter are retrieved by the url since part of the QA route config
|
||||||
|
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
|
||||||
|
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
|
||||||
|
* @returns BreadcrumbConfig object
|
||||||
|
*/
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BreadcrumbConfig<string> {
|
||||||
|
const sourceId = route.paramMap.get('sourceId');
|
||||||
|
const topicId = route.paramMap.get('topicId');
|
||||||
|
let key = sourceId;
|
||||||
|
|
||||||
|
if (topicId) {
|
||||||
|
key += `:${topicId}`;
|
||||||
|
}
|
||||||
|
const fullPath = state.url;
|
||||||
|
const url = fullPath.substr(0, fullPath.indexOf(sourceId));
|
||||||
|
|
||||||
|
return { provider: this.breadcrumbService, key, url };
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,40 @@
|
|||||||
|
import { TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
|
||||||
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
|
import {QualityAssuranceBreadcrumbService} from './quality-assurance-breadcrumb.service';
|
||||||
|
|
||||||
|
describe('QualityAssuranceBreadcrumbService', () => {
|
||||||
|
let service: QualityAssuranceBreadcrumbService;
|
||||||
|
let dataService: any;
|
||||||
|
let translateService: any = {
|
||||||
|
instant: (str) => str,
|
||||||
|
};
|
||||||
|
|
||||||
|
let exampleString;
|
||||||
|
let exampleURL;
|
||||||
|
let exampleQaKey;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
exampleString = 'sourceId';
|
||||||
|
exampleURL = '/test/quality-assurance/';
|
||||||
|
exampleQaKey = 'admin.quality-assurance.breadcrumbs';
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
init();
|
||||||
|
TestBed.configureTestingModule({}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service = new QualityAssuranceBreadcrumbService(dataService,translateService);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getBreadcrumbs', () => {
|
||||||
|
it('should return a breadcrumb based on a string', () => {
|
||||||
|
const breadcrumbs = service.getBreadcrumbs(exampleString, exampleURL);
|
||||||
|
getTestScheduler().expectObservable(breadcrumbs).toBe('(a|)', { a: [new Breadcrumb(exampleQaKey, exampleURL),
|
||||||
|
new Breadcrumb(exampleString, exampleURL + exampleString)]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,53 @@
|
|||||||
|
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
|
||||||
|
import { BreadcrumbsProviderService } from './breadcrumbsProviderService';
|
||||||
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { getFirstCompletedRemoteData } from '../shared/operators';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { QualityAssuranceTopicDataService } from '../notifications/qa/topics/quality-assurance-topic-data.service';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service to calculate QA breadcrumbs for a single part of the route
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class QualityAssuranceBreadcrumbService implements BreadcrumbsProviderService<string> {
|
||||||
|
|
||||||
|
private QUALITY_ASSURANCE_BREADCRUMB_KEY = 'admin.quality-assurance.breadcrumbs';
|
||||||
|
constructor(
|
||||||
|
protected qualityAssuranceService: QualityAssuranceTopicDataService,
|
||||||
|
private translationService: TranslateService,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to calculate the breadcrumbs
|
||||||
|
* @param key The key used to resolve the breadcrumb
|
||||||
|
* @param url The url to use as a link for this breadcrumb
|
||||||
|
*/
|
||||||
|
getBreadcrumbs(key: string, url: string): Observable<Breadcrumb[]> {
|
||||||
|
const sourceId = key.split(':')[0];
|
||||||
|
const topicId = key.split(':')[1];
|
||||||
|
|
||||||
|
if (topicId) {
|
||||||
|
return this.qualityAssuranceService.getTopic(topicId).pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
map((topic) => {
|
||||||
|
return [new Breadcrumb(this.translationService.instant(this.QUALITY_ASSURANCE_BREADCRUMB_KEY), url),
|
||||||
|
new Breadcrumb(sourceId, `${url}${sourceId}`),
|
||||||
|
new Breadcrumb(topicId, undefined)];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return observableOf([new Breadcrumb(this.translationService.instant(this.QUALITY_ASSURANCE_BREADCRUMB_KEY), url),
|
||||||
|
new Breadcrumb(sourceId, `${url}${sourceId}`)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -157,6 +157,9 @@ import { SequenceService } from './shared/sequence.service';
|
|||||||
import { CoreState } from './core-state.model';
|
import { CoreState } from './core-state.model';
|
||||||
import { GroupDataService } from './eperson/group-data.service';
|
import { GroupDataService } from './eperson/group-data.service';
|
||||||
import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model';
|
import { SubmissionAccessesModel } from './config/models/config-submission-accesses.model';
|
||||||
|
import { QualityAssuranceTopicObject } from './notifications/qa/models/quality-assurance-topic.model';
|
||||||
|
import { QualityAssuranceEventObject } from './notifications/qa/models/quality-assurance-event.model';
|
||||||
|
import { QualityAssuranceSourceObject } from './notifications/qa/models/quality-assurance-source.model';
|
||||||
import { RatingAdvancedWorkflowInfo } from './tasks/models/rating-advanced-workflow-info.model';
|
import { RatingAdvancedWorkflowInfo } from './tasks/models/rating-advanced-workflow-info.model';
|
||||||
import { AdvancedWorkflowInfo } from './tasks/models/advanced-workflow-info.model';
|
import { AdvancedWorkflowInfo } from './tasks/models/advanced-workflow-info.model';
|
||||||
import { SelectReviewerAdvancedWorkflowInfo } from './tasks/models/select-reviewer-advanced-workflow-info.model';
|
import { SelectReviewerAdvancedWorkflowInfo } from './tasks/models/select-reviewer-advanced-workflow-info.model';
|
||||||
@@ -369,9 +372,12 @@ export const models =
|
|||||||
ShortLivedToken,
|
ShortLivedToken,
|
||||||
Registration,
|
Registration,
|
||||||
UsageReport,
|
UsageReport,
|
||||||
|
QualityAssuranceTopicObject,
|
||||||
|
QualityAssuranceEventObject,
|
||||||
Root,
|
Root,
|
||||||
SearchConfig,
|
SearchConfig,
|
||||||
SubmissionAccessesModel,
|
SubmissionAccessesModel,
|
||||||
|
QualityAssuranceSourceObject,
|
||||||
AccessStatusObject,
|
AccessStatusObject,
|
||||||
ResearcherProfile,
|
ResearcherProfile,
|
||||||
OrcidQueue,
|
OrcidQueue,
|
||||||
|
@@ -34,4 +34,5 @@ export enum FeatureID {
|
|||||||
CanEditItem = 'canEditItem',
|
CanEditItem = 'canEditItem',
|
||||||
CanRegisterDOI = 'canRegisterDOI',
|
CanRegisterDOI = 'canRegisterDOI',
|
||||||
CanSubscribe = 'canSubscribeDso',
|
CanSubscribe = 'canSubscribeDso',
|
||||||
|
CanSeeQA = 'canSeeQA'
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,247 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||||
|
|
||||||
|
import { RequestService } from '../../../data/request.service';
|
||||||
|
import { buildPaginatedList } from '../../../data/paginated-list.model';
|
||||||
|
import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../../../cache/object-cache.service';
|
||||||
|
import { RestResponse } from '../../../cache/response.models';
|
||||||
|
import { PageInfo } from '../../../shared/page-info.model';
|
||||||
|
import { HALEndpointService } from '../../../shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
|
import { createSuccessfulRemoteDataObject } from '../../../../shared/remote-data.utils';
|
||||||
|
import { QualityAssuranceEventDataService } from './quality-assurance-event-data.service';
|
||||||
|
import {
|
||||||
|
qualityAssuranceEventObjectMissingPid,
|
||||||
|
qualityAssuranceEventObjectMissingPid2,
|
||||||
|
qualityAssuranceEventObjectMissingProjectFound
|
||||||
|
} from '../../../../shared/mocks/notifications.mock';
|
||||||
|
import { ReplaceOperation } from 'fast-json-patch';
|
||||||
|
import { RequestEntry } from '../../../data/request-entry.model';
|
||||||
|
import { FindListOptions } from '../../../data/find-list-options.model';
|
||||||
|
|
||||||
|
describe('QualityAssuranceEventDataService', () => {
|
||||||
|
let scheduler: TestScheduler;
|
||||||
|
let service: QualityAssuranceEventDataService;
|
||||||
|
let serviceASAny: any;
|
||||||
|
let responseCacheEntry: RequestEntry;
|
||||||
|
let responseCacheEntryB: RequestEntry;
|
||||||
|
let responseCacheEntryC: RequestEntry;
|
||||||
|
let requestService: RequestService;
|
||||||
|
let rdbService: RemoteDataBuildService;
|
||||||
|
let objectCache: ObjectCacheService;
|
||||||
|
let halService: HALEndpointService;
|
||||||
|
let notificationsService: NotificationsService;
|
||||||
|
let http: HttpClient;
|
||||||
|
let comparator: any;
|
||||||
|
|
||||||
|
const endpointURL = 'https://rest.api/rest/api/integration/qualityassurancetopics';
|
||||||
|
const requestUUID = '8b3c913a-5a4b-438b-9181-be1a5b4a1c8a';
|
||||||
|
const topic = 'ENRICH!MORE!PID';
|
||||||
|
|
||||||
|
const pageInfo = new PageInfo();
|
||||||
|
const array = [qualityAssuranceEventObjectMissingPid, qualityAssuranceEventObjectMissingPid2];
|
||||||
|
const paginatedList = buildPaginatedList(pageInfo, array);
|
||||||
|
const qaEventObjectRD = createSuccessfulRemoteDataObject(qualityAssuranceEventObjectMissingPid);
|
||||||
|
const qaEventObjectMissingProjectRD = createSuccessfulRemoteDataObject(qualityAssuranceEventObjectMissingProjectFound);
|
||||||
|
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
|
||||||
|
|
||||||
|
const status = 'ACCEPTED';
|
||||||
|
const operation: ReplaceOperation<string>[] = [
|
||||||
|
{
|
||||||
|
path: '/status',
|
||||||
|
op: 'replace',
|
||||||
|
value: status
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
|
||||||
|
responseCacheEntry = new RequestEntry();
|
||||||
|
responseCacheEntry.request = { href: 'https://rest.api/' } as any;
|
||||||
|
responseCacheEntry.response = new RestResponse(true, 200, 'Success');
|
||||||
|
requestService = jasmine.createSpyObj('requestService', {
|
||||||
|
generateRequestId: requestUUID,
|
||||||
|
send: true,
|
||||||
|
removeByHrefSubstring: {},
|
||||||
|
getByHref: jasmine.createSpy('getByHref'),
|
||||||
|
getByUUID: jasmine.createSpy('getByUUID')
|
||||||
|
});
|
||||||
|
|
||||||
|
responseCacheEntryB = new RequestEntry();
|
||||||
|
responseCacheEntryB.request = { href: 'https://rest.api/' } as any;
|
||||||
|
responseCacheEntryB.response = new RestResponse(true, 201, 'Created');
|
||||||
|
|
||||||
|
responseCacheEntryC = new RequestEntry();
|
||||||
|
responseCacheEntryC.request = { href: 'https://rest.api/' } as any;
|
||||||
|
responseCacheEntryC.response = new RestResponse(true, 204, 'No Content');
|
||||||
|
|
||||||
|
rdbService = jasmine.createSpyObj('rdbService', {
|
||||||
|
buildSingle: cold('(a)', {
|
||||||
|
a: qaEventObjectRD
|
||||||
|
}),
|
||||||
|
buildList: cold('(a)', {
|
||||||
|
a: paginatedListRD
|
||||||
|
}),
|
||||||
|
buildFromRequestUUID: jasmine.createSpy('buildFromRequestUUID'),
|
||||||
|
buildFromRequestUUIDAndAwait: jasmine.createSpy('buildFromRequestUUIDAndAwait')
|
||||||
|
});
|
||||||
|
|
||||||
|
objectCache = {} as ObjectCacheService;
|
||||||
|
halService = jasmine.createSpyObj('halService', {
|
||||||
|
getEndpoint: cold('a|', { a: endpointURL })
|
||||||
|
});
|
||||||
|
|
||||||
|
notificationsService = {} as NotificationsService;
|
||||||
|
http = {} as HttpClient;
|
||||||
|
comparator = {} as any;
|
||||||
|
|
||||||
|
service = new QualityAssuranceEventDataService(
|
||||||
|
requestService,
|
||||||
|
rdbService,
|
||||||
|
objectCache,
|
||||||
|
halService,
|
||||||
|
notificationsService,
|
||||||
|
comparator
|
||||||
|
);
|
||||||
|
|
||||||
|
serviceASAny = service;
|
||||||
|
|
||||||
|
spyOn(serviceASAny.searchData, 'searchBy').and.callThrough();
|
||||||
|
spyOn(serviceASAny, 'findById').and.callThrough();
|
||||||
|
spyOn(serviceASAny.patchData, 'patch').and.callThrough();
|
||||||
|
spyOn(serviceASAny, 'postOnRelated').and.callThrough();
|
||||||
|
spyOn(serviceASAny, 'deleteOnRelated').and.callThrough();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getEventsByTopic', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
serviceASAny.requestService.getByHref.and.returnValue(observableOf(responseCacheEntry));
|
||||||
|
serviceASAny.requestService.getByUUID.and.returnValue(observableOf(responseCacheEntry));
|
||||||
|
serviceASAny.rdbService.buildFromRequestUUID.and.returnValue(observableOf(qaEventObjectRD));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should proxy the call to searchData.searchBy', () => {
|
||||||
|
const options: FindListOptions = {
|
||||||
|
searchParams: [
|
||||||
|
{
|
||||||
|
fieldName: 'topic',
|
||||||
|
fieldValue: topic
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
service.getEventsByTopic(topic);
|
||||||
|
expect(serviceASAny.searchData.searchBy).toHaveBeenCalledWith('findByTopic', options, true, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RemoteData<PaginatedList<QualityAssuranceEventObject>> for the object with the given Topic', () => {
|
||||||
|
const result = service.getEventsByTopic(topic);
|
||||||
|
const expected = cold('(a)', {
|
||||||
|
a: paginatedListRD
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getEvent', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
serviceASAny.requestService.getByHref.and.returnValue(observableOf(responseCacheEntry));
|
||||||
|
serviceASAny.requestService.getByUUID.and.returnValue(observableOf(responseCacheEntry));
|
||||||
|
serviceASAny.rdbService.buildFromRequestUUID.and.returnValue(observableOf(qaEventObjectRD));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call findById', () => {
|
||||||
|
service.getEvent(qualityAssuranceEventObjectMissingPid.id).subscribe(
|
||||||
|
(res) => {
|
||||||
|
expect(serviceASAny.findById).toHaveBeenCalledWith(qualityAssuranceEventObjectMissingPid.id, true, true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RemoteData for the object with the given URL', () => {
|
||||||
|
const result = service.getEvent(qualityAssuranceEventObjectMissingPid.id);
|
||||||
|
const expected = cold('(a)', {
|
||||||
|
a: qaEventObjectRD
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('patchEvent', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
serviceASAny.requestService.getByHref.and.returnValue(observableOf(responseCacheEntry));
|
||||||
|
serviceASAny.requestService.getByUUID.and.returnValue(observableOf(responseCacheEntry));
|
||||||
|
serviceASAny.rdbService.buildFromRequestUUID.and.returnValue(observableOf(qaEventObjectRD));
|
||||||
|
serviceASAny.rdbService.buildFromRequestUUIDAndAwait.and.returnValue(observableOf(qaEventObjectRD));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should proxy the call to patchData.patch', () => {
|
||||||
|
service.patchEvent(status, qualityAssuranceEventObjectMissingPid).subscribe(
|
||||||
|
(res) => {
|
||||||
|
expect(serviceASAny.patchData.patch).toHaveBeenCalledWith(qualityAssuranceEventObjectMissingPid, operation);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RemoteData with HTTP 200', () => {
|
||||||
|
const result = service.patchEvent(status, qualityAssuranceEventObjectMissingPid);
|
||||||
|
const expected = cold('(a|)', {
|
||||||
|
a: createSuccessfulRemoteDataObject(qualityAssuranceEventObjectMissingPid)
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('boundProject', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
serviceASAny.requestService.getByHref.and.returnValue(observableOf(responseCacheEntryB));
|
||||||
|
serviceASAny.requestService.getByUUID.and.returnValue(observableOf(responseCacheEntryB));
|
||||||
|
serviceASAny.rdbService.buildFromRequestUUID.and.returnValue(observableOf(qaEventObjectMissingProjectRD));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call postOnRelated', () => {
|
||||||
|
service.boundProject(qualityAssuranceEventObjectMissingProjectFound.id, requestUUID).subscribe(
|
||||||
|
(res) => {
|
||||||
|
expect(serviceASAny.postOnRelated).toHaveBeenCalledWith(qualityAssuranceEventObjectMissingProjectFound.id, requestUUID);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RestResponse with HTTP 201', () => {
|
||||||
|
const result = service.boundProject(qualityAssuranceEventObjectMissingProjectFound.id, requestUUID);
|
||||||
|
const expected = cold('(a|)', {
|
||||||
|
a: createSuccessfulRemoteDataObject(qualityAssuranceEventObjectMissingProjectFound)
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removeProject', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
serviceASAny.requestService.getByHref.and.returnValue(observableOf(responseCacheEntryC));
|
||||||
|
serviceASAny.requestService.getByUUID.and.returnValue(observableOf(responseCacheEntryC));
|
||||||
|
serviceASAny.rdbService.buildFromRequestUUID.and.returnValue(observableOf(createSuccessfulRemoteDataObject({})));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call deleteOnRelated', () => {
|
||||||
|
service.removeProject(qualityAssuranceEventObjectMissingProjectFound.id).subscribe(
|
||||||
|
(res) => {
|
||||||
|
expect(serviceASAny.deleteOnRelated).toHaveBeenCalledWith(qualityAssuranceEventObjectMissingProjectFound.id);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RestResponse with HTTP 204', () => {
|
||||||
|
const result = service.removeProject(qualityAssuranceEventObjectMissingProjectFound.id);
|
||||||
|
const expected = cold('(a|)', {
|
||||||
|
a: createSuccessfulRemoteDataObject({})
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,203 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { find, take } from 'rxjs/operators';
|
||||||
|
import { ReplaceOperation } from 'fast-json-patch';
|
||||||
|
|
||||||
|
import { HALEndpointService } from '../../../shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
|
import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../../../cache/object-cache.service';
|
||||||
|
import { dataService } from '../../../data/base/data-service.decorator';
|
||||||
|
import { RequestService } from '../../../data/request.service';
|
||||||
|
import { RemoteData } from '../../../data/remote-data';
|
||||||
|
import { QualityAssuranceEventObject } from '../models/quality-assurance-event.model';
|
||||||
|
import { QUALITY_ASSURANCE_EVENT_OBJECT } from '../models/quality-assurance-event-object.resource-type';
|
||||||
|
import { FollowLinkConfig } from '../../../../shared/utils/follow-link-config.model';
|
||||||
|
import { PaginatedList } from '../../../data/paginated-list.model';
|
||||||
|
import { NoContent } from '../../../shared/NoContent.model';
|
||||||
|
import { FindListOptions } from '../../../data/find-list-options.model';
|
||||||
|
import { IdentifiableDataService } from '../../../data/base/identifiable-data.service';
|
||||||
|
import { CreateData, CreateDataImpl } from '../../../data/base/create-data';
|
||||||
|
import { PatchData, PatchDataImpl } from '../../../data/base/patch-data';
|
||||||
|
import { DeleteData, DeleteDataImpl } from '../../../data/base/delete-data';
|
||||||
|
import { SearchData, SearchDataImpl } from '../../../data/base/search-data';
|
||||||
|
import { DefaultChangeAnalyzer } from '../../../data/default-change-analyzer.service';
|
||||||
|
import { hasValue } from '../../../../shared/empty.util';
|
||||||
|
import { DeleteByIDRequest, PostRequest } from '../../../data/request.models';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The service handling all Quality Assurance topic REST requests.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
@dataService(QUALITY_ASSURANCE_EVENT_OBJECT)
|
||||||
|
export class QualityAssuranceEventDataService extends IdentifiableDataService<QualityAssuranceEventObject> {
|
||||||
|
|
||||||
|
private createData: CreateData<QualityAssuranceEventObject>;
|
||||||
|
private searchData: SearchData<QualityAssuranceEventObject>;
|
||||||
|
private patchData: PatchData<QualityAssuranceEventObject>;
|
||||||
|
private deleteData: DeleteData<QualityAssuranceEventObject>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize service variables
|
||||||
|
* @param {RequestService} requestService
|
||||||
|
* @param {RemoteDataBuildService} rdbService
|
||||||
|
* @param {ObjectCacheService} objectCache
|
||||||
|
* @param {HALEndpointService} halService
|
||||||
|
* @param {NotificationsService} notificationsService
|
||||||
|
* @param {DefaultChangeAnalyzer<QualityAssuranceEventObject>} comparator
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected comparator: DefaultChangeAnalyzer<QualityAssuranceEventObject>
|
||||||
|
) {
|
||||||
|
super('qualityassuranceevents', requestService, rdbService, objectCache, halService);
|
||||||
|
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
|
||||||
|
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
|
||||||
|
this.patchData = new PatchDataImpl<QualityAssuranceEventObject>(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
|
||||||
|
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of Quality Assurance events by topic.
|
||||||
|
*
|
||||||
|
* @param topic
|
||||||
|
* The Quality Assurance topic
|
||||||
|
* @param options
|
||||||
|
* Find list options object.
|
||||||
|
* @param linksToFollow
|
||||||
|
* List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved.
|
||||||
|
* @return Observable<RemoteData<PaginatedList<QualityAssuranceEventObject>>>
|
||||||
|
* The list of Quality Assurance events.
|
||||||
|
*/
|
||||||
|
public getEventsByTopic(topic: string, options: FindListOptions = {}, ...linksToFollow: FollowLinkConfig<QualityAssuranceEventObject>[]): Observable<RemoteData<PaginatedList<QualityAssuranceEventObject>>> {
|
||||||
|
options.searchParams = [
|
||||||
|
{
|
||||||
|
fieldName: 'topic',
|
||||||
|
fieldValue: topic
|
||||||
|
}
|
||||||
|
];
|
||||||
|
return this.searchData.searchBy('findByTopic', options, true, true, ...linksToFollow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear findByTopic requests from cache
|
||||||
|
*/
|
||||||
|
public clearFindByTopicRequests() {
|
||||||
|
this.requestService.setStaleByHrefSubstring('findByTopic');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a single Quality Assurance event.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* The Quality Assurance event id
|
||||||
|
* @param linksToFollow
|
||||||
|
* List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
||||||
|
* @return Observable<RemoteData<QualityAssuranceEventObject>>
|
||||||
|
* The Quality Assurance event.
|
||||||
|
*/
|
||||||
|
public getEvent(id: string, ...linksToFollow: FollowLinkConfig<QualityAssuranceEventObject>[]): Observable<RemoteData<QualityAssuranceEventObject>> {
|
||||||
|
return this.findById(id, true, true, ...linksToFollow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the new status of a Quality Assurance event.
|
||||||
|
*
|
||||||
|
* @param status
|
||||||
|
* The new status
|
||||||
|
* @param dso QualityAssuranceEventObject
|
||||||
|
* The event item
|
||||||
|
* @param reason
|
||||||
|
* The optional reason (not used for now; for future implementation)
|
||||||
|
* @return Observable<RestResponse>
|
||||||
|
* The REST response.
|
||||||
|
*/
|
||||||
|
public patchEvent(status, dso, reason?: string): Observable<RemoteData<QualityAssuranceEventObject>> {
|
||||||
|
const operation: ReplaceOperation<string>[] = [
|
||||||
|
{
|
||||||
|
path: '/status',
|
||||||
|
op: 'replace',
|
||||||
|
value: status
|
||||||
|
}
|
||||||
|
];
|
||||||
|
return this.patchData.patch(dso, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bound a project to a Quality Assurance event publication.
|
||||||
|
*
|
||||||
|
* @param itemId
|
||||||
|
* The Id of the Quality Assurance event
|
||||||
|
* @param projectId
|
||||||
|
* The project Id to bound
|
||||||
|
* @return Observable<RestResponse>
|
||||||
|
* The REST response.
|
||||||
|
*/
|
||||||
|
public boundProject(itemId: string, projectId: string): Observable<RemoteData<QualityAssuranceEventObject>> {
|
||||||
|
return this.postOnRelated(itemId, projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a project from a Quality Assurance event publication.
|
||||||
|
*
|
||||||
|
* @param itemId
|
||||||
|
* The Id of the Quality Assurance event
|
||||||
|
* @return Observable<RestResponse>
|
||||||
|
* The REST response.
|
||||||
|
*/
|
||||||
|
public removeProject(itemId: string): Observable<RemoteData<NoContent>> {
|
||||||
|
return this.deleteOnRelated(itemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a delete operation on an endpoint related item. Ex.: endpoint/<itemId>/related
|
||||||
|
* @param objectId The item id
|
||||||
|
* @return the RestResponse as an Observable
|
||||||
|
*/
|
||||||
|
private deleteOnRelated(objectId: string): Observable<RemoteData<NoContent>> {
|
||||||
|
const requestId = this.requestService.generateRequestId();
|
||||||
|
|
||||||
|
const hrefObs = this.getIDHrefObs(objectId);
|
||||||
|
|
||||||
|
hrefObs.pipe(
|
||||||
|
find((href: string) => hasValue(href)),
|
||||||
|
).subscribe((href: string) => {
|
||||||
|
const request = new DeleteByIDRequest(requestId, href + '/related', objectId);
|
||||||
|
if (hasValue(this.responseMsToLive)) {
|
||||||
|
request.responseMsToLive = this.responseMsToLive;
|
||||||
|
}
|
||||||
|
this.requestService.send(request);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.rdbService.buildFromRequestUUID<QualityAssuranceEventObject>(requestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a post on an endpoint related item with ID. Ex.: endpoint/<itemId>/related?item=<relatedItemId>
|
||||||
|
* @param objectId The item id
|
||||||
|
* @param relatedItemId The related item Id
|
||||||
|
* @param body The optional POST body
|
||||||
|
* @return the RestResponse as an Observable
|
||||||
|
*/
|
||||||
|
private postOnRelated(objectId: string, relatedItemId: string, body?: any) {
|
||||||
|
const requestId = this.requestService.generateRequestId();
|
||||||
|
const hrefObs = this.getIDHrefObs(objectId);
|
||||||
|
|
||||||
|
hrefObs.pipe(
|
||||||
|
take(1)
|
||||||
|
).subscribe((href: string) => {
|
||||||
|
const request = new PostRequest(requestId, href + '/related?item=' + relatedItemId, body);
|
||||||
|
if (hasValue(this.responseMsToLive)) {
|
||||||
|
request.responseMsToLive = this.responseMsToLive;
|
||||||
|
}
|
||||||
|
this.requestService.send(request);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.rdbService.buildFromRequestUUID<QualityAssuranceEventObject>(requestId);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { ResourceType } from '../../../shared/resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource type for the Quality Assurance event
|
||||||
|
*
|
||||||
|
* Needs to be in a separate file to prevent circular
|
||||||
|
* dependencies in webpack.
|
||||||
|
*/
|
||||||
|
export const QUALITY_ASSURANCE_EVENT_OBJECT = new ResourceType('qualityassuranceevent');
|
@@ -0,0 +1,171 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { autoserialize, autoserializeAs, deserialize } from 'cerialize';
|
||||||
|
import { QUALITY_ASSURANCE_EVENT_OBJECT } from './quality-assurance-event-object.resource-type';
|
||||||
|
import { excludeFromEquals } from '../../../utilities/equals.decorators';
|
||||||
|
import { ResourceType } from '../../../shared/resource-type';
|
||||||
|
import { HALLink } from '../../../shared/hal-link.model';
|
||||||
|
import { Item } from '../../../shared/item.model';
|
||||||
|
import { ITEM } from '../../../shared/item.resource-type';
|
||||||
|
import { link, typedObject } from '../../../cache/builders/build-decorators';
|
||||||
|
import { RemoteData } from '../../../data/remote-data';
|
||||||
|
import {CacheableObject} from '../../../cache/cacheable-object.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface representing the Quality Assurance event message
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface QualityAssuranceEventMessageObject {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface representing the Quality Assurance event message
|
||||||
|
*/
|
||||||
|
export interface SourceQualityAssuranceEventMessageObject {
|
||||||
|
/**
|
||||||
|
* The type of 'value'
|
||||||
|
*/
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value suggested by Notifications
|
||||||
|
*/
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The abstract suggested by Notifications
|
||||||
|
*/
|
||||||
|
abstract: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The project acronym suggested by Notifications
|
||||||
|
*/
|
||||||
|
acronym: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The project code suggested by Notifications
|
||||||
|
*/
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The project funder suggested by Notifications
|
||||||
|
*/
|
||||||
|
funder: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The project program suggested by Notifications
|
||||||
|
*/
|
||||||
|
fundingProgram?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The project jurisdiction suggested by Notifications
|
||||||
|
*/
|
||||||
|
jurisdiction: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The project title suggested by Notifications
|
||||||
|
*/
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Source ID.
|
||||||
|
*/
|
||||||
|
sourceId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The PID href.
|
||||||
|
*/
|
||||||
|
pidHref: string;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface representing the Quality Assurance event model
|
||||||
|
*/
|
||||||
|
@typedObject
|
||||||
|
export class QualityAssuranceEventObject implements CacheableObject {
|
||||||
|
/**
|
||||||
|
* A string representing the kind of object, e.g. community, item, …
|
||||||
|
*/
|
||||||
|
static type = QUALITY_ASSURANCE_EVENT_OBJECT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Quality Assurance event uuid inside DSpace
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The universally unique identifier of this Quality Assurance event
|
||||||
|
*/
|
||||||
|
@autoserializeAs(String, 'id')
|
||||||
|
uuid: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Quality Assurance event original id (ex.: the source archive OAI-PMH identifier)
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
originalId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The title of the article to which the suggestion refers
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reliability of the suggestion (of the data inside 'message')
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
trust: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timestamp Quality Assurance event was saved in DSpace
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
eventDate: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Quality Assurance event status (ACCEPTED, REJECTED, DISCARDED, PENDING)
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The suggestion data. Data may vary depending on the source
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
message: SourceQualityAssuranceEventMessageObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of this ConfigObject
|
||||||
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
|
@autoserialize
|
||||||
|
type: ResourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The links to all related resources returned by the rest api.
|
||||||
|
*/
|
||||||
|
@deserialize
|
||||||
|
_links: {
|
||||||
|
self: HALLink,
|
||||||
|
target: HALLink,
|
||||||
|
related: HALLink
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The related publication DSpace item
|
||||||
|
* Will be undefined unless the {@item HALLink} has been resolved.
|
||||||
|
*/
|
||||||
|
@link(ITEM)
|
||||||
|
target?: Observable<RemoteData<Item>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The related project for this Event
|
||||||
|
* Will be undefined unless the {@related HALLink} has been resolved.
|
||||||
|
*/
|
||||||
|
@link(ITEM)
|
||||||
|
related?: Observable<RemoteData<Item>>;
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { ResourceType } from '../../../shared/resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource type for the Quality Assurance source
|
||||||
|
*
|
||||||
|
* Needs to be in a separate file to prevent circular
|
||||||
|
* dependencies in webpack.
|
||||||
|
*/
|
||||||
|
export const QUALITY_ASSURANCE_SOURCE_OBJECT = new ResourceType('qualityassurancesource');
|
@@ -0,0 +1,52 @@
|
|||||||
|
import { autoserialize, deserialize } from 'cerialize';
|
||||||
|
|
||||||
|
import { excludeFromEquals } from '../../../utilities/equals.decorators';
|
||||||
|
import { ResourceType } from '../../../shared/resource-type';
|
||||||
|
import { HALLink } from '../../../shared/hal-link.model';
|
||||||
|
import { typedObject } from '../../../cache/builders/build-decorators';
|
||||||
|
import { QUALITY_ASSURANCE_SOURCE_OBJECT } from './quality-assurance-source-object.resource-type';
|
||||||
|
import {CacheableObject} from '../../../cache/cacheable-object.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface representing the Quality Assurance source model
|
||||||
|
*/
|
||||||
|
@typedObject
|
||||||
|
export class QualityAssuranceSourceObject implements CacheableObject {
|
||||||
|
/**
|
||||||
|
* A string representing the kind of object, e.g. community, item, …
|
||||||
|
*/
|
||||||
|
static type = QUALITY_ASSURANCE_SOURCE_OBJECT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Quality Assurance source id
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date of the last udate from Notifications
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
lastEvent: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total number of suggestions provided by Notifications for this source
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
totalEvents: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of this ConfigObject
|
||||||
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
|
@autoserialize
|
||||||
|
type: ResourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The links to all related resources returned by the rest api.
|
||||||
|
*/
|
||||||
|
@deserialize
|
||||||
|
_links: {
|
||||||
|
self: HALLink,
|
||||||
|
};
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { ResourceType } from '../../../shared/resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource type for the Quality Assurance topic
|
||||||
|
*
|
||||||
|
* Needs to be in a separate file to prevent circular
|
||||||
|
* dependencies in webpack.
|
||||||
|
*/
|
||||||
|
export const QUALITY_ASSURANCE_TOPIC_OBJECT = new ResourceType('qualityassurancetopic');
|
@@ -0,0 +1,58 @@
|
|||||||
|
import { autoserialize, deserialize } from 'cerialize';
|
||||||
|
|
||||||
|
import { QUALITY_ASSURANCE_TOPIC_OBJECT } from './quality-assurance-topic-object.resource-type';
|
||||||
|
import { excludeFromEquals } from '../../../utilities/equals.decorators';
|
||||||
|
import { ResourceType } from '../../../shared/resource-type';
|
||||||
|
import { HALLink } from '../../../shared/hal-link.model';
|
||||||
|
import { typedObject } from '../../../cache/builders/build-decorators';
|
||||||
|
import {CacheableObject} from '../../../cache/cacheable-object.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface representing the Quality Assurance topic model
|
||||||
|
*/
|
||||||
|
@typedObject
|
||||||
|
export class QualityAssuranceTopicObject implements CacheableObject {
|
||||||
|
/**
|
||||||
|
* A string representing the kind of object, e.g. community, item, …
|
||||||
|
*/
|
||||||
|
static type = QUALITY_ASSURANCE_TOPIC_OBJECT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Quality Assurance topic id
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Quality Assurance topic name to display
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date of the last udate from Notifications
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
lastEvent: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total number of suggestions provided by Notifications for this topic
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
totalEvents: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of this ConfigObject
|
||||||
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
|
@autoserialize
|
||||||
|
type: ResourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The links to all related resources returned by the rest api.
|
||||||
|
*/
|
||||||
|
@deserialize
|
||||||
|
_links: {
|
||||||
|
self: HALLink,
|
||||||
|
};
|
||||||
|
}
|
@@ -0,0 +1,125 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||||
|
|
||||||
|
import { RequestService } from '../../../data/request.service';
|
||||||
|
import { buildPaginatedList } from '../../../data/paginated-list.model';
|
||||||
|
import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../../../cache/object-cache.service';
|
||||||
|
import { RestResponse } from '../../../cache/response.models';
|
||||||
|
import { PageInfo } from '../../../shared/page-info.model';
|
||||||
|
import { HALEndpointService } from '../../../shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
|
import { createSuccessfulRemoteDataObject } from '../../../../shared/remote-data.utils';
|
||||||
|
import {
|
||||||
|
qualityAssuranceSourceObjectMoreAbstract,
|
||||||
|
qualityAssuranceSourceObjectMorePid
|
||||||
|
} from '../../../../shared/mocks/notifications.mock';
|
||||||
|
import { RequestEntry } from '../../../data/request-entry.model';
|
||||||
|
import { QualityAssuranceSourceDataService } from './quality-assurance-source-data.service';
|
||||||
|
|
||||||
|
describe('QualityAssuranceSourceDataService', () => {
|
||||||
|
let scheduler: TestScheduler;
|
||||||
|
let service: QualityAssuranceSourceDataService;
|
||||||
|
let responseCacheEntry: RequestEntry;
|
||||||
|
let requestService: RequestService;
|
||||||
|
let rdbService: RemoteDataBuildService;
|
||||||
|
let objectCache: ObjectCacheService;
|
||||||
|
let halService: HALEndpointService;
|
||||||
|
let notificationsService: NotificationsService;
|
||||||
|
let http: HttpClient;
|
||||||
|
let comparator: any;
|
||||||
|
|
||||||
|
const endpointURL = 'https://rest.api/rest/api/integration/qualityassurancesources';
|
||||||
|
const requestUUID = '8b3c913a-5a4b-438b-9181-be1a5b4a1c8a';
|
||||||
|
|
||||||
|
const pageInfo = new PageInfo();
|
||||||
|
const array = [qualityAssuranceSourceObjectMorePid, qualityAssuranceSourceObjectMoreAbstract];
|
||||||
|
const paginatedList = buildPaginatedList(pageInfo, array);
|
||||||
|
const qaSourceObjectRD = createSuccessfulRemoteDataObject(qualityAssuranceSourceObjectMorePid);
|
||||||
|
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
|
||||||
|
responseCacheEntry = new RequestEntry();
|
||||||
|
responseCacheEntry.response = new RestResponse(true, 200, 'Success');
|
||||||
|
requestService = jasmine.createSpyObj('requestService', {
|
||||||
|
generateRequestId: requestUUID,
|
||||||
|
send: true,
|
||||||
|
removeByHrefSubstring: {},
|
||||||
|
getByHref: observableOf(responseCacheEntry),
|
||||||
|
getByUUID: observableOf(responseCacheEntry),
|
||||||
|
});
|
||||||
|
|
||||||
|
rdbService = jasmine.createSpyObj('rdbService', {
|
||||||
|
buildSingle: cold('(a)', {
|
||||||
|
a: qaSourceObjectRD
|
||||||
|
}),
|
||||||
|
buildList: cold('(a)', {
|
||||||
|
a: paginatedListRD
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
objectCache = {} as ObjectCacheService;
|
||||||
|
halService = jasmine.createSpyObj('halService', {
|
||||||
|
getEndpoint: cold('a|', { a: endpointURL })
|
||||||
|
});
|
||||||
|
|
||||||
|
notificationsService = {} as NotificationsService;
|
||||||
|
http = {} as HttpClient;
|
||||||
|
comparator = {} as any;
|
||||||
|
|
||||||
|
service = new QualityAssuranceSourceDataService(
|
||||||
|
requestService,
|
||||||
|
rdbService,
|
||||||
|
objectCache,
|
||||||
|
halService,
|
||||||
|
notificationsService
|
||||||
|
);
|
||||||
|
|
||||||
|
spyOn((service as any).findAllData, 'findAll').and.callThrough();
|
||||||
|
spyOn((service as any), 'findById').and.callThrough();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSources', () => {
|
||||||
|
it('should call findAll', (done) => {
|
||||||
|
service.getSources().subscribe(
|
||||||
|
(res) => {
|
||||||
|
expect((service as any).findAllData.findAll).toHaveBeenCalledWith({}, true, true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RemoteData<PaginatedList<QualityAssuranceSourceObject>> for the object with the given URL', () => {
|
||||||
|
const result = service.getSources();
|
||||||
|
const expected = cold('(a)', {
|
||||||
|
a: paginatedListRD
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSource', () => {
|
||||||
|
it('should call findById', (done) => {
|
||||||
|
service.getSource(qualityAssuranceSourceObjectMorePid.id).subscribe(
|
||||||
|
(res) => {
|
||||||
|
expect((service as any).findById).toHaveBeenCalledWith(qualityAssuranceSourceObjectMorePid.id, true, true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RemoteData<QualityAssuranceSourceObject> for the object with the given URL', () => {
|
||||||
|
const result = service.getSource(qualityAssuranceSourceObjectMorePid.id);
|
||||||
|
const expected = cold('(a)', {
|
||||||
|
a: qaSourceObjectRD
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,87 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { HALEndpointService } from '../../../shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
|
import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../../../cache/object-cache.service';
|
||||||
|
import { dataService } from '../../../data/base/data-service.decorator';
|
||||||
|
import { RequestService } from '../../../data/request.service';
|
||||||
|
import { RemoteData } from '../../../data/remote-data';
|
||||||
|
import { QualityAssuranceSourceObject } from '../models/quality-assurance-source.model';
|
||||||
|
import { QUALITY_ASSURANCE_SOURCE_OBJECT } from '../models/quality-assurance-source-object.resource-type';
|
||||||
|
import { FollowLinkConfig } from '../../../../shared/utils/follow-link-config.model';
|
||||||
|
import { PaginatedList } from '../../../data/paginated-list.model';
|
||||||
|
import { FindListOptions } from '../../../data/find-list-options.model';
|
||||||
|
import { IdentifiableDataService } from '../../../data/base/identifiable-data.service';
|
||||||
|
import { FindAllData, FindAllDataImpl } from '../../../data/base/find-all-data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The service handling all Quality Assurance source REST requests.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
@dataService(QUALITY_ASSURANCE_SOURCE_OBJECT)
|
||||||
|
export class QualityAssuranceSourceDataService extends IdentifiableDataService<QualityAssuranceSourceObject> {
|
||||||
|
|
||||||
|
private findAllData: FindAllData<QualityAssuranceSourceObject>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize service variables
|
||||||
|
* @param {RequestService} requestService
|
||||||
|
* @param {RemoteDataBuildService} rdbService
|
||||||
|
* @param {ObjectCacheService} objectCache
|
||||||
|
* @param {HALEndpointService} halService
|
||||||
|
* @param {NotificationsService} notificationsService
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService
|
||||||
|
) {
|
||||||
|
super('qualityassurancesources', requestService, rdbService, objectCache, halService);
|
||||||
|
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of Quality Assurance source.
|
||||||
|
*
|
||||||
|
* @param options Find list options object.
|
||||||
|
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
|
||||||
|
* no valid cached version. Defaults to true
|
||||||
|
* @param reRequestOnStale Whether or not the request should automatically be re-
|
||||||
|
* requested after the response becomes stale
|
||||||
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved.
|
||||||
|
*
|
||||||
|
* @return Observable<RemoteData<PaginatedList<QualityAssuranceSourceObject>>>
|
||||||
|
* The list of Quality Assurance source.
|
||||||
|
*/
|
||||||
|
public getSources(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<QualityAssuranceSourceObject>[]): Observable<RemoteData<PaginatedList<QualityAssuranceSourceObject>>> {
|
||||||
|
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear FindAll source requests from cache
|
||||||
|
*/
|
||||||
|
public clearFindAllSourceRequests() {
|
||||||
|
this.requestService.setStaleByHrefSubstring('qualityassurancesources');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a single Quality Assurance source.
|
||||||
|
*
|
||||||
|
* @param id The Quality Assurance source id
|
||||||
|
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
|
||||||
|
* no valid cached version. Defaults to true
|
||||||
|
* @param reRequestOnStale Whether or not the request should automatically be re-
|
||||||
|
* requested after the response becomes stale
|
||||||
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved.
|
||||||
|
*
|
||||||
|
* @return Observable<RemoteData<QualityAssuranceSourceObject>> The Quality Assurance source.
|
||||||
|
*/
|
||||||
|
public getSource(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<QualityAssuranceSourceObject>[]): Observable<RemoteData<QualityAssuranceSourceObject>> {
|
||||||
|
return this.findById(id, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,125 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||||
|
|
||||||
|
import { RequestService } from '../../../data/request.service';
|
||||||
|
import { buildPaginatedList } from '../../../data/paginated-list.model';
|
||||||
|
import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../../../cache/object-cache.service';
|
||||||
|
import { RestResponse } from '../../../cache/response.models';
|
||||||
|
import { PageInfo } from '../../../shared/page-info.model';
|
||||||
|
import { HALEndpointService } from '../../../shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
|
import { createSuccessfulRemoteDataObject } from '../../../../shared/remote-data.utils';
|
||||||
|
import { QualityAssuranceTopicDataService } from './quality-assurance-topic-data.service';
|
||||||
|
import {
|
||||||
|
qualityAssuranceTopicObjectMoreAbstract,
|
||||||
|
qualityAssuranceTopicObjectMorePid
|
||||||
|
} from '../../../../shared/mocks/notifications.mock';
|
||||||
|
import { RequestEntry } from '../../../data/request-entry.model';
|
||||||
|
|
||||||
|
describe('QualityAssuranceTopicDataService', () => {
|
||||||
|
let scheduler: TestScheduler;
|
||||||
|
let service: QualityAssuranceTopicDataService;
|
||||||
|
let responseCacheEntry: RequestEntry;
|
||||||
|
let requestService: RequestService;
|
||||||
|
let rdbService: RemoteDataBuildService;
|
||||||
|
let objectCache: ObjectCacheService;
|
||||||
|
let halService: HALEndpointService;
|
||||||
|
let notificationsService: NotificationsService;
|
||||||
|
let http: HttpClient;
|
||||||
|
let comparator: any;
|
||||||
|
|
||||||
|
const endpointURL = 'https://rest.api/rest/api/integration/qualityassurancetopics';
|
||||||
|
const requestUUID = '8b3c913a-5a4b-438b-9181-be1a5b4a1c8a';
|
||||||
|
|
||||||
|
const pageInfo = new PageInfo();
|
||||||
|
const array = [qualityAssuranceTopicObjectMorePid, qualityAssuranceTopicObjectMoreAbstract];
|
||||||
|
const paginatedList = buildPaginatedList(pageInfo, array);
|
||||||
|
const qaTopicObjectRD = createSuccessfulRemoteDataObject(qualityAssuranceTopicObjectMorePid);
|
||||||
|
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
|
||||||
|
responseCacheEntry = new RequestEntry();
|
||||||
|
responseCacheEntry.response = new RestResponse(true, 200, 'Success');
|
||||||
|
requestService = jasmine.createSpyObj('requestService', {
|
||||||
|
generateRequestId: requestUUID,
|
||||||
|
send: true,
|
||||||
|
removeByHrefSubstring: {},
|
||||||
|
getByHref: observableOf(responseCacheEntry),
|
||||||
|
getByUUID: observableOf(responseCacheEntry),
|
||||||
|
});
|
||||||
|
|
||||||
|
rdbService = jasmine.createSpyObj('rdbService', {
|
||||||
|
buildSingle: cold('(a)', {
|
||||||
|
a: qaTopicObjectRD
|
||||||
|
}),
|
||||||
|
buildList: cold('(a)', {
|
||||||
|
a: paginatedListRD
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
objectCache = {} as ObjectCacheService;
|
||||||
|
halService = jasmine.createSpyObj('halService', {
|
||||||
|
getEndpoint: cold('a|', { a: endpointURL })
|
||||||
|
});
|
||||||
|
|
||||||
|
notificationsService = {} as NotificationsService;
|
||||||
|
http = {} as HttpClient;
|
||||||
|
comparator = {} as any;
|
||||||
|
|
||||||
|
service = new QualityAssuranceTopicDataService(
|
||||||
|
requestService,
|
||||||
|
rdbService,
|
||||||
|
objectCache,
|
||||||
|
halService,
|
||||||
|
notificationsService
|
||||||
|
);
|
||||||
|
|
||||||
|
spyOn((service as any).findAllData, 'findAll').and.callThrough();
|
||||||
|
spyOn((service as any), 'findById').and.callThrough();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTopics', () => {
|
||||||
|
it('should call findListByHref', (done) => {
|
||||||
|
service.getTopics().subscribe(
|
||||||
|
(res) => {
|
||||||
|
expect((service as any).findAllData.findAll).toHaveBeenCalledWith({}, true, true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RemoteData<PaginatedList<QualityAssuranceTopicObject>> for the object with the given URL', () => {
|
||||||
|
const result = service.getTopics();
|
||||||
|
const expected = cold('(a)', {
|
||||||
|
a: paginatedListRD
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTopic', () => {
|
||||||
|
it('should call findByHref', (done) => {
|
||||||
|
service.getTopic(qualityAssuranceTopicObjectMorePid.id).subscribe(
|
||||||
|
(res) => {
|
||||||
|
expect((service as any).findById).toHaveBeenCalledWith(qualityAssuranceTopicObjectMorePid.id, true, true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a RemoteData<QualityAssuranceTopicObject> for the object with the given URL', () => {
|
||||||
|
const result = service.getTopic(qualityAssuranceTopicObjectMorePid.id);
|
||||||
|
const expected = cold('(a)', {
|
||||||
|
a: qaTopicObjectRD
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,88 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { HALEndpointService } from '../../../shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
|
import { RemoteDataBuildService } from '../../../cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../../../cache/object-cache.service';
|
||||||
|
import { RequestService } from '../../../data/request.service';
|
||||||
|
import { RemoteData } from '../../../data/remote-data';
|
||||||
|
import { QualityAssuranceTopicObject } from '../models/quality-assurance-topic.model';
|
||||||
|
import { FollowLinkConfig } from '../../../../shared/utils/follow-link-config.model';
|
||||||
|
import { PaginatedList } from '../../../data/paginated-list.model';
|
||||||
|
import { FindListOptions } from '../../../data/find-list-options.model';
|
||||||
|
import { IdentifiableDataService } from '../../../data/base/identifiable-data.service';
|
||||||
|
import { dataService } from '../../../data/base/data-service.decorator';
|
||||||
|
import { QUALITY_ASSURANCE_TOPIC_OBJECT } from '../models/quality-assurance-topic-object.resource-type';
|
||||||
|
import { FindAllData, FindAllDataImpl } from '../../../data/base/find-all-data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The service handling all Quality Assurance topic REST requests.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
@dataService(QUALITY_ASSURANCE_TOPIC_OBJECT)
|
||||||
|
export class QualityAssuranceTopicDataService extends IdentifiableDataService<QualityAssuranceTopicObject> {
|
||||||
|
|
||||||
|
private findAllData: FindAllData<QualityAssuranceTopicObject>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize service variables
|
||||||
|
* @param {RequestService} requestService
|
||||||
|
* @param {RemoteDataBuildService} rdbService
|
||||||
|
* @param {ObjectCacheService} objectCache
|
||||||
|
* @param {HALEndpointService} halService
|
||||||
|
* @param {NotificationsService} notificationsService
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService
|
||||||
|
) {
|
||||||
|
super('qualityassurancetopics', requestService, rdbService, objectCache, halService);
|
||||||
|
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of Quality Assurance topics.
|
||||||
|
*
|
||||||
|
* @param options Find list options object.
|
||||||
|
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
|
||||||
|
* no valid cached version. Defaults to true
|
||||||
|
* @param reRequestOnStale Whether or not the request should automatically be re-
|
||||||
|
* requested after the response becomes stale
|
||||||
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved.
|
||||||
|
*
|
||||||
|
* @return Observable<RemoteData<PaginatedList<QualityAssuranceTopicObject>>>
|
||||||
|
* The list of Quality Assurance topics.
|
||||||
|
*/
|
||||||
|
public getTopics(options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<QualityAssuranceTopicObject>[]): Observable<RemoteData<PaginatedList<QualityAssuranceTopicObject>>> {
|
||||||
|
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear FindAll topics requests from cache
|
||||||
|
*/
|
||||||
|
public clearFindAllTopicsRequests() {
|
||||||
|
this.requestService.setStaleByHrefSubstring('qualityassurancetopics');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a single Quality Assurance topic.
|
||||||
|
*
|
||||||
|
* @param id The Quality Assurance topic id
|
||||||
|
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
|
||||||
|
* no valid cached version. Defaults to true
|
||||||
|
* @param reRequestOnStale Whether or not the request should automatically be re-
|
||||||
|
* requested after the response becomes stale
|
||||||
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved.
|
||||||
|
*
|
||||||
|
* @return Observable<RemoteData<QualityAssuranceTopicObject>>
|
||||||
|
* The Quality Assurance topic.
|
||||||
|
*/
|
||||||
|
public getTopic(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<QualityAssuranceTopicObject>[]): Observable<RemoteData<QualityAssuranceTopicObject>> {
|
||||||
|
return this.findById(id, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||||
|
}
|
||||||
|
}
|
@@ -4,6 +4,7 @@
|
|||||||
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center" *ngVar="(mdRepresentation$ | async) as mdRepresentation" role="cell">
|
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center" *ngVar="(mdRepresentation$ | async) as mdRepresentation" role="cell">
|
||||||
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing && !mdRepresentation">{{ mdValue.newValue.value }}</div>
|
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing && !mdRepresentation">{{ mdValue.newValue.value }}</div>
|
||||||
<textarea class="form-control" rows="5" *ngIf="mdValue.editing && !mdRepresentation" [(ngModel)]="mdValue.newValue.value"
|
<textarea class="form-control" rows="5" *ngIf="mdValue.editing && !mdRepresentation" [(ngModel)]="mdValue.newValue.value"
|
||||||
|
[attr.aria-label]="(dsoType + '.edit.metadata.edit.value') | translate"
|
||||||
[dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea>
|
[dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea>
|
||||||
<div class="d-flex" *ngIf="mdRepresentation">
|
<div class="d-flex" *ngIf="mdRepresentation">
|
||||||
<a class="mr-2" target="_blank" [routerLink]="mdRepresentationItemRoute$ | async">{{ mdRepresentationName$ | async }}</a>
|
<a class="mr-2" target="_blank" [routerLink]="mdRepresentationItemRoute$ | async">{{ mdRepresentationName$ | async }}</a>
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
<div class="ds-flex-cell ds-lang-cell" role="cell">
|
<div class="ds-flex-cell ds-lang-cell" role="cell">
|
||||||
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing">{{ mdValue.newValue.language }}</div>
|
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing">{{ mdValue.newValue.language }}</div>
|
||||||
<input class="form-control" type="text" *ngIf="mdValue.editing" [(ngModel)]="mdValue.newValue.language"
|
<input class="form-control" type="text" *ngIf="mdValue.editing" [(ngModel)]="mdValue.newValue.language"
|
||||||
|
[attr.aria-label]="(dsoType + '.edit.metadata.edit.language') | translate"
|
||||||
[dsDebounce]="300" (onDebounce)="confirm.emit(false)" />
|
[dsDebounce]="300" (onDebounce)="confirm.emit(false)" />
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center ds-flex-cell ds-edit-cell" role="cell">
|
<div class="text-center ds-flex-cell ds-edit-cell" role="cell">
|
||||||
|
@@ -62,21 +62,22 @@
|
|||||||
<a class="text-white"
|
<a class="text-white"
|
||||||
href="https://www.lyrasis.org/">{{ 'footer.link.lyrasis' | translate}}</a>
|
href="https://www.lyrasis.org/">{{ 'footer.link.lyrasis' | translate}}</a>
|
||||||
</p>
|
</p>
|
||||||
<ul class="footer-info list-unstyled small d-flex justify-content-center mb-0">
|
<ul class="footer-info list-unstyled d-flex justify-content-center mb-0">
|
||||||
<li>
|
<li>
|
||||||
<a class="text-white" href="javascript:void(0);"
|
<button class="btn btn-link text-white" type="button" (click)="showCookieSettings()">
|
||||||
(click)="showCookieSettings()">{{ 'footer.link.cookies' | translate}}</a>
|
{{ 'footer.link.cookies' | translate}}
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li *ngIf="showPrivacyPolicy">
|
<li *ngIf="showPrivacyPolicy">
|
||||||
<a class="text-white"
|
<a class="btn text-white"
|
||||||
routerLink="info/privacy">{{ 'footer.link.privacy-policy' | translate}}</a>
|
routerLink="info/privacy">{{ 'footer.link.privacy-policy' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li *ngIf="showEndUserAgreement">
|
<li *ngIf="showEndUserAgreement">
|
||||||
<a class="text-white"
|
<a class="btn text-white"
|
||||||
routerLink="info/end-user-agreement">{{ 'footer.link.end-user-agreement' | translate}}</a>
|
routerLink="info/end-user-agreement">{{ 'footer.link.end-user-agreement' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li *ngIf="showSendFeedback$ | async">
|
<li *ngIf="showSendFeedback$ | async">
|
||||||
<a class="text-white"
|
<a class="btn text-white"
|
||||||
routerLink="info/feedback">{{ 'footer.link.feedback' | translate}}</a>
|
routerLink="info/feedback">{{ 'footer.link.feedback' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@@ -26,9 +26,19 @@
|
|||||||
li {
|
li {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|
||||||
a {
|
a, .btn-link {
|
||||||
padding: 0 calc(var(--bs-spacer) / 2);
|
padding: 0 calc(var(--bs-spacer) / 2);
|
||||||
color: inherit
|
color: inherit;
|
||||||
|
font-size: .875em;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
<div *ngFor="let entry of healthInfoComponent | dsObjNgFor" data-test="collapse">
|
<div *ngFor="let entry of healthInfoComponent | dsObjNgFor" data-test="collapse">
|
||||||
<div *ngIf="entry && !isPlainProperty(entry.value)" class="mb-3 border-bottom">
|
<div *ngIf="entry && !isPlainProperty(entry.value)" class="mb-3 border-bottom">
|
||||||
<div class="w-100 d-flex justify-content-between collapse-toggle" (click)="collapse.toggle()">
|
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="collapse.toggle()">
|
||||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!collapse.collapsed"
|
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!collapse.collapsed"
|
||||||
aria-controls="collapseExample">
|
[attr.aria-controls]="'health-info-component-' + entry.key + '-content'">
|
||||||
{{ entry.key | titlecase }}
|
{{ entry.key | titlecase }}
|
||||||
</button>
|
</button>
|
||||||
<div class="d-inline-block">
|
<div class="d-flex my-auto">
|
||||||
<span *ngIf="collapse.collapsed" class="fas fa-chevron-down"></span>
|
<span *ngIf="collapse.collapsed" class="fas fa-chevron-down"></span>
|
||||||
<span *ngIf="!collapse.collapsed" class="fas fa-chevron-up"></span>
|
<span *ngIf="!collapse.collapsed" class="fas fa-chevron-up"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div #collapse="ngbCollapse" [ngbCollapse]="isCollapsed">
|
<div #collapse="ngbCollapse" [id]="'health-info-component-' + entry.key + '-content'" [ngbCollapse]="isCollapsed">
|
||||||
<div class="card border-0">
|
<div class="card border-0">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<ds-health-info-component [healthInfoComponent]="entry.value"
|
<ds-health-info-component [healthInfoComponent]="entry.value"
|
||||||
|
@@ -2,14 +2,14 @@
|
|||||||
<ngb-accordion #acc="ngbAccordion" [activeIds]="activeId">
|
<ngb-accordion #acc="ngbAccordion" [activeIds]="activeId">
|
||||||
<ngb-panel [id]="entry.key" *ngFor="let entry of healthInfoResponse | dsObjNgFor">
|
<ngb-panel [id]="entry.key" *ngFor="let entry of healthInfoResponse | dsObjNgFor">
|
||||||
<ng-template ngbPanelHeader>
|
<ng-template ngbPanelHeader>
|
||||||
<div class="w-100 d-flex justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle(entry.key)" data-test="info-component">
|
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle(entry.key)" data-test="info-component">
|
||||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!acc.isExpanded(entry.key)"
|
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded(entry.key)"
|
||||||
aria-controls="collapsePanels">
|
[attr.aria-controls]="'health-info-' + entry.key + '-content'">
|
||||||
{{ getPanelLabel(entry.key) | titlecase }}
|
{{ getPanelLabel(entry.key) | titlecase }}
|
||||||
</button>
|
</button>
|
||||||
<div class="text-right d-flex">
|
<div class="text-right d-flex gap-2">
|
||||||
<ds-health-status [status]="entry.value?.status"></ds-health-status>
|
<ds-health-status [status]="entry.value?.status"></ds-health-status>
|
||||||
<div class="ml-3 d-inline-block">
|
<div class="d-flex my-auto">
|
||||||
<span *ngIf="acc.isExpanded(entry.key)" class="fas fa-chevron-up fa-fw"></span>
|
<span *ngIf="acc.isExpanded(entry.key)" class="fas fa-chevron-up fa-fw"></span>
|
||||||
<span *ngIf="!acc.isExpanded(entry.key)" class="fas fa-chevron-down fa-fw"></span>
|
<span *ngIf="!acc.isExpanded(entry.key)" class="fas fa-chevron-down fa-fw"></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,8 +17,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template ngbPanelContent>
|
<ng-template ngbPanelContent>
|
||||||
<ds-health-info-component [healthInfoComponentName]="entry.key"
|
<ds-health-info-component [healthInfoComponentName]="entry.key" [healthInfoComponent]="entry.value"
|
||||||
[healthInfoComponent]="entry.value"></ds-health-info-component>
|
[id]="'health-info-' + entry.key + '-content'">
|
||||||
|
</ds-health-info-component>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ngb-panel>
|
</ngb-panel>
|
||||||
</ngb-accordion>
|
</ngb-accordion>
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<h2>{{'health-page.heading' | translate}}</h2>
|
<h2>{{'health-page.heading' | translate}}</h2>
|
||||||
<div *ngIf="(healthResponse | async) && (healthInfoResponse | async)">
|
<div *ngIf="(healthResponse | async) && (healthInfoResponse | async)">
|
||||||
<ul ngbNav #nav="ngbNav" [activeId]="'status'" class="nav-tabs">
|
<ul ngbNav #nav="ngbNav" [activeId]="'status'" class="nav-tabs">
|
||||||
<li [ngbNavItem]="'status'">
|
<li [ngbNavItem]="'status'" role="presentation">
|
||||||
<a ngbNavLink>{{'health-page.status-tab' | translate}}</a>
|
<a ngbNavLink>{{'health-page.status-tab' | translate}}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div id="status">
|
<div id="status">
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li [ngbNavItem]="'info'">
|
<li [ngbNavItem]="'info'" role="presentation">
|
||||||
<a ngbNavLink>{{'health-page.info-tab' | translate}}</a>
|
<a ngbNavLink>{{'health-page.info-tab' | translate}}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div id="info">
|
<div id="info">
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||||
</div>
|
</div>
|
||||||
<ds-alert *ngIf="!(healthResponse | async) || !(healthInfoResponse | async)" [type]="'alert-danger'" [content]="'health-page.error.msg'"></ds-alert>
|
<ds-alert *ngIf="!(healthResponse | async) || !(healthInfoResponse | async)" [type]="AlertType.Error" [content]="'health-page.error.msg'"></ds-alert>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@@ -5,6 +5,7 @@ import { take } from 'rxjs/operators';
|
|||||||
|
|
||||||
import { HealthService } from './health.service';
|
import { HealthService } from './health.service';
|
||||||
import { HealthInfoResponse, HealthResponse } from './models/health-component.model';
|
import { HealthInfoResponse, HealthResponse } from './models/health-component.model';
|
||||||
|
import { AlertType } from '../shared/alert/alert-type';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-health-page',
|
selector: 'ds-health-page',
|
||||||
@@ -33,6 +34,8 @@ export class HealthPageComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
healthInfoResponseInitialised: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
healthInfoResponseInitialised: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
readonly AlertType = AlertType;
|
||||||
|
|
||||||
constructor(private healthDataService: HealthService) {
|
constructor(private healthDataService: HealthService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
<ng-container *ngIf="healthComponent?.components">
|
<ng-container *ngIf="healthComponent?.components">
|
||||||
<div *ngFor="let entry of healthComponent?.components | dsObjNgFor" class="mb-3 border-bottom" data-test="collapse">
|
<div *ngFor="let entry of healthComponent?.components | dsObjNgFor" class="mb-3 border-bottom" data-test="collapse">
|
||||||
<div class="w-100 d-flex justify-content-between collapse-toggle" (click)="collapse.toggle()">
|
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" (click)="collapse.toggle()">
|
||||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!collapse.collapsed"
|
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!collapse.collapsed"
|
||||||
aria-controls="collapseExample">
|
[attr.aria-controls]="'health-component-' + entry.key + 'content'">
|
||||||
{{ entry.key | titlecase }}
|
{{ entry.key | titlecase }}
|
||||||
</button>
|
</button>
|
||||||
<div class="d-inline-block">
|
<div class="d-flex my-auto">
|
||||||
<span *ngIf="collapse.collapsed" class="fas fa-chevron-down"></span>
|
<span *ngIf="collapse.collapsed" class="fas fa-chevron-down"></span>
|
||||||
<span *ngIf="!collapse.collapsed" class="fas fa-chevron-up"></span>
|
<span *ngIf="!collapse.collapsed" class="fas fa-chevron-up"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div #collapse="ngbCollapse" [ngbCollapse]="isCollapsed">
|
<div #collapse="ngbCollapse" [id]="'health-component-' + entry.key + 'content'" [ngbCollapse]="isCollapsed">
|
||||||
<div class="card border-0">
|
<div class="card border-0">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<ds-health-component [healthComponent]="entry.value"
|
<ds-health-component [healthComponent]="entry.value"
|
||||||
|
@@ -1,15 +1,18 @@
|
|||||||
<p class="h4">{{'health-page.status' | translate}} : <ds-health-status [status]="healthResponse.status"></ds-health-status></p>
|
<p class="h4">
|
||||||
|
{{'health-page.status' | translate}}:
|
||||||
|
<ds-health-status [status]="healthResponse.status" class="d-inline-flex"></ds-health-status>
|
||||||
|
</p>
|
||||||
<ngb-accordion #acc="ngbAccordion" [activeIds]="activeId">
|
<ngb-accordion #acc="ngbAccordion" [activeIds]="activeId">
|
||||||
<ngb-panel [id]="entry.key" *ngFor="let entry of healthResponse.components | dsObjNgFor">
|
<ngb-panel [id]="entry.key" *ngFor="let entry of healthResponse.components | dsObjNgFor">
|
||||||
<ng-template ngbPanelHeader>
|
<ng-template ngbPanelHeader>
|
||||||
<div class="w-100 d-flex justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle(entry.key)" data-test="component">
|
<div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle(entry.key)" data-test="component">
|
||||||
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!acc.isExpanded(entry.key)"
|
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded(entry.key)"
|
||||||
aria-controls="collapsePanels">
|
[attr.aria-controls]="'health-panel-' + entry.key + '-content'">
|
||||||
{{ getPanelLabel(entry.key) | titlecase }}
|
{{ getPanelLabel(entry.key) | titlecase }}
|
||||||
</button>
|
</button>
|
||||||
<div class="text-right d-flex">
|
<div class="text-right d-flex gap-2">
|
||||||
<ds-health-status [status]="entry.value?.status"></ds-health-status>
|
<ds-health-status [status]="entry.value?.status"></ds-health-status>
|
||||||
<div class="ml-3 d-inline-block">
|
<div class="d-flex my-auto">
|
||||||
<span *ngIf="acc.isExpanded(entry.key)" class="fas fa-chevron-up fa-fw"></span>
|
<span *ngIf="acc.isExpanded(entry.key)" class="fas fa-chevron-up fa-fw"></span>
|
||||||
<span *ngIf="!acc.isExpanded(entry.key)" class="fas fa-chevron-down fa-fw"></span>
|
<span *ngIf="!acc.isExpanded(entry.key)" class="fas fa-chevron-down fa-fw"></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,9 +20,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template ngbPanelContent>
|
<ng-template ngbPanelContent>
|
||||||
<ds-health-component [healthComponent]="entry.value" [healthComponentName]="entry.key"></ds-health-component>
|
<ds-health-component [healthComponent]="entry.value" [healthComponentName]="entry.key"
|
||||||
|
[id]="'health-panel-' + entry.key + '-content'" role="presentation">
|
||||||
|
</ds-health-component>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ngb-panel>
|
</ngb-panel>
|
||||||
</ngb-accordion>
|
</ngb-accordion>
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
<ng-container [ngSwitch]="status">
|
<ng-container [ngSwitch]="status">
|
||||||
<i *ngSwitchCase="HealthStatus.UP"
|
<i *ngSwitchCase="HealthStatus.UP"
|
||||||
class="fa fa-check-circle text-success ml-2 mt-1"
|
class="fa fa-check-circle text-success my-auto"
|
||||||
ngbTooltip="{{'health-page.status.ok.info' | translate}}" container="body" ></i>
|
ngbTooltip="{{'health-page.status.ok.info' | translate}}" container="body" ></i>
|
||||||
<i *ngSwitchCase="HealthStatus.UP_WITH_ISSUES"
|
<i *ngSwitchCase="HealthStatus.UP_WITH_ISSUES"
|
||||||
class="fa fa-exclamation-triangle text-warning ml-2 mt-1"
|
class="fa fa-exclamation-triangle text-warning my-auto"
|
||||||
ngbTooltip="{{'health-page.status.warning.info' | translate}}" container="body"></i>
|
ngbTooltip="{{'health-page.status.warning.info' | translate}}" container="body"></i>
|
||||||
<i *ngSwitchCase="HealthStatus.DOWN"
|
<i *ngSwitchCase="HealthStatus.DOWN"
|
||||||
class="fa fa-times-circle text-danger ml-2 mt-1"
|
class="fa fa-times-circle text-danger my-auto"
|
||||||
ngbTooltip="{{'health-page.status.error.info' | translate}}" container="body"></i>
|
ngbTooltip="{{'health-page.status.error.info' | translate}}" container="body"></i>
|
||||||
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -0,0 +1,3 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<div class="row row-offcanvas row-offcanvas-right">
|
<div class="row row-offcanvas row-offcanvas-right">
|
||||||
<div class="col-xs-12 col-sm-12 col-md-9">
|
<div class="col-xs-12 col-sm-12 col-md-9">
|
||||||
<form class="primary" [formGroup]="feedbackForm" (ngSubmit)="createFeedback()">
|
<form class="primary" [formGroup]="feedbackForm" (ngSubmit)="createFeedback()">
|
||||||
<h2>{{ 'info.feedback.head' | translate }}</h2>
|
<h1>{{ 'info.feedback.head' | translate }}</h1>
|
||||||
<p>{{ 'info.feedback.info' | translate }}</p>
|
<p>{{ 'info.feedback.info' | translate }}</p>
|
||||||
<fieldset class="col p-0">
|
<fieldset class="col p-0">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h3 class="mb-4">{{'bitstream-request-a-copy.header' | translate}}</h3>
|
<h1 class="mb-4">{{'bitstream-request-a-copy.header' | translate}}</h1>
|
||||||
<div *ngIf="canDownload$|async" class="alert alert-success">
|
<div *ngIf="canDownload$|async" class="alert alert-success">
|
||||||
<span>{{'bitstream-request-a-copy.alert.canDownload1' | translate}}</span>
|
<span>{{'bitstream-request-a-copy.alert.canDownload1' | translate}}</span>
|
||||||
<a [routerLink]="getBitstreamLink()">{{'bitstream-request-a-copy.alert.canDownload2'| translate}}</a>
|
<a [routerLink]="getBitstreamLink()">{{'bitstream-request-a-copy.alert.canDownload2'| translate}}</a>
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<ng-container *ngIf="bundles">
|
<ng-container *ngIf="bundles">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 mb-2">
|
<div class="col-12 mb-2">
|
||||||
<h2>{{'item.bitstreams.upload.title' | translate}}</h2>
|
<h1>{{'item.bitstreams.upload.title' | translate}}</h1>
|
||||||
<ng-container *ngVar="(itemRD$ | async)?.payload as item">
|
<ng-container *ngVar="(itemRD$ | async)?.payload as item">
|
||||||
<div *ngIf="item">
|
<div *ngIf="item">
|
||||||
<span class="font-weight-bold">{{'item.bitstreams.upload.item' | translate}}</span>
|
<span class="font-weight-bold">{{'item.bitstreams.upload.item' | translate}}</span>
|
||||||
|
@@ -1,14 +1,16 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h2 class="border-bottom">{{'item.edit.head' | translate}}</h2>
|
<h1 class="border-bottom">{{'item.edit.head' | translate}}</h1>
|
||||||
<div class="pt-2">
|
<div class="pt-2">
|
||||||
<ul class="nav nav-tabs justify-content-start" role="tablist">
|
<ul class="nav nav-tabs justify-content-start" role="tablist">
|
||||||
<li *ngFor="let page of pages" class="nav-item" [attr.aria-selected]="page.page === currentPage" role="tab">
|
<li *ngFor="let page of pages" class="nav-item" role="presentation">
|
||||||
<a *ngIf="(page.enabled | async)"
|
<a *ngIf="(page.enabled | async)"
|
||||||
|
[attr.aria-selected]="page.page === currentPage"
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
[ngClass]="{'active' : page.page === currentPage}"
|
[ngClass]="{'active' : page.page === currentPage}"
|
||||||
[routerLink]="['./' + page.page]">
|
[routerLink]="['./' + page.page]"
|
||||||
|
role="tab">
|
||||||
{{'item.edit.tabs.' + page.page + '.head' | translate}}
|
{{'item.edit.tabs.' + page.page + '.head' | translate}}
|
||||||
</a>
|
</a>
|
||||||
<span [ngbTooltip]="'item.edit.tabs.disabled.tooltip' | translate">
|
<span [ngbTooltip]="'item.edit.tabs.disabled.tooltip' | translate">
|
||||||
|
@@ -1,21 +1,25 @@
|
|||||||
<div class="item-bitstreams" *ngVar="(bundles$ | async) as bundles">
|
<div class="item-bitstreams" *ngVar="(bundles$ | async) as bundles">
|
||||||
<div class="button-row top d-flex mt-2 space-children-mr">
|
<div class="button-row top d-flex mt-2 space-children-mr">
|
||||||
<button class="mr-auto btn btn-success"
|
<button class="mr-auto btn btn-success"
|
||||||
|
[attr.aria-label]="'item.edit.bitstreams.upload-button' | translate"
|
||||||
[routerLink]="[itemPageRoute, 'bitstreams', 'new']"><i
|
[routerLink]="[itemPageRoute, 'bitstreams', 'new']"><i
|
||||||
class="fas fa-upload"></i>
|
class="fas fa-upload"></i>
|
||||||
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.upload-button" | translate}}</span>
|
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.upload-button" | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
||||||
|
[attr.aria-label]="'item.edit.bitstreams.reinstate-button' | translate"
|
||||||
(click)="reinstate()"><i
|
(click)="reinstate()"><i
|
||||||
class="fas fa-undo-alt"></i>
|
class="fas fa-undo-alt"></i>
|
||||||
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.reinstate-button" | translate}}</span>
|
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.reinstate-button" | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async) || submitting"
|
<button class="btn btn-primary" [disabled]="!(hasChanges() | async) || submitting"
|
||||||
|
[attr.aria-label]="'item.edit.bitstreams.save-button' | translate"
|
||||||
(click)="submit()"><i
|
(click)="submit()"><i
|
||||||
class="fas fa-save"></i>
|
class="fas fa-save"></i>
|
||||||
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.save-button" | translate}}</span>
|
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.save-button" | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
<button class="btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||||
|
[attr.aria-label]="'item.edit.bitstreams.discard-button' | translate"
|
||||||
[disabled]="!(hasChanges() | async) || submitting"
|
[disabled]="!(hasChanges() | async) || submitting"
|
||||||
(click)="discard()"><i
|
(click)="discard()"><i
|
||||||
class="fas fa-times"></i>
|
class="fas fa-times"></i>
|
||||||
@@ -49,16 +53,19 @@
|
|||||||
<div class="button-row bottom">
|
<div class="button-row bottom">
|
||||||
<div class="mt-4 float-right space-children-mr ml-gap">
|
<div class="mt-4 float-right space-children-mr ml-gap">
|
||||||
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
||||||
|
[attr.aria-label]="'item.edit.bitstreams.reinstate-button' | translate"
|
||||||
(click)="reinstate()"><i
|
(click)="reinstate()"><i
|
||||||
class="fas fa-undo-alt"></i>
|
class="fas fa-undo-alt"></i>
|
||||||
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.reinstate-button" | translate}}</span>
|
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.reinstate-button" | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async) || submitting"
|
<button class="btn btn-primary" [disabled]="!(hasChanges() | async) || submitting"
|
||||||
|
[attr.aria-label]="'item.edit.bitstreams.save-button' | translate"
|
||||||
(click)="submit()"><i
|
(click)="submit()"><i
|
||||||
class="fas fa-save"></i>
|
class="fas fa-save"></i>
|
||||||
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.save-button" | translate}}</span>
|
<span class="d-none d-sm-inline"> {{"item.edit.bitstreams.save-button" | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
<button class="btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||||
|
[attr.aria-label]="'item.edit.bitstreams.discard-button' | translate"
|
||||||
[disabled]="!(hasChanges() | async) || submitting"
|
[disabled]="!(hasChanges() | async) || submitting"
|
||||||
(click)="discard()"><i
|
(click)="discard()"><i
|
||||||
class="fas fa-times"></i>
|
class="fas fa-times"></i>
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
<div class="btn-group bundle-action-buttons">
|
<div class="btn-group bundle-action-buttons">
|
||||||
<button [routerLink]="[itemPageRoute, 'bitstreams', 'new']"
|
<button [routerLink]="[itemPageRoute, 'bitstreams', 'new']"
|
||||||
[queryParams]="{bundle: bundle.id}"
|
[queryParams]="{bundle: bundle.id}"
|
||||||
|
[attr.aria-label]="'item.edit.bitstreams.bundle.edit.buttons.upload' | translate"
|
||||||
class="btn btn-outline-success btn-sm"
|
class="btn btn-outline-success btn-sm"
|
||||||
title="{{'item.edit.bitstreams.bundle.edit.buttons.upload' | translate}}">
|
title="{{'item.edit.bitstreams.bundle.edit.buttons.upload' | translate}}">
|
||||||
<i class="fas fa-upload fa-fw"></i>
|
<i class="fas fa-upload fa-fw"></i>
|
||||||
|
@@ -26,21 +26,25 @@
|
|||||||
<div class="text-center w-100">
|
<div class="text-center w-100">
|
||||||
<div class="btn-group relationship-action-buttons">
|
<div class="btn-group relationship-action-buttons">
|
||||||
<a *ngIf="bitstreamDownloadUrl != null" [routerLink]="bitstreamDownloadUrl"
|
<a *ngIf="bitstreamDownloadUrl != null" [routerLink]="bitstreamDownloadUrl"
|
||||||
class="btn btn-outline-primary btn-sm"
|
[attr.aria-label]="'item.edit.bitstreams.edit.buttons.download' | translate"
|
||||||
title="{{'item.edit.bitstreams.edit.buttons.download' | translate}}"
|
class="btn btn-outline-primary btn-sm"
|
||||||
[attr.data-test]="'download-button' | dsBrowserOnly">
|
title="{{'item.edit.bitstreams.edit.buttons.download' | translate}}"
|
||||||
|
[attr.data-test]="'download-button' | dsBrowserOnly">
|
||||||
<i class="fas fa-download fa-fw"></i>
|
<i class="fas fa-download fa-fw"></i>
|
||||||
</a>
|
</a>
|
||||||
<button [routerLink]="['/bitstreams/', bitstream.id, 'edit']" class="btn btn-outline-primary btn-sm"
|
<button [routerLink]="['/bitstreams/', bitstream.id, 'edit']" class="btn btn-outline-primary btn-sm"
|
||||||
|
[attr.aria-label]="'item.edit.bitstreams.edit.buttons.edit' | translate"
|
||||||
title="{{'item.edit.bitstreams.edit.buttons.edit' | translate}}">
|
title="{{'item.edit.bitstreams.edit.buttons.edit' | translate}}">
|
||||||
<i class="fas fa-edit fa-fw"></i>
|
<i class="fas fa-edit fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
<button [disabled]="!canRemove()" (click)="remove()"
|
<button [disabled]="!canRemove()" (click)="remove()"
|
||||||
|
[attr.aria-label]="'item.edit.bitstreams.edit.buttons.remove' | translate"
|
||||||
class="btn btn-outline-danger btn-sm"
|
class="btn btn-outline-danger btn-sm"
|
||||||
title="{{'item.edit.bitstreams.edit.buttons.remove' | translate}}">
|
title="{{'item.edit.bitstreams.edit.buttons.remove' | translate}}">
|
||||||
<i class="fas fa-trash-alt fa-fw"></i>
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
</button>
|
</button>
|
||||||
<button [disabled]="!canUndo()" (click)="undo()"
|
<button [disabled]="!canUndo()" (click)="undo()"
|
||||||
|
[attr.aria-label]="'item.edit.bitstreams.edit.buttons.undo' | translate"
|
||||||
class="btn btn-outline-warning btn-sm"
|
class="btn btn-outline-warning btn-sm"
|
||||||
title="{{'item.edit.bitstreams.edit.buttons.undo' | translate}}">
|
title="{{'item.edit.bitstreams.edit.buttons.undo' | translate}}">
|
||||||
<i class="fas fa-undo-alt fa-fw"></i>
|
<i class="fas fa-undo-alt fa-fw"></i>
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
<p>{{'item.edit.item-mapper.description' | translate}}</p>
|
<p>{{'item.edit.item-mapper.description' | translate}}</p>
|
||||||
|
|
||||||
<ul ngbNav (navChange)="tabChange($event)" [destroyOnHide]="true" #tabs="ngbNav" class="nav-tabs">
|
<ul ngbNav (navChange)="tabChange($event)" [destroyOnHide]="true" #tabs="ngbNav" class="nav-tabs">
|
||||||
<li [ngbNavItem]="'browseTab'">
|
<li [ngbNavItem]="'browseTab'" role="presentation">
|
||||||
<a ngbNavLink>{{'item.edit.item-mapper.tabs.browse' | translate}}</a>
|
<a ngbNavLink>{{'item.edit.item-mapper.tabs.browse' | translate}}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li [ngbNavItem]="'mapTab'">
|
<li [ngbNavItem]="'mapTab'" role="presentation">
|
||||||
<a ngbNavLink>{{'item.edit.item-mapper.tabs.map' | translate}}</a>
|
<a ngbNavLink>{{'item.edit.item-mapper.tabs.map' | translate}}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="container mt-3">
|
<div class="container mt-3">
|
||||||
<h3>{{'item.edit.curate.title' |translate:{item: (itemName$ |async)} }}</h3>
|
<h2>{{'item.edit.curate.title' |translate:{item: (itemName$ |async)} }}</h2>
|
||||||
<ds-curation-form
|
<ds-curation-form
|
||||||
*ngIf="dsoRD$ | async as dsoRD"
|
*ngIf="dsoRD$ | async as dsoRD"
|
||||||
[dsoHandle]="dsoRD?.payload.handle"
|
[dsoHandle]="dsoRD?.payload.handle"
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
|
||||||
<h2>{{headerMessage | translate: {id: item.handle} }}</h2>
|
<h1>{{headerMessage | translate: {id: item.handle} }}</h1>
|
||||||
<p>{{descriptionMessage | translate}}</p>
|
<p>{{descriptionMessage | translate}}</p>
|
||||||
<ds-modify-item-overview [item]="item"></ds-modify-item-overview>
|
<ds-modify-item-overview [item]="item"></ds-modify-item-overview>
|
||||||
|
|
||||||
|
@@ -171,7 +171,7 @@ describe('ItemDeleteComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should render a page with messages based on the \'delete\' messageKey', () => {
|
it('should render a page with messages based on the \'delete\' messageKey', () => {
|
||||||
const header = fixture.debugElement.query(By.css('h2')).nativeElement;
|
const header = fixture.debugElement.query(By.css('h1')).nativeElement;
|
||||||
expect(header.innerHTML).toContain('item.edit.delete.header');
|
expect(header.innerHTML).toContain('item.edit.delete.header');
|
||||||
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
||||||
expect(description.innerHTML).toContain('item.edit.delete.description');
|
expect(description.innerHTML).toContain('item.edit.delete.description');
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h2>{{'item.edit.move.head' | translate: {id: (itemRD$ | async)?.payload?.handle} }}</h2>
|
<h1>{{'item.edit.move.head' | translate: {id: (itemRD$ | async)?.payload?.handle} }}</h1>
|
||||||
<p>{{'item.edit.move.description' | translate}}</p>
|
<p>{{'item.edit.move.description' | translate}}</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
@@ -80,7 +80,7 @@ describe('ItemPrivateComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should render a page with messages based on the \'private\' messageKey', () => {
|
it('should render a page with messages based on the \'private\' messageKey', () => {
|
||||||
const header = fixture.debugElement.query(By.css('h2')).nativeElement;
|
const header = fixture.debugElement.query(By.css('h1')).nativeElement;
|
||||||
expect(header.innerHTML).toContain('item.edit.private.header');
|
expect(header.innerHTML).toContain('item.edit.private.header');
|
||||||
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
||||||
expect(description.innerHTML).toContain('item.edit.private.description');
|
expect(description.innerHTML).toContain('item.edit.private.description');
|
||||||
|
@@ -74,7 +74,7 @@ describe('ItemPublicComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should render a page with messages based on the \'public\' messageKey', () => {
|
it('should render a page with messages based on the \'public\' messageKey', () => {
|
||||||
const header = fixture.debugElement.query(By.css('h2')).nativeElement;
|
const header = fixture.debugElement.query(By.css('h1')).nativeElement;
|
||||||
expect(header.innerHTML).toContain('item.edit.public.header');
|
expect(header.innerHTML).toContain('item.edit.public.header');
|
||||||
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
||||||
expect(description.innerHTML).toContain('item.edit.public.description');
|
expect(description.innerHTML).toContain('item.edit.public.description');
|
||||||
|
@@ -76,7 +76,7 @@ describe('ItemReinstateComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should render a page with messages based on the \'reinstate\' messageKey', () => {
|
it('should render a page with messages based on the \'reinstate\' messageKey', () => {
|
||||||
const header = fixture.debugElement.query(By.css('h2')).nativeElement;
|
const header = fixture.debugElement.query(By.css('h1')).nativeElement;
|
||||||
expect(header.innerHTML).toContain('item.edit.reinstate.header');
|
expect(header.innerHTML).toContain('item.edit.reinstate.header');
|
||||||
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
||||||
expect(description.innerHTML).toContain('item.edit.reinstate.description');
|
expect(description.innerHTML).toContain('item.edit.reinstate.description');
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
<h5>
|
<h2 class="h4">
|
||||||
{{getRelationshipMessageKey() | async | translate}}
|
{{getRelationshipMessageKey() | async | translate}}
|
||||||
<button class="ml-2 btn btn-success" [disabled]="(hasChanges | async)" (click)="openLookup()">
|
<button class="ml-2 btn btn-success" [disabled]="(hasChanges | async)" (click)="openLookup()">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
<span class="d-none d-sm-inline"> {{"item.edit.relationships.edit.buttons.add" | translate}}</span>
|
<span class="d-none d-sm-inline"> {{"item.edit.relationships.edit.buttons.add" | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
</h5>
|
</h2>
|
||||||
<ng-container *ngVar="updates$ | async as updates">
|
<ng-container *ngVar="updates$ | async as updates">
|
||||||
<ng-container *ngIf="updates && !(loading$ | async)">
|
<ng-container *ngIf="updates && !(loading$ | async)">
|
||||||
<ng-container *ngVar="updates | dsObjectValues as updateValues">
|
<ng-container *ngVar="updates | dsObjectValues as updateValues">
|
||||||
|
@@ -74,7 +74,7 @@ describe('ItemWithdrawComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should render a page with messages based on the \'withdraw\' messageKey', () => {
|
it('should render a page with messages based on the \'withdraw\' messageKey', () => {
|
||||||
const header = fixture.debugElement.query(By.css('h2')).nativeElement;
|
const header = fixture.debugElement.query(By.css('h1')).nativeElement;
|
||||||
expect(header.innerHTML).toContain('item.edit.withdraw.header');
|
expect(header.innerHTML).toContain('item.edit.withdraw.header');
|
||||||
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
||||||
expect(description.innerHTML).toContain('item.edit.withdraw.description');
|
expect(description.innerHTML).toContain('item.edit.withdraw.description');
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h2>{{headerMessage | translate: {id: item.handle} }}</h2>
|
<h1>{{headerMessage | translate: {id: item.handle} }}</h1>
|
||||||
<p>{{descriptionMessage | translate}}</p>
|
<p>{{descriptionMessage | translate}}</p>
|
||||||
<ds-modify-item-overview [item]="item"></ds-modify-item-overview>
|
<ds-modify-item-overview [item]="item"></ds-modify-item-overview>
|
||||||
<div class="space-children-mr">
|
<div class="space-children-mr">
|
||||||
|
@@ -111,7 +111,7 @@ describe('AbstractSimpleItemActionComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should render a page with messages based on the provided messageKey', () => {
|
it('should render a page with messages based on the provided messageKey', () => {
|
||||||
const header = fixture.debugElement.query(By.css('h2')).nativeElement;
|
const header = fixture.debugElement.query(By.css('h1')).nativeElement;
|
||||||
expect(header.innerHTML).toContain('item.edit.myEditAction.header');
|
expect(header.innerHTML).toContain('item.edit.myEditAction.header');
|
||||||
|
|
||||||
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
const description = fixture.debugElement.query(By.css('p')).nativeElement;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<ds-metadata-field-wrapper [label]="label | translate">
|
<ds-metadata-field-wrapper [label]="label | translate">
|
||||||
<div *ngVar="(originals$ | async)?.payload as originals">
|
<div *ngVar="(originals$ | async)?.payload as originals">
|
||||||
<div *ngIf="hasValuesInBundle(originals)">
|
<div *ngIf="hasValuesInBundle(originals)">
|
||||||
<h5 class="simple-view-element-header">{{"item.page.filesection.original.bundle" | translate}}</h5>
|
<h3 class="h5 simple-view-element-header">{{"item.page.filesection.original.bundle" | translate}}</h3>
|
||||||
<ds-pagination *ngIf="originals?.page?.length > 0"
|
<ds-pagination *ngIf="originals?.page?.length > 0"
|
||||||
[hideGear]="true"
|
[hideGear]="true"
|
||||||
[hidePagerWhenSinglePage]="true"
|
[hidePagerWhenSinglePage]="true"
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div *ngVar="(licenses$ | async)?.payload as licenses">
|
<div *ngVar="(licenses$ | async)?.payload as licenses">
|
||||||
<div *ngIf="hasValuesInBundle(licenses)">
|
<div *ngIf="hasValuesInBundle(licenses)">
|
||||||
<h5 class="simple-view-element-header">{{"item.page.filesection.license.bundle" | translate}}</h5>
|
<h3 class="h5 simple-view-element-header">{{"item.page.filesection.license.bundle" | translate}}</h3>
|
||||||
<ds-pagination *ngIf="licenses?.page?.length > 0"
|
<ds-pagination *ngIf="licenses?.page?.length > 0"
|
||||||
[hideGear]="true"
|
[hideGear]="true"
|
||||||
[hidePagerWhenSinglePage]="true"
|
[hidePagerWhenSinglePage]="true"
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user