mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge branch 'main' into w2p-71764_Features-support-part-2
Conflicts: src/app/+collection-page/collection-page-routing.module.ts src/app/+community-page/community-page-routing.module.ts src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts src/app/+item-page/edit-item-page/item-status/item-status.component.ts src/app/+item-page/item-page-routing.module.ts src/app/app-routing.module.ts src/app/core/core.module.ts src/app/shared/log-in/log-in.component.html src/app/shared/log-in/log-in.component.spec.ts src/app/shared/log-in/log-in.component.ts
This commit is contained in:
5
.github/pull_request_template.md
vendored
5
.github/pull_request_template.md
vendored
@@ -21,8 +21,5 @@ _This checklist provides a reminder of what we are going to look for when review
|
|||||||
- [ ] My PR is small in size (e.g. less than 1,000 lines of code, not including comments & specs/tests), or I have provided reasons as to why that's not possible.
|
- [ ] My PR is small in size (e.g. less than 1,000 lines of code, not including comments & specs/tests), or I have provided reasons as to why that's not possible.
|
||||||
- [ ] My PR passes [TSLint](https://palantir.github.io/tslint/) validation using `yarn run lint`
|
- [ ] My PR passes [TSLint](https://palantir.github.io/tslint/) validation using `yarn run lint`
|
||||||
- [ ] My PR includes [TypeDoc](https://typedoc.org/) comments for _all new (or modified) public methods and classes_. It also includes TypeDoc for large or complex private methods.
|
- [ ] My PR includes [TypeDoc](https://typedoc.org/) comments for _all new (or modified) public methods and classes_. It also includes TypeDoc for large or complex private methods.
|
||||||
- [ ] My PR passes all specs/tests and includes new/updated specs for any bug fixes, improvements or new features. A few reminders about what constitutes good tests:
|
- [ ] My PR passes all specs/tests and includes new/updated specs or tests based on the [Code Testing Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Testing+Guide).
|
||||||
* Include tests for different user types (if behavior differs), including: (1) Anonymous user, (2) Logged in user (non-admin), and (3) Administrator.
|
|
||||||
* Include tests for error scenarios, e.g. when errors/warnings should appear (or buttons should be disabled).
|
|
||||||
* For bug fixes, include a test that reproduces the bug and proves it is fixed. For clarity, it may be useful to provide the test in a separate commit from the bug fix.
|
|
||||||
- [ ] If my PR includes new, third-party dependencies (in `package.json`), I've made sure their licenses align with the [DSpace BSD License](https://github.com/DSpace/DSpace/blob/main/LICENSE) based on the [Licensing of Contributions](https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines#CodeContributionGuidelines-LicensingofContributions) documentation.
|
- [ ] If my PR includes new, third-party dependencies (in `package.json`), I've made sure their licenses align with the [DSpace BSD License](https://github.com/DSpace/DSpace/blob/main/LICENSE) based on the [Licensing of Contributions](https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines#CodeContributionGuidelines-LicensingofContributions) documentation.
|
||||||
|
25
.github/workflows/label_merge_conflicts.yml
vendored
Normal file
25
.github/workflows/label_merge_conflicts.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# This workflow checks open PRs for merge conflicts and labels them when conflicts are found
|
||||||
|
name: Check for merge conflicts
|
||||||
|
|
||||||
|
# Run whenever the "main" branch is updated
|
||||||
|
# NOTE: This means merge conflicts are only checked for when a PR is merged to main.
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
triage:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
# See: https://github.com/mschilde/auto-label-merge-conflicts/
|
||||||
|
- name: Auto-label PRs with merge conflicts
|
||||||
|
uses: mschilde/auto-label-merge-conflicts@v2.0
|
||||||
|
# Add "merge conflict" label if a merge conflict is detected. Remove it when resolved.
|
||||||
|
# Note, the authentication token is created automatically
|
||||||
|
# See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token
|
||||||
|
with:
|
||||||
|
CONFLICT_LABEL_NAME: 'merge conflict'
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# Ignore errors
|
||||||
|
continue-on-error: true
|
@@ -51,10 +51,10 @@ before_script:
|
|||||||
|
|
||||||
script:
|
script:
|
||||||
# build app and run all tests
|
# build app and run all tests
|
||||||
- ng lint
|
- ng lint || travis_terminate 1;
|
||||||
- travis_wait yarn run build:prod
|
- travis_wait yarn run build:prod || travis_terminate 1;
|
||||||
- yarn test:headless
|
- yarn test:headless || travis_terminate 1;
|
||||||
- yarn run e2e:ci
|
- yarn run e2e:ci || travis_terminate 1;
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
# Shutdown docker after everything runs
|
# Shutdown docker after everything runs
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
"builder": "@angular-builders/custom-webpack:browser",
|
"builder": "@angular-builders/custom-webpack:browser",
|
||||||
"options": {
|
"options": {
|
||||||
"customWebpackConfig": {
|
"customWebpackConfig": {
|
||||||
"path": "./webpack/webpack.common.ts",
|
"path": "./webpack/webpack.browser.ts",
|
||||||
"mergeStrategies": {
|
"mergeStrategies": {
|
||||||
"loaders": "prepend"
|
"loaders": "prepend"
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,8 @@
|
|||||||
"tsConfig": "tsconfig.app.json",
|
"tsConfig": "tsconfig.app.json",
|
||||||
"aot": false,
|
"aot": false,
|
||||||
"assets": [
|
"assets": [
|
||||||
"src/assets"
|
"src/assets",
|
||||||
|
"src/robots.txt"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.scss"
|
"src/styles.scss"
|
||||||
@@ -84,7 +85,7 @@
|
|||||||
"builder": "@angular-builders/custom-webpack:karma",
|
"builder": "@angular-builders/custom-webpack:karma",
|
||||||
"options": {
|
"options": {
|
||||||
"customWebpackConfig": {
|
"customWebpackConfig": {
|
||||||
"path": "./webpack/webpack.common.ts",
|
"path": "./webpack/webpack.test.ts",
|
||||||
"mergeStrategies": {
|
"mergeStrategies": {
|
||||||
"loaders": "prepend"
|
"loaders": "prepend"
|
||||||
}
|
}
|
||||||
|
@@ -146,6 +146,7 @@
|
|||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"fork-ts-checker-webpack-plugin": "^0.4.10",
|
"fork-ts-checker-webpack-plugin": "^0.4.10",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
|
"http-proxy-middleware": "^1.0.5",
|
||||||
"jasmine-core": "^3.3.0",
|
"jasmine-core": "^3.3.0",
|
||||||
"jasmine-marbles": "0.3.1",
|
"jasmine-marbles": "0.3.1",
|
||||||
"jasmine-spec-reporter": "~4.2.1",
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
|
@@ -54,6 +54,13 @@ import(environmentFilePath)
|
|||||||
function generateEnvironmentFile(file: GlobalConfig): void {
|
function generateEnvironmentFile(file: GlobalConfig): void {
|
||||||
file.production = production;
|
file.production = production;
|
||||||
buildBaseUrls(file);
|
buildBaseUrls(file);
|
||||||
|
|
||||||
|
// TODO remove workaround in beta 5
|
||||||
|
if (file.rest.nameSpace.match("(.*)/api/?$") !== null) {
|
||||||
|
const newValue = getNameSpace(file.rest.nameSpace);
|
||||||
|
console.log(colors.white.bgMagenta.bold(`The rest.nameSpace property in your environment file or in your DSPACE_REST_NAMESPACE environment variable ends with '/api'.\nThis is deprecated. As '/api' isn't configurable on the rest side, it shouldn't be repeated in every environment file.\nPlease change the rest nameSpace to '${newValue}'`));
|
||||||
|
}
|
||||||
|
|
||||||
const contents = `export const environment = ` + JSON.stringify(file);
|
const contents = `export const environment = ` + JSON.stringify(file);
|
||||||
writeFile(targetPath, contents, (err) => {
|
writeFile(targetPath, contents, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -112,5 +119,16 @@ function getPort(port: number): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getNameSpace(nameSpace: string): string {
|
function getNameSpace(nameSpace: string): string {
|
||||||
return nameSpace ? nameSpace.charAt(0) === '/' ? nameSpace : '/' + nameSpace : '';
|
// TODO remove workaround in beta 5
|
||||||
|
const apiMatches = nameSpace.match("(.*)/api/?$");
|
||||||
|
if (apiMatches != null) {
|
||||||
|
let newValue = '/'
|
||||||
|
if (hasValue(apiMatches[1])) {
|
||||||
|
newValue = apiMatches[1];
|
||||||
|
}
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nameSpace ? nameSpace.charAt(0) === '/' ? nameSpace : '/' + nameSpace : '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
74
server.ts
74
server.ts
@@ -15,7 +15,6 @@
|
|||||||
* import for `ngExpressEngine`.
|
* import for `ngExpressEngine`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'zone.js/dist/zone-node';
|
|
||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import 'rxjs';
|
import 'rxjs';
|
||||||
|
|
||||||
@@ -33,6 +32,8 @@ import { enableProdMode, NgModuleFactory, Type } from '@angular/core';
|
|||||||
|
|
||||||
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
|
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
|
||||||
import { environment } from './src/environments/environment';
|
import { environment } from './src/environments/environment';
|
||||||
|
import { createProxyMiddleware } from 'http-proxy-middleware';
|
||||||
|
import { hasValue } from './src/app/shared/empty.util';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set path for the browser application's dist folder
|
* Set path for the browser application's dist folder
|
||||||
@@ -98,7 +99,6 @@ app.engine('html', (_, options, callback) =>
|
|||||||
/*
|
/*
|
||||||
* Register the view engines for html and ejs
|
* Register the view engines for html and ejs
|
||||||
*/
|
*/
|
||||||
app.set('view engine', 'ejs');
|
|
||||||
app.set('view engine', 'html');
|
app.set('view engine', 'html');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -106,6 +106,11 @@ app.set('view engine', 'html');
|
|||||||
*/
|
*/
|
||||||
app.set('views', DIST_FOLDER);
|
app.set('views', DIST_FOLDER);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy the sitemaps
|
||||||
|
*/
|
||||||
|
app.use('/sitemap**', createProxyMiddleware({ target: `${environment.rest.baseUrl}/sitemaps`, changeOrigin: true }));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adds a cache control header to the response
|
* Adds a cache control header to the response
|
||||||
* The cache control value can be configured in the environments file and defaults to max-age=60
|
* The cache control value can be configured in the environments file and defaults to max-age=60
|
||||||
@@ -125,56 +130,27 @@ app.get('*.*', cacheControl, express.static(DIST_FOLDER, { index: false }));
|
|||||||
* The callback function to serve server side angular
|
* The callback function to serve server side angular
|
||||||
*/
|
*/
|
||||||
function ngApp(req, res) {
|
function ngApp(req, res) {
|
||||||
// Object to be set to window.dspace when CSR is used
|
|
||||||
// this allows us to pass the info in the original request
|
|
||||||
// to the dspace7-angular instance running in the client's browser
|
|
||||||
const dspace = {
|
|
||||||
originalRequest: {
|
|
||||||
headers: req.headers,
|
|
||||||
body: req.body,
|
|
||||||
method: req.method,
|
|
||||||
params: req.params,
|
|
||||||
reportProgress: req.reportProgress,
|
|
||||||
withCredentials: req.withCredentials,
|
|
||||||
responseType: req.responseType,
|
|
||||||
urlWithParams: req.urlWithParams
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// callback function for the case when SSR throws an error.
|
|
||||||
function onHandleError(parentZoneDelegate, currentZone, targetZone, error) {
|
|
||||||
if (!res._headerSent) {
|
|
||||||
console.warn('Error in SSR, serving for direct CSR. Error details : ', error);
|
|
||||||
res.sendFile('index.csr.ejs', {
|
|
||||||
root: DIST_FOLDER,
|
|
||||||
scripts: `<script>window.dspace = ${JSON.stringify(dspace)}</script>`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (environment.universal.preboot) {
|
if (environment.universal.preboot) {
|
||||||
// If preboot is enabled, create a new zone for SSR, and
|
res.render(DIST_FOLDER + '/index.html', {
|
||||||
// register the error handler for when it throws an error
|
req,
|
||||||
Zone.current.fork({ name: 'CSR fallback', onHandleError }).run(() => {
|
res,
|
||||||
res.render(DIST_FOLDER + '/index.html', {
|
preboot: environment.universal.preboot,
|
||||||
req,
|
async: environment.universal.async,
|
||||||
res,
|
time: environment.universal.time,
|
||||||
preboot: environment.universal.preboot,
|
baseUrl: environment.ui.nameSpace,
|
||||||
async: environment.universal.async,
|
originUrl: environment.ui.baseUrl,
|
||||||
time: environment.universal.time,
|
requestUrl: req.originalUrl
|
||||||
baseUrl: environment.ui.nameSpace,
|
}, (err) => {
|
||||||
originUrl: environment.ui.baseUrl,
|
console.warn('Error in SSR, serving for direct CSR.');
|
||||||
requestUrl: req.originalUrl
|
if (hasValue(err)) {
|
||||||
});
|
console.warn('Error details : ', err);
|
||||||
});
|
}
|
||||||
|
res.sendFile(DIST_FOLDER + '/index.html');
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
// If preboot is disabled, just serve the client side ejs template and pass it the required
|
// If preboot is disabled, just serve the client
|
||||||
// variables
|
|
||||||
console.log('Universal off, serving for direct CSR');
|
console.log('Universal off, serving for direct CSR');
|
||||||
res.render('index-csr.ejs', {
|
res.sendFile(DIST_FOLDER + '/index.html');
|
||||||
root: DIST_FOLDER,
|
|
||||||
scripts: `<script>window.dspace = ${JSON.stringify(dspace)}</script>`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||||
|
import { getAccessControlModuleRoute } from '../admin-routing-paths';
|
||||||
|
|
||||||
|
export const GROUP_EDIT_PATH = 'groups';
|
||||||
|
|
||||||
|
export function getGroupEditRoute(id: string) {
|
||||||
|
return new URLCombiner(getAccessControlModuleRoute(), GROUP_EDIT_PATH, id).toString();
|
||||||
|
}
|
@@ -3,14 +3,7 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { EPeopleRegistryComponent } from './epeople-registry/epeople-registry.component';
|
import { EPeopleRegistryComponent } from './epeople-registry/epeople-registry.component';
|
||||||
import { GroupFormComponent } from './group-registry/group-form/group-form.component';
|
import { GroupFormComponent } from './group-registry/group-form/group-form.component';
|
||||||
import { GroupsRegistryComponent } from './group-registry/groups-registry.component';
|
import { GroupsRegistryComponent } from './group-registry/groups-registry.component';
|
||||||
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
import { GROUP_EDIT_PATH } from './admin-access-control-routing-paths';
|
||||||
import { getAccessControlModulePath } from '../admin-routing.module';
|
|
||||||
|
|
||||||
export const GROUP_EDIT_PATH = 'groups';
|
|
||||||
|
|
||||||
export function getGroupEditPath(id: string) {
|
|
||||||
return new URLCombiner(getAccessControlModulePath(), GROUP_EDIT_PATH, id).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@@ -7,87 +7,88 @@
|
|||||||
<ds-eperson-form *ngIf="isEPersonFormShown" (submitForm)="forceUpdateEPeople()"
|
<ds-eperson-form *ngIf="isEPersonFormShown" (submitForm)="forceUpdateEPeople()"
|
||||||
(cancelForm)="isEPersonFormShown = false"></ds-eperson-form>
|
(cancelForm)="isEPersonFormShown = false"></ds-eperson-form>
|
||||||
|
|
||||||
<div *ngIf="!isEPersonFormShown" class="button-row top d-flex pb-2">
|
<div *ngIf="!isEPersonFormShown">
|
||||||
<button class="mr-auto btn btn-success addEPerson-button"
|
<div class="button-row top d-flex pb-2">
|
||||||
(click)="isEPersonFormShown = true">
|
<button class="mr-auto btn btn-success addEPerson-button"
|
||||||
<i class="fas fa-plus"></i>
|
(click)="isEPersonFormShown = true">
|
||||||
<span class="d-none d-sm-inline">{{labelPrefix + 'button.add' | translate}}</span>
|
<i class="fas fa-plus"></i>
|
||||||
</button>
|
<span class="d-none d-sm-inline">{{labelPrefix + 'button.add' | translate}}</span>
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
<h3 id="search" class="border-bottom pb-2">{{labelPrefix + 'search.head' | translate}}
|
|
||||||
<button (click)="clearFormAndResetResult();"
|
|
||||||
class="btn btn-primary float-right">{{labelPrefix + 'button.see-all' | translate}}</button>
|
|
||||||
</h3>
|
|
||||||
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="row">
|
|
||||||
<div class="col-12 col-sm-3">
|
|
||||||
<select name="scope" id="scope" formControlName="scope" class="form-control" aria-label="Search scope">
|
|
||||||
<option value="metadata">{{labelPrefix + 'search.scope.metadata' | translate}}</option>
|
|
||||||
<option value="email">{{labelPrefix + 'search.scope.email' | translate}}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-9 col-12">
|
|
||||||
<div class="form-group input-group">
|
<h3 id="search" class="border-bottom pb-2">{{labelPrefix + 'search.head' | translate}}
|
||||||
<input type="text" name="query" id="query" formControlName="query"
|
<button (click)="clearFormAndResetResult();"
|
||||||
class="form-control" aria-label="Search input">
|
class="btn btn-primary float-right">{{labelPrefix + 'button.see-all' | translate}}</button>
|
||||||
<span class="input-group-append">
|
</h3>
|
||||||
<button type="submit"
|
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="row">
|
||||||
class="search-button btn btn-secondary">{{ labelPrefix + 'search.button' | translate }}</button>
|
<div class="col-12 col-sm-3">
|
||||||
</span>
|
<select name="scope" id="scope" formControlName="scope" class="form-control" aria-label="Search scope">
|
||||||
|
<option value="metadata">{{labelPrefix + 'search.scope.metadata' | translate}}</option>
|
||||||
|
<option value="email">{{labelPrefix + 'search.scope.email' | translate}}</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-sm-9 col-12">
|
||||||
|
<div class="form-group input-group">
|
||||||
|
<input type="text" name="query" id="query" formControlName="query"
|
||||||
|
class="form-control" aria-label="Search input">
|
||||||
|
<span class="input-group-append">
|
||||||
|
<button type="submit"
|
||||||
|
class="search-button btn btn-secondary">{{ labelPrefix + 'search.button' | translate }}</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<ds-pagination
|
||||||
|
*ngIf="(ePeople | async)?.payload?.totalElements > 0"
|
||||||
|
[paginationOptions]="config"
|
||||||
|
[pageInfoState]="(ePeople | async)?.payload"
|
||||||
|
[collectionSize]="(ePeople | async)?.payload?.totalElements"
|
||||||
|
[hideGear]="true"
|
||||||
|
[hidePagerWhenSinglePage]="true"
|
||||||
|
(pageChange)="onPageChange($event)">
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="epeople" class="table table-striped table-hover table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">{{labelPrefix + 'table.id' | translate}}</th>
|
||||||
|
<th scope="col">{{labelPrefix + 'table.name' | translate}}</th>
|
||||||
|
<th scope="col">{{labelPrefix + 'table.email' | translate}}</th>
|
||||||
|
<th>{{labelPrefix + 'table.edit' | translate}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let eperson of (ePeople | async)?.payload?.page"
|
||||||
|
[ngClass]="{'table-primary' : isActive(eperson) | async}">
|
||||||
|
<td>{{eperson.id}}</td>
|
||||||
|
<td>{{eperson.name}}</td>
|
||||||
|
<td>{{eperson.email}}</td>
|
||||||
|
<td>
|
||||||
|
<div class="btn-group edit-field">
|
||||||
|
<button (click)="toggleEditEPerson(eperson)"
|
||||||
|
class="btn btn-outline-primary btn-sm access-control-editEPersonButton"
|
||||||
|
title="{{labelPrefix + 'table.edit.buttons.edit' | translate: {name: eperson.name} }}">
|
||||||
|
<i class="fas fa-edit fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
<button (click)="deleteEPerson(eperson)"
|
||||||
|
class="btn btn-outline-danger btn-sm access-control-deleteEPersonButton"
|
||||||
|
title="{{labelPrefix + 'table.edit.buttons.remove' | translate: {name: eperson.name} }}">
|
||||||
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ds-pagination>
|
||||||
|
|
||||||
|
<div *ngIf="(ePeople | async)?.payload?.totalElements == 0" class="alert alert-info w-100 mb-2" role="alert">
|
||||||
|
{{labelPrefix + 'no-items' | translate}}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
|
|
||||||
<ds-pagination
|
|
||||||
*ngIf="(ePeople | async)?.payload?.totalElements > 0"
|
|
||||||
[paginationOptions]="config"
|
|
||||||
[pageInfoState]="(ePeople | async)?.payload"
|
|
||||||
[collectionSize]="(ePeople | async)?.payload?.totalElements"
|
|
||||||
[hideGear]="true"
|
|
||||||
[hidePagerWhenSinglePage]="true"
|
|
||||||
(pageChange)="onPageChange($event)">
|
|
||||||
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table id="epeople" class="table table-striped table-hover table-bordered">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">{{labelPrefix + 'table.id' | translate}}</th>
|
|
||||||
<th scope="col">{{labelPrefix + 'table.name' | translate}}</th>
|
|
||||||
<th scope="col">{{labelPrefix + 'table.email' | translate}}</th>
|
|
||||||
<th>{{labelPrefix + 'table.edit' | translate}}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr *ngFor="let eperson of (ePeople | async)?.payload?.page"
|
|
||||||
[ngClass]="{'table-primary' : isActive(eperson) | async}">
|
|
||||||
<td>{{eperson.id}}</td>
|
|
||||||
<td>{{eperson.name}}</td>
|
|
||||||
<td>{{eperson.email}}</td>
|
|
||||||
<td>
|
|
||||||
<div class="btn-group edit-field">
|
|
||||||
<button (click)="toggleEditEPerson(eperson)"
|
|
||||||
class="btn btn-outline-primary btn-sm access-control-editEPersonButton"
|
|
||||||
title="{{labelPrefix + 'table.edit.buttons.edit' | translate: {name: eperson.name} }}">
|
|
||||||
<i class="fas fa-edit fa-fw"></i>
|
|
||||||
</button>
|
|
||||||
<button (click)="deleteEPerson(eperson)"
|
|
||||||
class="btn btn-outline-danger btn-sm access-control-deleteEPersonButton"
|
|
||||||
title="{{labelPrefix + 'table.edit.buttons.remove' | translate: {name: eperson.name} }}">
|
|
||||||
<i class="fas fa-trash-alt fa-fw"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</ds-pagination>
|
|
||||||
|
|
||||||
<div *ngIf="(ePeople | async)?.payload?.totalElements == 0" class="alert alert-info w-100 mb-2" role="alert">
|
|
||||||
{{labelPrefix + 'no-items' | translate}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -175,7 +175,7 @@ describe('EPeopleRegistryComponent', () => {
|
|||||||
it('editEPerson form is toggled', () => {
|
it('editEPerson form is toggled', () => {
|
||||||
const ePeopleIds = fixture.debugElement.queryAll(By.css('#epeople tr td:first-child'));
|
const ePeopleIds = fixture.debugElement.queryAll(By.css('#epeople tr td:first-child'));
|
||||||
ePersonDataServiceStub.getActiveEPerson().subscribe((activeEPerson: EPerson) => {
|
ePersonDataServiceStub.getActiveEPerson().subscribe((activeEPerson: EPerson) => {
|
||||||
if (activeEPerson === ePeopleIds[0].nativeElement.textContent) {
|
if (ePeopleIds[0] && activeEPerson === ePeopleIds[0].nativeElement.textContent) {
|
||||||
expect(component.isEPersonFormShown).toEqual(false);
|
expect(component.isEPersonFormShown).toEqual(false);
|
||||||
} else {
|
} else {
|
||||||
expect(component.isEPersonFormShown).toEqual(true);
|
expect(component.isEPersonFormShown).toEqual(true);
|
||||||
@@ -183,6 +183,10 @@ describe('EPeopleRegistryComponent', () => {
|
|||||||
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('EPerson search section is hidden', () => {
|
||||||
|
expect(fixture.debugElement.query(By.css('#search'))).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -0,0 +1,15 @@
|
|||||||
|
<div class="container">
|
||||||
|
<h2 id="header">{{'admin.metadata-import.page.header' | translate}}</h2>
|
||||||
|
<p>{{'admin.metadata-import.page.help' | translate}}</p>
|
||||||
|
|
||||||
|
<ds-file-dropzone-no-uploader
|
||||||
|
(onFileAdded)="setFile($event)"
|
||||||
|
[dropMessageLabel]="'admin.metadata-import.page.dropMsg'"
|
||||||
|
[dropMessageLabelReplacement]="'admin.metadata-import.page.dropMsgReplace'">
|
||||||
|
</ds-file-dropzone-no-uploader>
|
||||||
|
|
||||||
|
<button class="btn btn-secondary" id="backButton"
|
||||||
|
(click)="this.onReturn();">{{'admin.metadata-import.page.button.return' | translate}}</button>
|
||||||
|
<button class="btn btn-primary" id="proceedButton"
|
||||||
|
(click)="this.importMetadata();">{{'admin.metadata-import.page.button.proceed' | translate}}</button>
|
||||||
|
</div>
|
@@ -0,0 +1,151 @@
|
|||||||
|
import { Location } from '@angular/common';
|
||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||||
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
|
import {
|
||||||
|
METADATA_IMPORT_SCRIPT_NAME,
|
||||||
|
ScriptDataService
|
||||||
|
} from '../../core/data/processes/script-data.service';
|
||||||
|
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||||
|
import { ProcessParameter } from '../../process-page/processes/process-parameter.model';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
|
||||||
|
import { FileValueAccessorDirective } from '../../shared/utils/file-value-accessor.directive';
|
||||||
|
import { FileValidator } from '../../shared/utils/require-file.validator';
|
||||||
|
import { MetadataImportPageComponent } from './metadata-import-page.component';
|
||||||
|
|
||||||
|
describe('MetadataImportPageComponent', () => {
|
||||||
|
let comp: MetadataImportPageComponent;
|
||||||
|
let fixture: ComponentFixture<MetadataImportPageComponent>;
|
||||||
|
|
||||||
|
let user;
|
||||||
|
|
||||||
|
let notificationService: NotificationsServiceStub;
|
||||||
|
let scriptService: any;
|
||||||
|
let router;
|
||||||
|
let authService;
|
||||||
|
let locationStub;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
notificationService = new NotificationsServiceStub();
|
||||||
|
scriptService = jasmine.createSpyObj('scriptService',
|
||||||
|
{
|
||||||
|
invoke: observableOf({
|
||||||
|
response:
|
||||||
|
{
|
||||||
|
isSuccessful: true,
|
||||||
|
resourceSelfLinks: ['https://localhost:8080/api/core/processes/45']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
user = Object.assign(new EPerson(), {
|
||||||
|
id: 'userId',
|
||||||
|
email: 'user@test.com'
|
||||||
|
});
|
||||||
|
authService = jasmine.createSpyObj('authService', {
|
||||||
|
getAuthenticatedUserFromStore: observableOf(user)
|
||||||
|
});
|
||||||
|
router = jasmine.createSpyObj('router', {
|
||||||
|
navigateByUrl: jasmine.createSpy('navigateByUrl')
|
||||||
|
});
|
||||||
|
locationStub = jasmine.createSpyObj('location', {
|
||||||
|
back: jasmine.createSpy('back')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
init();
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
FormsModule,
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
RouterTestingModule.withRoutes([])
|
||||||
|
],
|
||||||
|
declarations: [MetadataImportPageComponent, FileValueAccessorDirective, FileValidator],
|
||||||
|
providers: [
|
||||||
|
{ provide: NotificationsService, useValue: notificationService },
|
||||||
|
{ provide: ScriptDataService, useValue: scriptService },
|
||||||
|
{ provide: Router, useValue: router },
|
||||||
|
{ provide: AuthService, useValue: authService },
|
||||||
|
{ provide: Location, useValue: locationStub },
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(MetadataImportPageComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(comp).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('if back button is pressed', () => {
|
||||||
|
beforeEach(fakeAsync(() => {
|
||||||
|
const proceed = fixture.debugElement.query(By.css('#backButton')).nativeElement;
|
||||||
|
proceed.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
it('should do location.back', () => {
|
||||||
|
expect(locationStub.back).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('if file is set', () => {
|
||||||
|
let fileMock: File;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fileMock = new File([''], 'filename.txt', { type: 'text/plain' });
|
||||||
|
comp.setFile(fileMock);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('if proceed button is pressed', () => {
|
||||||
|
beforeEach(fakeAsync(() => {
|
||||||
|
const proceed = fixture.debugElement.query(By.css('#proceedButton')).nativeElement;
|
||||||
|
proceed.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
it('metadata-import script is invoked with its -e currentUserEmail, -f fileName and the mockFile', () => {
|
||||||
|
const parameterValues: ProcessParameter[] = [
|
||||||
|
Object.assign(new ProcessParameter(), { name: '-e', value: user.email }),
|
||||||
|
Object.assign(new ProcessParameter(), { name: '-f', value: 'filename.txt' }),
|
||||||
|
];
|
||||||
|
expect(scriptService.invoke).toHaveBeenCalledWith(METADATA_IMPORT_SCRIPT_NAME, parameterValues, [fileMock]);
|
||||||
|
});
|
||||||
|
it('success notification is shown', () => {
|
||||||
|
expect(notificationService.success).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
it('redirected to process page', () => {
|
||||||
|
expect(router.navigateByUrl).toHaveBeenCalledWith('/processes/45');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('if proceed is pressed; but script invoke fails', () => {
|
||||||
|
beforeEach(fakeAsync(() => {
|
||||||
|
jasmine.getEnv().allowRespy(true);
|
||||||
|
spyOn(scriptService, 'invoke').and.returnValue(observableOf({
|
||||||
|
response:
|
||||||
|
{
|
||||||
|
isSuccessful: false,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
const proceed = fixture.debugElement.query(By.css('#proceedButton')).nativeElement;
|
||||||
|
proceed.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
it('error notification is shown', () => {
|
||||||
|
expect(notificationService.error).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,106 @@
|
|||||||
|
import { Location } from '@angular/common';
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { map, switchMap, take } from 'rxjs/operators';
|
||||||
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
|
import { METADATA_IMPORT_SCRIPT_NAME, ScriptDataService } from '../../core/data/processes/script-data.service';
|
||||||
|
import { RequestEntry } from '../../core/data/request.reducer';
|
||||||
|
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||||
|
import { ProcessParameter } from '../../process-page/processes/process-parameter.model';
|
||||||
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-metadata-import-page',
|
||||||
|
templateUrl: './metadata-import-page.component.html'
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that represents a metadata import page for administrators
|
||||||
|
*/
|
||||||
|
export class MetadataImportPageComponent implements OnInit {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current value of the file
|
||||||
|
*/
|
||||||
|
fileObject: File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The authenticated user's email
|
||||||
|
*/
|
||||||
|
private currentUserEmail$: Observable<string>;
|
||||||
|
|
||||||
|
public constructor(protected authService: AuthService,
|
||||||
|
private location: Location,
|
||||||
|
protected translate: TranslateService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
private scriptDataService: ScriptDataService,
|
||||||
|
private router: Router) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set file
|
||||||
|
* @param file
|
||||||
|
*/
|
||||||
|
setFile(file) {
|
||||||
|
this.fileObject = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method provided by Angular. Invoked after the constructor.
|
||||||
|
*/
|
||||||
|
ngOnInit() {
|
||||||
|
this.currentUserEmail$ = this.authService.getAuthenticatedUserFromStore().pipe(
|
||||||
|
map((user: EPerson) => user.email)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When return button is pressed go to previous location
|
||||||
|
*/
|
||||||
|
public onReturn() {
|
||||||
|
this.location.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts import-metadata script with -e currentUserEmail -f fileName (and the selected file)
|
||||||
|
*/
|
||||||
|
public importMetadata() {
|
||||||
|
if (this.fileObject == null) {
|
||||||
|
this.notificationsService.error(this.translate.get('admin.metadata-import.page.error.addFile'));
|
||||||
|
} else {
|
||||||
|
this.currentUserEmail$.pipe(
|
||||||
|
switchMap((email: string) => {
|
||||||
|
if (isNotEmpty(email)) {
|
||||||
|
const parameterValues: ProcessParameter[] = [
|
||||||
|
Object.assign(new ProcessParameter(), { name: '-e', value: email }),
|
||||||
|
Object.assign(new ProcessParameter(), { name: '-f', value: this.fileObject.name }),
|
||||||
|
];
|
||||||
|
return this.scriptDataService.invoke(METADATA_IMPORT_SCRIPT_NAME, parameterValues, [this.fileObject])
|
||||||
|
.pipe(
|
||||||
|
take(1),
|
||||||
|
map((requestEntry: RequestEntry) => {
|
||||||
|
if (requestEntry.response.isSuccessful) {
|
||||||
|
const title = this.translate.get('process.new.notification.success.title');
|
||||||
|
const content = this.translate.get('process.new.notification.success.content');
|
||||||
|
this.notificationsService.success(title, content);
|
||||||
|
const response: any = requestEntry.response;
|
||||||
|
if (isNotEmpty(response.resourceSelfLinks)) {
|
||||||
|
const processNumber = response.resourceSelfLinks[0].split('/').pop();
|
||||||
|
this.router.navigateByUrl('/processes/' + processNumber);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const title = this.translate.get('process.new.notification.error.title');
|
||||||
|
const content = this.translate.get('process.new.notification.error.content');
|
||||||
|
this.notificationsService.error(title, content);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
take(1)
|
||||||
|
).subscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||||
|
import { getRegistriesModuleRoute } from '../admin-routing-paths';
|
||||||
|
|
||||||
|
export const BITSTREAMFORMATS_MODULE_PATH = 'bitstream-formats';
|
||||||
|
|
||||||
|
export function getBitstreamFormatsModuleRoute() {
|
||||||
|
return new URLCombiner(getRegistriesModuleRoute(), BITSTREAMFORMATS_MODULE_PATH).toString();
|
||||||
|
}
|
@@ -2,15 +2,8 @@ import { MetadataRegistryComponent } from './metadata-registry/metadata-registry
|
|||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { MetadataSchemaComponent } from './metadata-schema/metadata-schema.component';
|
import { MetadataSchemaComponent } from './metadata-schema/metadata-schema.component';
|
||||||
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
|
||||||
import { getRegistriesModulePath } from '../admin-routing.module';
|
|
||||||
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
|
import { BITSTREAMFORMATS_MODULE_PATH } from './admin-registries-routing-paths';
|
||||||
const BITSTREAMFORMATS_MODULE_PATH = 'bitstream-formats';
|
|
||||||
|
|
||||||
export function getBitstreamFormatsModulePath() {
|
|
||||||
return new URLCombiner(getRegistriesModulePath(), BITSTREAMFORMATS_MODULE_PATH).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@@ -5,8 +5,8 @@ import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model'
|
|||||||
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
||||||
import { RestResponse } from '../../../../core/cache/response.models';
|
import { RestResponse } from '../../../../core/cache/response.models';
|
||||||
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
import { getBitstreamFormatsModulePath } from '../../admin-registries-routing.module';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { getBitstreamFormatsModuleRoute } from '../../admin-registries-routing-paths';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the page to create a new bitstream format.
|
* This component renders the page to create a new bitstream format.
|
||||||
@@ -37,7 +37,7 @@ export class AddBitstreamFormatComponent {
|
|||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
this.notificationService.success(this.translateService.get('admin.registries.bitstream-formats.create.success.head'),
|
this.notificationService.success(this.translateService.get('admin.registries.bitstream-formats.create.success.head'),
|
||||||
this.translateService.get('admin.registries.bitstream-formats.create.success.content'));
|
this.translateService.get('admin.registries.bitstream-formats.create.success.content'));
|
||||||
this.router.navigate([getBitstreamFormatsModulePath()]);
|
this.router.navigate([getBitstreamFormatsModuleRoute()]);
|
||||||
this.bitstreamFormatDataService.clearBitStreamFormatRequests().subscribe();
|
this.bitstreamFormatDataService.clearBitStreamFormatRequests().subscribe();
|
||||||
} else {
|
} else {
|
||||||
this.notificationService.error(this.translateService.get('admin.registries.bitstream-formats.create.failure.head'),
|
this.notificationService.error(this.translateService.get('admin.registries.bitstream-formats.create.failure.head'),
|
||||||
|
@@ -7,8 +7,8 @@ import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model'
|
|||||||
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
||||||
import { RestResponse } from '../../../../core/cache/response.models';
|
import { RestResponse } from '../../../../core/cache/response.models';
|
||||||
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
import { getBitstreamFormatsModulePath } from '../../admin-registries-routing.module';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { getBitstreamFormatsModuleRoute } from '../../admin-registries-routing-paths';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the edit page of a bitstream format.
|
* This component renders the edit page of a bitstream format.
|
||||||
@@ -51,7 +51,7 @@ export class EditBitstreamFormatComponent implements OnInit {
|
|||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
this.notificationService.success(this.translateService.get('admin.registries.bitstream-formats.edit.success.head'),
|
this.notificationService.success(this.translateService.get('admin.registries.bitstream-formats.edit.success.head'),
|
||||||
this.translateService.get('admin.registries.bitstream-formats.edit.success.content'));
|
this.translateService.get('admin.registries.bitstream-formats.edit.success.content'));
|
||||||
this.router.navigate([getBitstreamFormatsModulePath()]);
|
this.router.navigate([getBitstreamFormatsModuleRoute()]);
|
||||||
} else {
|
} else {
|
||||||
this.notificationService.error('admin.registries.bitstream-formats.edit.failure.head',
|
this.notificationService.error('admin.registries.bitstream-formats.edit.failure.head',
|
||||||
'admin.registries.bitstream-formats.create.edit.content');
|
'admin.registries.bitstream-formats.create.edit.content');
|
||||||
|
@@ -12,9 +12,9 @@ import {
|
|||||||
DynamicTextAreaModel
|
DynamicTextAreaModel
|
||||||
} from '@ng-dynamic-forms/core';
|
} from '@ng-dynamic-forms/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { getBitstreamFormatsModulePath } from '../../admin-registries-routing.module';
|
|
||||||
import { hasValue, isEmpty } from '../../../../shared/empty.util';
|
import { hasValue, isEmpty } from '../../../../shared/empty.util';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { getBitstreamFormatsModuleRoute } from '../../admin-registries-routing-paths';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The component responsible for rendering the form to create/edit a bitstream format
|
* The component responsible for rendering the form to create/edit a bitstream format
|
||||||
@@ -189,6 +189,6 @@ export class FormatFormComponent implements OnInit {
|
|||||||
* Cancels the edit/create action of the bitstream format and navigates back to the bitstream format registry
|
* Cancels the edit/create action of the bitstream format and navigates back to the bitstream format registry
|
||||||
*/
|
*/
|
||||||
onCancel() {
|
onCancel() {
|
||||||
this.router.navigate([getBitstreamFormatsModulePath()]);
|
this.router.navigate([getBitstreamFormatsModuleRoute()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
src/app/+admin/admin-routing-paths.ts
Normal file
13
src/app/+admin/admin-routing-paths.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
||||||
|
import { getAdminModuleRoute } from '../app-routing-paths';
|
||||||
|
|
||||||
|
export const REGISTRIES_MODULE_PATH = 'registries';
|
||||||
|
export const ACCESS_CONTROL_MODULE_PATH = 'access-control';
|
||||||
|
|
||||||
|
export function getRegistriesModuleRoute() {
|
||||||
|
return new URLCombiner(getAdminModuleRoute(), REGISTRIES_MODULE_PATH).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAccessControlModuleRoute() {
|
||||||
|
return new URLCombiner(getAdminModuleRoute(), ACCESS_CONTROL_MODULE_PATH).toString();
|
||||||
|
}
|
@@ -1,23 +1,12 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { getAdminModulePath } from '../app-routing.module';
|
import { MetadataImportPageComponent } from './admin-import-metadata-page/metadata-import-page.component';
|
||||||
import { AdminSearchPageComponent } from './admin-search-page/admin-search-page.component';
|
import { AdminSearchPageComponent } from './admin-search-page/admin-search-page.component';
|
||||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
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 { URLCombiner } from '../core/url-combiner/url-combiner';
|
|
||||||
import { AdminCurationTasksComponent } from './admin-curation-tasks/admin-curation-tasks.component';
|
import { AdminCurationTasksComponent } from './admin-curation-tasks/admin-curation-tasks.component';
|
||||||
|
import { ACCESS_CONTROL_MODULE_PATH, REGISTRIES_MODULE_PATH } from './admin-routing-paths';
|
||||||
const REGISTRIES_MODULE_PATH = 'registries';
|
|
||||||
export const ACCESS_CONTROL_MODULE_PATH = 'access-control';
|
|
||||||
|
|
||||||
export function getRegistriesModulePath() {
|
|
||||||
return new URLCombiner(getAdminModulePath(), REGISTRIES_MODULE_PATH).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAccessControlModulePath() {
|
|
||||||
return new URLCombiner(getAdminModulePath(), ACCESS_CONTROL_MODULE_PATH).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -48,6 +37,12 @@ export function getAccessControlModulePath() {
|
|||||||
component: AdminCurationTasksComponent,
|
component: AdminCurationTasksComponent,
|
||||||
data: { title: 'admin.curation-tasks.title', breadcrumbKey: 'admin.curation-tasks' }
|
data: { title: 'admin.curation-tasks.title', breadcrumbKey: 'admin.curation-tasks' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'metadata-import',
|
||||||
|
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
||||||
|
component: MetadataImportPageComponent,
|
||||||
|
data: { title: 'admin.metadata-import.title', breadcrumbKey: 'admin.metadata-import' }
|
||||||
|
},
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
@@ -12,7 +12,7 @@ import { CollectionSearchResult } from '../../../../../shared/object-collection/
|
|||||||
import { Collection } from '../../../../../core/shared/collection.model';
|
import { Collection } from '../../../../../core/shared/collection.model';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { getCollectionEditPath } from '../../../../../+collection-page/collection-page-routing.module';
|
import { getCollectionEditRoute } from '../../../../../+collection-page/collection-page-routing-paths';
|
||||||
|
|
||||||
describe('CollectionAdminSearchResultGridElementComponent', () => {
|
describe('CollectionAdminSearchResultGridElementComponent', () => {
|
||||||
let component: CollectionAdminSearchResultGridElementComponent;
|
let component: CollectionAdminSearchResultGridElementComponent;
|
||||||
@@ -61,6 +61,6 @@ describe('CollectionAdminSearchResultGridElementComponent', () => {
|
|||||||
it('should render an edit button with the correct link', () => {
|
it('should render an edit button with the correct link', () => {
|
||||||
const a = fixture.debugElement.query(By.css('a.edit-link'));
|
const a = fixture.debugElement.query(By.css('a.edit-link'));
|
||||||
const link = a.nativeElement.href;
|
const link = a.nativeElement.href;
|
||||||
expect(link).toContain(getCollectionEditPath(id));
|
expect(link).toContain(getCollectionEditRoute(id));
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@@ -4,8 +4,8 @@ import { listableObjectComponent } from '../../../../../shared/object-collection
|
|||||||
import { Context } from '../../../../../core/shared/context.model';
|
import { Context } from '../../../../../core/shared/context.model';
|
||||||
import { CollectionSearchResult } from '../../../../../shared/object-collection/shared/collection-search-result.model';
|
import { CollectionSearchResult } from '../../../../../shared/object-collection/shared/collection-search-result.model';
|
||||||
import { Collection } from '../../../../../core/shared/collection.model';
|
import { Collection } from '../../../../../core/shared/collection.model';
|
||||||
import { getCollectionEditPath } from '../../../../../+collection-page/collection-page-routing.module';
|
|
||||||
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
|
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
|
||||||
|
import { getCollectionEditRoute } from '../../../../../+collection-page/collection-page-routing-paths';
|
||||||
|
|
||||||
@listableObjectComponent(CollectionSearchResult, ViewMode.GridElement, Context.AdminSearch)
|
@listableObjectComponent(CollectionSearchResult, ViewMode.GridElement, Context.AdminSearch)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -21,6 +21,6 @@ export class CollectionAdminSearchResultGridElementComponent extends SearchResul
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.editPath = getCollectionEditPath(this.dso.uuid);
|
this.editPath = getCollectionEditRoute(this.dso.uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,9 +13,9 @@ import { By } from '@angular/platform-browser';
|
|||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { CommunityAdminSearchResultGridElementComponent } from './community-admin-search-result-grid-element.component';
|
import { CommunityAdminSearchResultGridElementComponent } from './community-admin-search-result-grid-element.component';
|
||||||
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
|
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
|
||||||
import { getCommunityEditPath } from '../../../../../+community-page/community-page-routing.module';
|
|
||||||
import { Community } from '../../../../../core/shared/community.model';
|
import { Community } from '../../../../../core/shared/community.model';
|
||||||
import { CommunityAdminSearchResultListElementComponent } from '../../admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component';
|
import { CommunityAdminSearchResultListElementComponent } from '../../admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component';
|
||||||
|
import { getCommunityEditRoute } from '../../../../../+community-page/community-page-routing-paths';
|
||||||
|
|
||||||
describe('CommunityAdminSearchResultGridElementComponent', () => {
|
describe('CommunityAdminSearchResultGridElementComponent', () => {
|
||||||
let component: CommunityAdminSearchResultGridElementComponent;
|
let component: CommunityAdminSearchResultGridElementComponent;
|
||||||
@@ -65,6 +65,6 @@ describe('CommunityAdminSearchResultGridElementComponent', () => {
|
|||||||
it('should render an edit button with the correct link', () => {
|
it('should render an edit button with the correct link', () => {
|
||||||
const a = fixture.debugElement.query(By.css('a.edit-link'));
|
const a = fixture.debugElement.query(By.css('a.edit-link'));
|
||||||
const link = a.nativeElement.href;
|
const link = a.nativeElement.href;
|
||||||
expect(link).toContain(getCommunityEditPath(id));
|
expect(link).toContain(getCommunityEditRoute(id));
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@@ -4,8 +4,8 @@ import { listableObjectComponent } from '../../../../../shared/object-collection
|
|||||||
import { Context } from '../../../../../core/shared/context.model';
|
import { Context } from '../../../../../core/shared/context.model';
|
||||||
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
|
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
|
||||||
import { Community } from '../../../../../core/shared/community.model';
|
import { Community } from '../../../../../core/shared/community.model';
|
||||||
import { getCommunityEditPath } from '../../../../../+community-page/community-page-routing.module';
|
|
||||||
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
|
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
|
||||||
|
import { getCommunityEditRoute } from '../../../../../+community-page/community-page-routing-paths';
|
||||||
|
|
||||||
@listableObjectComponent(CommunitySearchResult, ViewMode.GridElement, Context.AdminSearch)
|
@listableObjectComponent(CommunitySearchResult, ViewMode.GridElement, Context.AdminSearch)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -21,6 +21,6 @@ export class CommunityAdminSearchResultGridElementComponent extends SearchResult
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.editPath = getCommunityEditPath(this.dso.uuid);
|
this.editPath = getCommunityEditRoute(this.dso.uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,16 +4,6 @@ import { ViewMode } from '../../../../../core/shared/view-mode.model';
|
|||||||
import { getListableObjectComponent, listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
import { getListableObjectComponent, listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||||
import { Context } from '../../../../../core/shared/context.model';
|
import { Context } from '../../../../../core/shared/context.model';
|
||||||
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
|
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
|
||||||
import { getItemEditPath } from '../../../../../+item-page/item-page-routing.module';
|
|
||||||
import { URLCombiner } from '../../../../../core/url-combiner/url-combiner';
|
|
||||||
import {
|
|
||||||
ITEM_EDIT_DELETE_PATH,
|
|
||||||
ITEM_EDIT_MOVE_PATH,
|
|
||||||
ITEM_EDIT_PRIVATE_PATH,
|
|
||||||
ITEM_EDIT_PUBLIC_PATH,
|
|
||||||
ITEM_EDIT_REINSTATE_PATH,
|
|
||||||
ITEM_EDIT_WITHDRAW_PATH
|
|
||||||
} from '../../../../../+item-page/edit-item-page/edit-item-page.routing.module';
|
|
||||||
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
|
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
|
||||||
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
|
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
|
||||||
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
|
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
|
||||||
|
@@ -10,7 +10,7 @@ import { CollectionSearchResult } from '../../../../../shared/object-collection/
|
|||||||
import { Collection } from '../../../../../core/shared/collection.model';
|
import { Collection } from '../../../../../core/shared/collection.model';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { getCollectionEditPath } from '../../../../../+collection-page/collection-page-routing.module';
|
import { getCollectionEditRoute } from '../../../../../+collection-page/collection-page-routing-paths';
|
||||||
|
|
||||||
describe('CollectionAdminSearchResultListElementComponent', () => {
|
describe('CollectionAdminSearchResultListElementComponent', () => {
|
||||||
let component: CollectionAdminSearchResultListElementComponent;
|
let component: CollectionAdminSearchResultListElementComponent;
|
||||||
@@ -55,6 +55,6 @@ describe('CollectionAdminSearchResultListElementComponent', () => {
|
|||||||
it('should render an edit button with the correct link', () => {
|
it('should render an edit button with the correct link', () => {
|
||||||
const a = fixture.debugElement.query(By.css('a'));
|
const a = fixture.debugElement.query(By.css('a'));
|
||||||
const link = a.nativeElement.href;
|
const link = a.nativeElement.href;
|
||||||
expect(link).toContain(getCollectionEditPath(id));
|
expect(link).toContain(getCollectionEditRoute(id));
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@@ -5,7 +5,7 @@ import { Context } from '../../../../../core/shared/context.model';
|
|||||||
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
||||||
import { CollectionSearchResult } from '../../../../../shared/object-collection/shared/collection-search-result.model';
|
import { CollectionSearchResult } from '../../../../../shared/object-collection/shared/collection-search-result.model';
|
||||||
import { Collection } from '../../../../../core/shared/collection.model';
|
import { Collection } from '../../../../../core/shared/collection.model';
|
||||||
import { getCollectionEditPath } from '../../../../../+collection-page/collection-page-routing.module';
|
import { getCollectionEditRoute } from '../../../../../+collection-page/collection-page-routing-paths';
|
||||||
|
|
||||||
@listableObjectComponent(CollectionSearchResult, ViewMode.ListElement, Context.AdminSearch)
|
@listableObjectComponent(CollectionSearchResult, ViewMode.ListElement, Context.AdminSearch)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -21,6 +21,6 @@ export class CollectionAdminSearchResultListElementComponent extends SearchResul
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.editPath = getCollectionEditPath(this.dso.uuid);
|
this.editPath = getCollectionEditRoute(this.dso.uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,8 +9,8 @@ import { By } from '@angular/platform-browser';
|
|||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { CommunityAdminSearchResultListElementComponent } from './community-admin-search-result-list-element.component';
|
import { CommunityAdminSearchResultListElementComponent } from './community-admin-search-result-list-element.component';
|
||||||
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
|
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
|
||||||
import { getCommunityEditPath } from '../../../../../+community-page/community-page-routing.module';
|
|
||||||
import { Community } from '../../../../../core/shared/community.model';
|
import { Community } from '../../../../../core/shared/community.model';
|
||||||
|
import { getCommunityEditRoute } from '../../../../../+community-page/community-page-routing-paths';
|
||||||
|
|
||||||
describe('CommunityAdminSearchResultListElementComponent', () => {
|
describe('CommunityAdminSearchResultListElementComponent', () => {
|
||||||
let component: CommunityAdminSearchResultListElementComponent;
|
let component: CommunityAdminSearchResultListElementComponent;
|
||||||
@@ -55,6 +55,6 @@ describe('CommunityAdminSearchResultListElementComponent', () => {
|
|||||||
it('should render an edit button with the correct link', () => {
|
it('should render an edit button with the correct link', () => {
|
||||||
const a = fixture.debugElement.query(By.css('a'));
|
const a = fixture.debugElement.query(By.css('a'));
|
||||||
const link = a.nativeElement.href;
|
const link = a.nativeElement.href;
|
||||||
expect(link).toContain(getCommunityEditPath(id));
|
expect(link).toContain(getCommunityEditRoute(id));
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@@ -5,7 +5,7 @@ import { Context } from '../../../../../core/shared/context.model';
|
|||||||
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
||||||
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
|
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
|
||||||
import { Community } from '../../../../../core/shared/community.model';
|
import { Community } from '../../../../../core/shared/community.model';
|
||||||
import { getCommunityEditPath } from '../../../../../+community-page/community-page-routing.module';
|
import { getCommunityEditRoute } from '../../../../../+community-page/community-page-routing-paths';
|
||||||
|
|
||||||
@listableObjectComponent(CommunitySearchResult, ViewMode.ListElement, Context.AdminSearch)
|
@listableObjectComponent(CommunitySearchResult, ViewMode.ListElement, Context.AdminSearch)
|
||||||
@Component({
|
@Component({
|
||||||
@@ -21,6 +21,6 @@ export class CommunityAdminSearchResultListElementComponent extends SearchResult
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.editPath = getCommunityEditPath(this.dso.uuid);
|
this.editPath = getCommunityEditRoute(this.dso.uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,27 +1,27 @@
|
|||||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 edit-link" [routerLink]="[getEditPath()]" [title]="'admin.search.item.edit' | translate">
|
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 edit-link" [routerLink]="[getEditRoute()]" [title]="'admin.search.item.edit' | translate">
|
||||||
<i class="fa fa-edit"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.edit" | translate}}</span>
|
<i class="fa fa-edit"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.edit" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isWithdrawn" class="btn btn-light my-1 withdraw-link" [routerLink]="[getWithdrawPath()]" [title]="'admin.search.item.withdraw' | translate">
|
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isWithdrawn" class="btn btn-light my-1 withdraw-link" [routerLink]="[getWithdrawRoute()]" [title]="'admin.search.item.withdraw' | translate">
|
||||||
<i class="fa fa-ban"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.withdraw" | translate}}</span>
|
<i class="fa fa-ban"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.withdraw" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isWithdrawn" class="btn btn-light my-1 reinstate-link" [routerLink]="[getReinstatePath()]" [title]="'admin.search.item.reinstate' | translate">
|
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isWithdrawn" class="btn btn-light my-1 reinstate-link" [routerLink]="[getReinstateRoute()]" [title]="'admin.search.item.reinstate' | translate">
|
||||||
<i class="fa fa-undo"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.reinstate" | translate}}</span>
|
<i class="fa fa-undo"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.reinstate" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isDiscoverable" class="btn btn-light my-1 private-link" [routerLink]="[getPrivatePath()]" [title]="'admin.search.item.make-private' | translate">
|
<a [ngClass]="{'btn-sm': small}" *ngIf="item && item.isDiscoverable" class="btn btn-light my-1 private-link" [routerLink]="[getPrivateRoute()]" [title]="'admin.search.item.make-private' | translate">
|
||||||
<i class="fa fa-eye-slash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-private" | translate}}</span>
|
<i class="fa fa-eye-slash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-private" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isDiscoverable" class="btn btn-light my-1 public-link" [routerLink]="[getPublicPath()]" [title]="'admin.search.item.make-public' | translate">
|
<a [ngClass]="{'btn-sm': small}" *ngIf="item && !item.isDiscoverable" class="btn btn-light my-1 public-link" [routerLink]="[getPublicRoute()]" [title]="'admin.search.item.make-public' | translate">
|
||||||
<i class="fa fa-eye"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-public" | translate}}</span>
|
<i class="fa fa-eye"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.make-public" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeletePath()]" [title]="'admin.search.item.delete' | translate">
|
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.search.item.delete' | translate">
|
||||||
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.delete" | translate}}</span>
|
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.delete" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 move-link" [routerLink]="[getMovePath()]" [title]="'admin.search.item.move' | translate">
|
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 move-link" [routerLink]="[getMoveRoute()]" [title]="'admin.search.item.move' | translate">
|
||||||
<i class="fa fa-arrow-circle-right"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.move" | translate}}</span>
|
<i class="fa fa-arrow-circle-right"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.search.item.move" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
@@ -6,16 +6,16 @@ import { By } from '@angular/platform-browser';
|
|||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { ItemAdminSearchResultActionsComponent } from './item-admin-search-result-actions.component';
|
import { ItemAdminSearchResultActionsComponent } from './item-admin-search-result-actions.component';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
|
||||||
|
import { getItemEditRoute } from '../../../+item-page/item-page-routing-paths';
|
||||||
import {
|
import {
|
||||||
ITEM_EDIT_DELETE_PATH,
|
|
||||||
ITEM_EDIT_MOVE_PATH,
|
ITEM_EDIT_MOVE_PATH,
|
||||||
ITEM_EDIT_PRIVATE_PATH,
|
ITEM_EDIT_DELETE_PATH,
|
||||||
ITEM_EDIT_PUBLIC_PATH,
|
ITEM_EDIT_PUBLIC_PATH,
|
||||||
|
ITEM_EDIT_PRIVATE_PATH,
|
||||||
ITEM_EDIT_REINSTATE_PATH,
|
ITEM_EDIT_REINSTATE_PATH,
|
||||||
ITEM_EDIT_WITHDRAW_PATH
|
ITEM_EDIT_WITHDRAW_PATH
|
||||||
} from '../../../+item-page/edit-item-page/edit-item-page.routing.module';
|
} from '../../../+item-page/edit-item-page/edit-item-page.routing-paths';
|
||||||
import { getItemEditPath } from '../../../+item-page/item-page-routing.module';
|
|
||||||
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
|
|
||||||
|
|
||||||
describe('ItemAdminSearchResultActionsComponent', () => {
|
describe('ItemAdminSearchResultActionsComponent', () => {
|
||||||
let component: ItemAdminSearchResultActionsComponent;
|
let component: ItemAdminSearchResultActionsComponent;
|
||||||
@@ -55,19 +55,19 @@ describe('ItemAdminSearchResultActionsComponent', () => {
|
|||||||
it('should render an edit button with the correct link', () => {
|
it('should render an edit button with the correct link', () => {
|
||||||
const button = fixture.debugElement.query(By.css('a.edit-link'));
|
const button = fixture.debugElement.query(By.css('a.edit-link'));
|
||||||
const link = button.nativeElement.href;
|
const link = button.nativeElement.href;
|
||||||
expect(link).toContain(getItemEditPath(id));
|
expect(link).toContain(getItemEditRoute(id));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render a delete button with the correct link', () => {
|
it('should render a delete button with the correct link', () => {
|
||||||
const button = fixture.debugElement.query(By.css('a.delete-link'));
|
const button = fixture.debugElement.query(By.css('a.delete-link'));
|
||||||
const link = button.nativeElement.href;
|
const link = button.nativeElement.href;
|
||||||
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_DELETE_PATH).toString());
|
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_DELETE_PATH).toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render a move button with the correct link', () => {
|
it('should render a move button with the correct link', () => {
|
||||||
const a = fixture.debugElement.query(By.css('a.move-link'));
|
const a = fixture.debugElement.query(By.css('a.move-link'));
|
||||||
const link = a.nativeElement.href;
|
const link = a.nativeElement.href;
|
||||||
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_MOVE_PATH).toString());
|
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_MOVE_PATH).toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when the item is not withdrawn', () => {
|
describe('when the item is not withdrawn', () => {
|
||||||
@@ -79,7 +79,7 @@ describe('ItemAdminSearchResultActionsComponent', () => {
|
|||||||
it('should render a withdraw button with the correct link', () => {
|
it('should render a withdraw button with the correct link', () => {
|
||||||
const a = fixture.debugElement.query(By.css('a.withdraw-link'));
|
const a = fixture.debugElement.query(By.css('a.withdraw-link'));
|
||||||
const link = a.nativeElement.href;
|
const link = a.nativeElement.href;
|
||||||
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_WITHDRAW_PATH).toString());
|
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_WITHDRAW_PATH).toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not render a reinstate button with the correct link', () => {
|
it('should not render a reinstate button with the correct link', () => {
|
||||||
@@ -102,7 +102,7 @@ describe('ItemAdminSearchResultActionsComponent', () => {
|
|||||||
it('should render a reinstate button with the correct link', () => {
|
it('should render a reinstate button with the correct link', () => {
|
||||||
const a = fixture.debugElement.query(By.css('a.reinstate-link'));
|
const a = fixture.debugElement.query(By.css('a.reinstate-link'));
|
||||||
const link = a.nativeElement.href;
|
const link = a.nativeElement.href;
|
||||||
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_REINSTATE_PATH).toString());
|
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_REINSTATE_PATH).toString());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ describe('ItemAdminSearchResultActionsComponent', () => {
|
|||||||
it('should render a make private button with the correct link', () => {
|
it('should render a make private button with the correct link', () => {
|
||||||
const a = fixture.debugElement.query(By.css('a.private-link'));
|
const a = fixture.debugElement.query(By.css('a.private-link'));
|
||||||
const link = a.nativeElement.href;
|
const link = a.nativeElement.href;
|
||||||
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_PRIVATE_PATH).toString());
|
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_PRIVATE_PATH).toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not render a make public button with the correct link', () => {
|
it('should not render a make public button with the correct link', () => {
|
||||||
@@ -138,7 +138,7 @@ describe('ItemAdminSearchResultActionsComponent', () => {
|
|||||||
it('should render a make private button with the correct link', () => {
|
it('should render a make private button with the correct link', () => {
|
||||||
const a = fixture.debugElement.query(By.css('a.public-link'));
|
const a = fixture.debugElement.query(By.css('a.public-link'));
|
||||||
const link = a.nativeElement.href;
|
const link = a.nativeElement.href;
|
||||||
expect(link).toContain(new URLCombiner(getItemEditPath(id), ITEM_EDIT_PUBLIC_PATH).toString());
|
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_PUBLIC_PATH).toString());
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { getItemEditPath } from '../../../+item-page/item-page-routing.module';
|
|
||||||
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
|
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
|
||||||
|
import { getItemEditRoute } from '../../../+item-page/item-page-routing-paths';
|
||||||
import {
|
import {
|
||||||
ITEM_EDIT_DELETE_PATH,
|
|
||||||
ITEM_EDIT_MOVE_PATH,
|
ITEM_EDIT_MOVE_PATH,
|
||||||
ITEM_EDIT_PRIVATE_PATH,
|
ITEM_EDIT_DELETE_PATH,
|
||||||
ITEM_EDIT_PUBLIC_PATH,
|
ITEM_EDIT_PUBLIC_PATH,
|
||||||
|
ITEM_EDIT_PRIVATE_PATH,
|
||||||
ITEM_EDIT_REINSTATE_PATH,
|
ITEM_EDIT_REINSTATE_PATH,
|
||||||
ITEM_EDIT_WITHDRAW_PATH
|
ITEM_EDIT_WITHDRAW_PATH
|
||||||
} from '../../../+item-page/edit-item-page/edit-item-page.routing.module';
|
} from '../../../+item-page/edit-item-page/edit-item-page.routing-paths';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-admin-search-result-actions-element',
|
selector: 'ds-item-admin-search-result-actions-element',
|
||||||
@@ -33,49 +33,49 @@ export class ItemAdminSearchResultActionsComponent {
|
|||||||
/**
|
/**
|
||||||
* Returns the path to the edit page of this item
|
* Returns the path to the edit page of this item
|
||||||
*/
|
*/
|
||||||
getEditPath(): string {
|
getEditRoute(): string {
|
||||||
return getItemEditPath(this.item.uuid)
|
return getItemEditRoute(this.item.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the path to the move page of this item
|
* Returns the path to the move page of this item
|
||||||
*/
|
*/
|
||||||
getMovePath(): string {
|
getMoveRoute(): string {
|
||||||
return new URLCombiner(this.getEditPath(), ITEM_EDIT_MOVE_PATH).toString();
|
return new URLCombiner(this.getEditRoute(), ITEM_EDIT_MOVE_PATH).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the path to the delete page of this item
|
* Returns the path to the delete page of this item
|
||||||
*/
|
*/
|
||||||
getDeletePath(): string {
|
getDeleteRoute(): string {
|
||||||
return new URLCombiner(this.getEditPath(), ITEM_EDIT_DELETE_PATH).toString();
|
return new URLCombiner(this.getEditRoute(), ITEM_EDIT_DELETE_PATH).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the path to the withdraw page of this item
|
* Returns the path to the withdraw page of this item
|
||||||
*/
|
*/
|
||||||
getWithdrawPath(): string {
|
getWithdrawRoute(): string {
|
||||||
return new URLCombiner(this.getEditPath(), ITEM_EDIT_WITHDRAW_PATH).toString();
|
return new URLCombiner(this.getEditRoute(), ITEM_EDIT_WITHDRAW_PATH).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the path to the reinstate page of this item
|
* Returns the path to the reinstate page of this item
|
||||||
*/
|
*/
|
||||||
getReinstatePath(): string {
|
getReinstateRoute(): string {
|
||||||
return new URLCombiner(this.getEditPath(), ITEM_EDIT_REINSTATE_PATH).toString();
|
return new URLCombiner(this.getEditRoute(), ITEM_EDIT_REINSTATE_PATH).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the path to the page where the user can make this item private
|
* Returns the path to the page where the user can make this item private
|
||||||
*/
|
*/
|
||||||
getPrivatePath(): string {
|
getPrivateRoute(): string {
|
||||||
return new URLCombiner(this.getEditPath(), ITEM_EDIT_PRIVATE_PATH).toString();
|
return new URLCombiner(this.getEditRoute(), ITEM_EDIT_PRIVATE_PATH).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the path to the page where the user can make this item public
|
* Returns the path to the page where the user can make this item public
|
||||||
*/
|
*/
|
||||||
getPublicPath(): string {
|
getPublicRoute(): string {
|
||||||
return new URLCombiner(this.getEditPath(), ITEM_EDIT_PUBLIC_PATH).toString();
|
return new URLCombiner(this.getEditRoute(), ITEM_EDIT_PUBLIC_PATH).toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<div class="sidebar-collapsible">
|
<div class="sidebar-collapsible">
|
||||||
<span class="section-header-text">
|
<span class="section-header-text">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="itemComponents.get(section.id); injector: itemInjectors.get(section.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(section.id).component; injector: (sectionMap$ | async).get(section.id).injector;"></ng-container>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<ng-container *ngFor="let section of (sections | async)">
|
<ng-container *ngFor="let section of (sections | async)">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="sectionComponents.get(section.id); injector: sectionInjectors.get(section.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(section.id).component; injector: (sectionMap$ | async).get(section.id).injector;"></ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,4 +49,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
@@ -2,6 +2,7 @@ import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core
|
|||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { ScriptDataService } from '../../core/data/processes/script-data.service';
|
||||||
import { AdminSidebarComponent } from './admin-sidebar.component';
|
import { AdminSidebarComponent } from './admin-sidebar.component';
|
||||||
import { MenuService } from '../../shared/menu/menu.service';
|
import { MenuService } from '../../shared/menu/menu.service';
|
||||||
import { MenuServiceStub } from '../../shared/testing/menu-service.stub';
|
import { MenuServiceStub } from '../../shared/testing/menu-service.stub';
|
||||||
@@ -21,11 +22,13 @@ describe('AdminSidebarComponent', () => {
|
|||||||
let fixture: ComponentFixture<AdminSidebarComponent>;
|
let fixture: ComponentFixture<AdminSidebarComponent>;
|
||||||
const menuService = new MenuServiceStub();
|
const menuService = new MenuServiceStub();
|
||||||
let authorizationService: AuthorizationDataService;
|
let authorizationService: AuthorizationDataService;
|
||||||
|
let scriptService;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
authorizationService = jasmine.createSpyObj('authorizationService', {
|
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||||
isAuthorized: observableOf(true)
|
isAuthorized: observableOf(true)
|
||||||
});
|
});
|
||||||
|
scriptService = jasmine.createSpyObj('scriptService', { scriptWithNameExistsAndCanExecute: observableOf(true) });
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule],
|
imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule],
|
||||||
declarations: [AdminSidebarComponent],
|
declarations: [AdminSidebarComponent],
|
||||||
@@ -36,9 +39,11 @@ describe('AdminSidebarComponent', () => {
|
|||||||
{ provide: AuthService, useClass: AuthServiceStub },
|
{ provide: AuthService, useClass: AuthServiceStub },
|
||||||
{ provide: ActivatedRoute, useValue: {} },
|
{ provide: ActivatedRoute, useValue: {} },
|
||||||
{ provide: AuthorizationDataService, useValue: authorizationService },
|
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||||
|
{ provide: ScriptDataService, useValue: scriptService },
|
||||||
{
|
{
|
||||||
provide: NgbModal, useValue: {
|
provide: NgbModal, useValue: {
|
||||||
open: () => {/*comment*/}
|
open: () => {/*comment*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@@ -1,9 +1,14 @@
|
|||||||
import { Component, Injector, OnInit } from '@angular/core';
|
import { Component, Injector, OnInit } from '@angular/core';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { combineLatest as combineLatestObservable } from 'rxjs';
|
import { combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { first, map } from 'rxjs/operators';
|
import { first, map, take } from 'rxjs/operators';
|
||||||
import { AuthService } from '../../core/auth/auth.service';
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
|
import {
|
||||||
|
METADATA_EXPORT_SCRIPT_NAME,
|
||||||
|
METADATA_IMPORT_SCRIPT_NAME,
|
||||||
|
ScriptDataService
|
||||||
|
} from '../../core/data/processes/script-data.service';
|
||||||
import { slideHorizontal, slideSidebar } from '../../shared/animations/slide';
|
import { slideHorizontal, slideSidebar } from '../../shared/animations/slide';
|
||||||
import { CreateCollectionParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component';
|
import { CreateCollectionParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component';
|
||||||
import { CreateCommunityParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component';
|
import { CreateCommunityParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component';
|
||||||
@@ -11,6 +16,9 @@ import { CreateItemParentSelectorComponent } from '../../shared/dso-selector/mod
|
|||||||
import { EditCollectionSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component';
|
import { EditCollectionSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component';
|
||||||
import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component';
|
import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component';
|
||||||
import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component';
|
import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component';
|
||||||
|
import {
|
||||||
|
ExportMetadataSelectorComponent
|
||||||
|
} from '../../shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component';
|
||||||
import { MenuID, MenuItemType } from '../../shared/menu/initial-menus-state';
|
import { MenuID, MenuItemType } from '../../shared/menu/initial-menus-state';
|
||||||
import { LinkMenuItemModel } from '../../shared/menu/menu-item/models/link.model';
|
import { LinkMenuItemModel } from '../../shared/menu/menu-item/models/link.model';
|
||||||
import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick.model';
|
import { OnClickMenuItemModel } from '../../shared/menu/menu-item/models/onclick.model';
|
||||||
@@ -64,7 +72,8 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
private variableService: CSSVariableService,
|
private variableService: CSSVariableService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private modalService: NgbModal,
|
private modalService: NgbModal,
|
||||||
private authorizationService: AuthorizationDataService
|
private authorizationService: AuthorizationDataService,
|
||||||
|
private scriptDataService: ScriptDataService,
|
||||||
) {
|
) {
|
||||||
super(menuService, injector);
|
super(menuService, injector);
|
||||||
}
|
}
|
||||||
@@ -75,6 +84,8 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.createMenu();
|
this.createMenu();
|
||||||
this.createSiteAdministratorMenuSections();
|
this.createSiteAdministratorMenuSections();
|
||||||
|
this.createExportMenuSections();
|
||||||
|
this.createImportMenuSections();
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.sidebarWidth = this.variableService.getVariable('sidebarItemsWidth');
|
this.sidebarWidth = this.variableService.getVariable('sidebarItemsWidth');
|
||||||
this.authService.isAuthenticated()
|
this.authService.isAuthenticated()
|
||||||
@@ -88,7 +99,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
this.sidebarOpen = !collapsed;
|
this.sidebarOpen = !collapsed;
|
||||||
this.sidebarClosed = collapsed;
|
this.sidebarClosed = collapsed;
|
||||||
});
|
});
|
||||||
this.sidebarExpanded = combineLatestObservable(this.menuCollapsed, this.menuPreviewCollapsed)
|
this.sidebarExpanded = observableCombineLatest(this.menuCollapsed, this.menuPreviewCollapsed)
|
||||||
.pipe(
|
.pipe(
|
||||||
map(([collapsed, previewCollapsed]) => (!collapsed || !previewCollapsed))
|
map(([collapsed, previewCollapsed]) => (!collapsed || !previewCollapsed))
|
||||||
);
|
);
|
||||||
@@ -225,94 +236,18 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
} as OnClickMenuItemModel,
|
} as OnClickMenuItemModel,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Import */
|
/* Curation tasks */
|
||||||
{
|
{
|
||||||
id: 'import',
|
id: 'curation_tasks',
|
||||||
active: false,
|
|
||||||
visible: true,
|
|
||||||
model: {
|
|
||||||
type: MenuItemType.TEXT,
|
|
||||||
text: 'menu.section.import'
|
|
||||||
} as TextMenuItemModel,
|
|
||||||
icon: 'sign-in-alt',
|
|
||||||
index: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'import_metadata',
|
|
||||||
parentID: 'import',
|
|
||||||
active: false,
|
active: false,
|
||||||
visible: true,
|
visible: true,
|
||||||
model: {
|
model: {
|
||||||
type: MenuItemType.LINK,
|
type: MenuItemType.LINK,
|
||||||
text: 'menu.section.import_metadata',
|
text: 'menu.section.curation_task',
|
||||||
link: ''
|
|
||||||
} as LinkMenuItemModel,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'import_batch',
|
|
||||||
parentID: 'import',
|
|
||||||
active: false,
|
|
||||||
visible: true,
|
|
||||||
model: {
|
|
||||||
type: MenuItemType.LINK,
|
|
||||||
text: 'menu.section.import_batch',
|
|
||||||
link: ''
|
|
||||||
} as LinkMenuItemModel,
|
|
||||||
},
|
|
||||||
/* Export */
|
|
||||||
{
|
|
||||||
id: 'export',
|
|
||||||
active: false,
|
|
||||||
visible: true,
|
|
||||||
model: {
|
|
||||||
type: MenuItemType.TEXT,
|
|
||||||
text: 'menu.section.export'
|
|
||||||
} as TextMenuItemModel,
|
|
||||||
icon: 'sign-out-alt',
|
|
||||||
index: 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'export_community',
|
|
||||||
parentID: 'export',
|
|
||||||
active: false,
|
|
||||||
visible: true,
|
|
||||||
model: {
|
|
||||||
type: MenuItemType.LINK,
|
|
||||||
text: 'menu.section.export_community',
|
|
||||||
link: ''
|
|
||||||
} as LinkMenuItemModel,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'export_collection',
|
|
||||||
parentID: 'export',
|
|
||||||
active: false,
|
|
||||||
visible: true,
|
|
||||||
model: {
|
|
||||||
type: MenuItemType.LINK,
|
|
||||||
text: 'menu.section.export_collection',
|
|
||||||
link: ''
|
|
||||||
} as LinkMenuItemModel,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'export_item',
|
|
||||||
parentID: 'export',
|
|
||||||
active: false,
|
|
||||||
visible: true,
|
|
||||||
model: {
|
|
||||||
type: MenuItemType.LINK,
|
|
||||||
text: 'menu.section.export_item',
|
|
||||||
link: ''
|
|
||||||
} as LinkMenuItemModel,
|
|
||||||
}, {
|
|
||||||
id: 'export_metadata',
|
|
||||||
parentID: 'export',
|
|
||||||
active: false,
|
|
||||||
visible: true,
|
|
||||||
model: {
|
|
||||||
type: MenuItemType.LINK,
|
|
||||||
text: 'menu.section.export_metadata',
|
|
||||||
link: ''
|
link: ''
|
||||||
} as LinkMenuItemModel,
|
} as LinkMenuItemModel,
|
||||||
|
icon: 'filter',
|
||||||
|
index: 7
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Statistics */
|
/* Statistics */
|
||||||
@@ -362,6 +297,146 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create menu sections dependent on whether or not the current user is a site administrator and on whether or not
|
||||||
|
* the export scripts exist and the current user is allowed to execute them
|
||||||
|
*/
|
||||||
|
createExportMenuSections() {
|
||||||
|
const menuList = [
|
||||||
|
/* Export */
|
||||||
|
{
|
||||||
|
id: 'export',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.TEXT,
|
||||||
|
text: 'menu.section.export'
|
||||||
|
} as TextMenuItemModel,
|
||||||
|
icon: 'sign-out-alt',
|
||||||
|
index: 3,
|
||||||
|
shouldPersistOnRouteChange: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'export_community',
|
||||||
|
parentID: 'export',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.LINK,
|
||||||
|
text: 'menu.section.export_community',
|
||||||
|
link: ''
|
||||||
|
} as LinkMenuItemModel,
|
||||||
|
shouldPersistOnRouteChange: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'export_collection',
|
||||||
|
parentID: 'export',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.LINK,
|
||||||
|
text: 'menu.section.export_collection',
|
||||||
|
link: ''
|
||||||
|
} as LinkMenuItemModel,
|
||||||
|
shouldPersistOnRouteChange: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'export_item',
|
||||||
|
parentID: 'export',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.LINK,
|
||||||
|
text: 'menu.section.export_item',
|
||||||
|
link: ''
|
||||||
|
} as LinkMenuItemModel,
|
||||||
|
shouldPersistOnRouteChange: true
|
||||||
|
},
|
||||||
|
];
|
||||||
|
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, menuSection));
|
||||||
|
|
||||||
|
observableCombineLatest(
|
||||||
|
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
||||||
|
// this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME)
|
||||||
|
).pipe(
|
||||||
|
// TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed; otherwise even in production mode, the metadata export button is only available after a refresh (and not in dev mode)
|
||||||
|
// filter(([authorized, metadataExportScriptExists]: boolean[]) => authorized && metadataExportScriptExists),
|
||||||
|
take(1)
|
||||||
|
).subscribe(() => {
|
||||||
|
this.menuService.addSection(this.menuID, {
|
||||||
|
id: 'export_metadata',
|
||||||
|
parentID: 'export',
|
||||||
|
active: true,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.ONCLICK,
|
||||||
|
text: 'menu.section.export_metadata',
|
||||||
|
function: () => {
|
||||||
|
this.modalService.open(ExportMetadataSelectorComponent);
|
||||||
|
}
|
||||||
|
} as OnClickMenuItemModel,
|
||||||
|
shouldPersistOnRouteChange: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create menu sections dependent on whether or not the current user is a site administrator and on whether or not
|
||||||
|
* the import scripts exist and the current user is allowed to execute them
|
||||||
|
*/
|
||||||
|
createImportMenuSections() {
|
||||||
|
const menuList = [
|
||||||
|
/* Import */
|
||||||
|
{
|
||||||
|
id: 'import',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.TEXT,
|
||||||
|
text: 'menu.section.import'
|
||||||
|
} as TextMenuItemModel,
|
||||||
|
icon: 'sign-in-alt',
|
||||||
|
index: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'import_batch',
|
||||||
|
parentID: 'import',
|
||||||
|
active: false,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.LINK,
|
||||||
|
text: 'menu.section.import_batch',
|
||||||
|
link: ''
|
||||||
|
} as LinkMenuItemModel,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, {
|
||||||
|
shouldPersistOnRouteChange: true
|
||||||
|
})));
|
||||||
|
|
||||||
|
observableCombineLatest(
|
||||||
|
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
||||||
|
// this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_IMPORT_SCRIPT_NAME)
|
||||||
|
).pipe(
|
||||||
|
// TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed
|
||||||
|
// filter(([authorized, metadataImportScriptExists]: boolean[]) => authorized && metadataImportScriptExists),
|
||||||
|
take(1)
|
||||||
|
).subscribe(() => {
|
||||||
|
this.menuService.addSection(this.menuID, {
|
||||||
|
id: 'import_metadata',
|
||||||
|
parentID: 'import',
|
||||||
|
active: true,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.LINK,
|
||||||
|
text: 'menu.section.import_metadata',
|
||||||
|
link: '/admin/metadata-import'
|
||||||
|
} as LinkMenuItemModel,
|
||||||
|
shouldPersistOnRouteChange: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create menu sections dependent on whether or not the current user is a site administrator
|
* Create menu sections dependent on whether or not the current user is a site administrator
|
||||||
*/
|
*/
|
||||||
|
@@ -12,16 +12,16 @@
|
|||||||
(click)="toggleSection($event)">
|
(click)="toggleSection($event)">
|
||||||
<span class="section-header-text">
|
<span class="section-header-text">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="itemComponents.get(section.id); injector: itemInjectors.get(section.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(section.id).component; injector: (sectionMap$ | async).get(section.id).injector;"></ng-container>
|
||||||
</span>
|
</span>
|
||||||
<i class="fas fa-chevron-right fa-pull-right"
|
<i class="fas fa-chevron-right fa-pull-right"
|
||||||
[@rotate]="(expanded | async) ? 'expanded' : 'collapsed'" [title]="('menu.section.toggle.' + section.id) | translate"></i>
|
[@rotate]="(expanded | async) ? 'expanded' : 'collapsed'" [title]="('menu.section.toggle.' + section.id) | translate"></i>
|
||||||
</a>
|
</a>
|
||||||
<ul class="sidebar-sub-level-items list-unstyled" @slide *ngIf="(expanded | async)">
|
<ul class="sidebar-sub-level-items list-unstyled" @slide *ngIf="(expanded | async)">
|
||||||
<li *ngFor="let subSection of (subSections | async)">
|
<li *ngFor="let subSection of (subSections$ | async)">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="itemComponents.get(subSection.id); injector: itemInjectors.get(subSection.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(subSection.id).component; injector: (sectionMap$ | async).get(subSection.id).injector;"></ng-container>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeletePath()]" [title]="'admin.workflow.item.delete' | translate">
|
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 delete-link" [routerLink]="[getDeleteRoute()]" [title]="'admin.workflow.item.delete' | translate">
|
||||||
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.delete" | translate}}</span>
|
<i class="fa fa-trash"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.delete" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 send-back-link" [routerLink]="[getSendBackPath()]" [title]="'admin.workflow.item.send-back' | translate">
|
<a [ngClass]="{'btn-sm': small}" class="btn btn-light my-1 send-back-link" [routerLink]="[getSendBackRoute()]" [title]="'admin.workflow.item.send-back' | translate">
|
||||||
<i class="fa fa-hand-point-left"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.send-back" | translate}}</span>
|
<i class="fa fa-hand-point-left"></i><span *ngIf="!small" class="d-none d-sm-inline"> {{"admin.workflow.item.send-back" | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
|
@@ -5,19 +5,22 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import {
|
|
||||||
ITEM_EDIT_DELETE_PATH,
|
|
||||||
ITEM_EDIT_MOVE_PATH,
|
|
||||||
ITEM_EDIT_PRIVATE_PATH,
|
|
||||||
ITEM_EDIT_PUBLIC_PATH,
|
|
||||||
ITEM_EDIT_REINSTATE_PATH,
|
|
||||||
ITEM_EDIT_WITHDRAW_PATH
|
|
||||||
} from '../../../+item-page/edit-item-page/edit-item-page.routing.module';
|
|
||||||
import { getItemEditPath } from '../../../+item-page/item-page-routing.module';
|
|
||||||
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
|
import { URLCombiner } from '../../../core/url-combiner/url-combiner';
|
||||||
import { WorkflowItemAdminWorkflowActionsComponent } from './workflow-item-admin-workflow-actions.component';
|
import { WorkflowItemAdminWorkflowActionsComponent } from './workflow-item-admin-workflow-actions.component';
|
||||||
import { WorkflowItem } from '../../../core/submission/models/workflowitem.model';
|
import { WorkflowItem } from '../../../core/submission/models/workflowitem.model';
|
||||||
import { getWorkflowItemDeletePath, getWorkflowItemSendBackPath } from '../../../+workflowitems-edit-page/workflowitems-edit-page-routing.module';
|
import {
|
||||||
|
getWorkflowItemSendBackRoute,
|
||||||
|
getWorkflowItemDeleteRoute
|
||||||
|
} from '../../../+workflowitems-edit-page/workflowitems-edit-page-routing-paths';
|
||||||
|
import { getItemEditRoute } from '../../../+item-page/item-page-routing-paths';
|
||||||
|
import {
|
||||||
|
ITEM_EDIT_MOVE_PATH,
|
||||||
|
ITEM_EDIT_DELETE_PATH,
|
||||||
|
ITEM_EDIT_PUBLIC_PATH,
|
||||||
|
ITEM_EDIT_PRIVATE_PATH,
|
||||||
|
ITEM_EDIT_REINSTATE_PATH,
|
||||||
|
ITEM_EDIT_WITHDRAW_PATH
|
||||||
|
} from '../../../+item-page/edit-item-page/edit-item-page.routing-paths';
|
||||||
|
|
||||||
describe('WorkflowItemAdminWorkflowActionsComponent', () => {
|
describe('WorkflowItemAdminWorkflowActionsComponent', () => {
|
||||||
let component: WorkflowItemAdminWorkflowActionsComponent;
|
let component: WorkflowItemAdminWorkflowActionsComponent;
|
||||||
@@ -57,12 +60,12 @@ describe('WorkflowItemAdminWorkflowActionsComponent', () => {
|
|||||||
it('should render a delete button with the correct link', () => {
|
it('should render a delete button with the correct link', () => {
|
||||||
const button = fixture.debugElement.query(By.css('a.delete-link'));
|
const button = fixture.debugElement.query(By.css('a.delete-link'));
|
||||||
const link = button.nativeElement.href;
|
const link = button.nativeElement.href;
|
||||||
expect(link).toContain(new URLCombiner(getWorkflowItemDeletePath(wfi.id)).toString());
|
expect(link).toContain(new URLCombiner(getWorkflowItemDeleteRoute(wfi.id)).toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render a move button with the correct link', () => {
|
it('should render a move button with the correct link', () => {
|
||||||
const a = fixture.debugElement.query(By.css('a.send-back-link'));
|
const a = fixture.debugElement.query(By.css('a.send-back-link'));
|
||||||
const link = a.nativeElement.href;
|
const link = a.nativeElement.href;
|
||||||
expect(link).toContain(new URLCombiner(getWorkflowItemSendBackPath(wfi.id)).toString());
|
expect(link).toContain(new URLCombiner(getWorkflowItemSendBackRoute(wfi.id)).toString());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { WorkflowItem } from '../../../core/submission/models/workflowitem.model';
|
import { WorkflowItem } from '../../../core/submission/models/workflowitem.model';
|
||||||
import { getWorkflowItemDeletePath, getWorkflowItemSendBackPath } from '../../../+workflowitems-edit-page/workflowitems-edit-page-routing.module';
|
import {
|
||||||
|
getWorkflowItemSendBackRoute,
|
||||||
|
getWorkflowItemDeleteRoute
|
||||||
|
} from '../../../+workflowitems-edit-page/workflowitems-edit-page-routing-paths';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-workflow-item-admin-workflow-actions-element',
|
selector: 'ds-workflow-item-admin-workflow-actions-element',
|
||||||
@@ -25,15 +28,15 @@ export class WorkflowItemAdminWorkflowActionsComponent {
|
|||||||
/**
|
/**
|
||||||
* Returns the path to the delete page of this workflow item
|
* Returns the path to the delete page of this workflow item
|
||||||
*/
|
*/
|
||||||
getDeletePath(): string {
|
getDeleteRoute(): string {
|
||||||
|
|
||||||
return getWorkflowItemDeletePath(this.wfi.id)
|
return getWorkflowItemDeleteRoute(this.wfi.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the path to the send back page of this workflow item
|
* Returns the path to the send back page of this workflow item
|
||||||
*/
|
*/
|
||||||
getSendBackPath(): string {
|
getSendBackRoute(): string {
|
||||||
return getWorkflowItemSendBackPath(this.wfi.id);
|
return getWorkflowItemSendBackRoute(this.wfi.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import { AdminAccessControlModule } from './admin-access-control/admin-access-control.module';
|
import { AdminAccessControlModule } from './admin-access-control/admin-access-control.module';
|
||||||
|
import { MetadataImportPageComponent } from './admin-import-metadata-page/metadata-import-page.component';
|
||||||
import { AdminRegistriesModule } from './admin-registries/admin-registries.module';
|
import { AdminRegistriesModule } from './admin-registries/admin-registries.module';
|
||||||
import { AdminRoutingModule } from './admin-routing.module';
|
import { AdminRoutingModule } from './admin-routing.module';
|
||||||
import { AdminSearchPageComponent } from './admin-search-page/admin-search-page.component';
|
import { AdminSearchPageComponent } from './admin-search-page/admin-search-page.component';
|
||||||
@@ -40,7 +41,9 @@ import { AdminCurationTasksComponent } from './admin-curation-tasks/admin-curati
|
|||||||
|
|
||||||
WorkflowItemSearchResultAdminWorkflowListElementComponent,
|
WorkflowItemSearchResultAdminWorkflowListElementComponent,
|
||||||
WorkflowItemSearchResultAdminWorkflowGridElementComponent,
|
WorkflowItemSearchResultAdminWorkflowGridElementComponent,
|
||||||
WorkflowItemAdminWorkflowActionsComponent
|
WorkflowItemAdminWorkflowActionsComponent,
|
||||||
|
|
||||||
|
MetadataImportPageComponent
|
||||||
|
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
@@ -54,7 +57,9 @@ import { AdminCurationTasksComponent } from './admin-curation-tasks/admin-curati
|
|||||||
|
|
||||||
WorkflowItemSearchResultAdminWorkflowListElementComponent,
|
WorkflowItemSearchResultAdminWorkflowListElementComponent,
|
||||||
WorkflowItemSearchResultAdminWorkflowGridElementComponent,
|
WorkflowItemSearchResultAdminWorkflowGridElementComponent,
|
||||||
WorkflowItemAdminWorkflowActionsComponent
|
WorkflowItemAdminWorkflowActionsComponent,
|
||||||
|
|
||||||
|
MetadataImportPageComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AdminModule {
|
export class AdminModule {
|
||||||
|
@@ -36,7 +36,7 @@ import { Observable } from 'rxjs/internal/Observable';
|
|||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { PaginatedList } from '../../core/data/paginated-list';
|
import { PaginatedList } from '../../core/data/paginated-list';
|
||||||
import { followLink } from '../../shared/utils/follow-link-config.model';
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
import { getItemEditPath } from '../../+item-page/item-page-routing.module';
|
import { getItemEditRoute } from '../../+item-page/item-page-routing-paths';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-edit-bitstream-page',
|
selector: 'ds-edit-bitstream-page',
|
||||||
@@ -506,7 +506,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
navigateToItemEditBitstreams() {
|
navigateToItemEditBitstreams() {
|
||||||
if (hasValue(this.itemId)) {
|
if (hasValue(this.itemId)) {
|
||||||
this.router.navigate([getItemEditPath(this.itemId), 'bitstreams']);
|
this.router.navigate([getItemEditRoute(this.itemId), 'bitstreams']);
|
||||||
} else {
|
} else {
|
||||||
this.location.back();
|
this.location.back();
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ import { Observable } from 'rxjs';
|
|||||||
import { getRemoteDataPayload, getSucceededRemoteData } from '../core/shared/operators';
|
import { getRemoteDataPayload, getSucceededRemoteData } from '../core/shared/operators';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
import { getDSOPath } from '../app-routing.module';
|
import { getDSORoute } from '../app-routing-paths';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that resolves the BreadcrumbConfig object for a DSpaceObject on a browse by page
|
* The class that resolves the BreadcrumbConfig object for a DSpaceObject on a browse by page
|
||||||
@@ -32,7 +32,7 @@ export class BrowseByDSOBreadcrumbResolver {
|
|||||||
getSucceededRemoteData(),
|
getSucceededRemoteData(),
|
||||||
getRemoteDataPayload(),
|
getRemoteDataPayload(),
|
||||||
map((object: Community | Collection) => {
|
map((object: Community | Collection) => {
|
||||||
return { provider: this.breadcrumbService, key: object, url: getDSOPath(object) };
|
return { provider: this.breadcrumbService, key: object, url: getDSORoute(object) };
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -133,7 +133,7 @@ export class CollectionItemMapperComponent implements OnInit {
|
|||||||
return this.searchService.search(Object.assign(new PaginatedSearchOptions(options), {
|
return this.searchService.search(Object.assign(new PaginatedSearchOptions(options), {
|
||||||
query: this.buildQuery(collectionRD.payload.id, options.query),
|
query: this.buildQuery(collectionRD.payload.id, options.query),
|
||||||
scope: undefined,
|
scope: undefined,
|
||||||
dsoType: DSpaceObjectType.ITEM,
|
dsoTypes: [DSpaceObjectType.ITEM],
|
||||||
sort: this.defaultSortOptions
|
sort: this.defaultSortOptions
|
||||||
}), 10000).pipe(
|
}), 10000).pipe(
|
||||||
toDSpaceObjectListRD(),
|
toDSpaceObjectListRD(),
|
||||||
|
25
src/app/+collection-page/collection-page-routing-paths.ts
Normal file
25
src/app/+collection-page/collection-page-routing-paths.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
||||||
|
|
||||||
|
export const COLLECTION_PARENT_PARAMETER = 'parent';
|
||||||
|
|
||||||
|
export const COLLECTION_MODULE_PATH = 'collections';
|
||||||
|
|
||||||
|
export function getCollectionModuleRoute() {
|
||||||
|
return `/${COLLECTION_MODULE_PATH}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCollectionPageRoute(collectionId: string) {
|
||||||
|
return new URLCombiner(getCollectionModuleRoute(), collectionId).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCollectionEditRoute(id: string) {
|
||||||
|
return new URLCombiner(getCollectionModuleRoute(), id, COLLECTION_EDIT_PATH).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCollectionCreateRoute() {
|
||||||
|
return new URLCombiner(getCollectionModuleRoute(), COLLECTION_CREATE_PATH).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const COLLECTION_CREATE_PATH = 'create';
|
||||||
|
export const COLLECTION_EDIT_PATH = 'edit';
|
||||||
|
export const ITEMTEMPLATE_PATH = 'itemtemplate';
|
@@ -7,8 +7,6 @@ import { CreateCollectionPageComponent } from './create-collection-page/create-c
|
|||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { CreateCollectionPageGuard } from './create-collection-page/create-collection-page.guard';
|
import { CreateCollectionPageGuard } from './create-collection-page/create-collection-page.guard';
|
||||||
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
|
import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
|
||||||
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
|
||||||
import { getCollectionModulePath } from '../app-routing.module';
|
|
||||||
import { EditItemTemplatePageComponent } from './edit-item-template-page/edit-item-template-page.component';
|
import { EditItemTemplatePageComponent } from './edit-item-template-page/edit-item-template-page.component';
|
||||||
import { ItemTemplatePageResolver } from './edit-item-template-page/item-template-page.resolver';
|
import { ItemTemplatePageResolver } from './edit-item-template-page/item-template-page.resolver';
|
||||||
import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component';
|
import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component';
|
||||||
@@ -16,26 +14,13 @@ import { CollectionBreadcrumbResolver } from '../core/breadcrumbs/collection-bre
|
|||||||
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
|
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
|
||||||
import { LinkService } from '../core/cache/builders/link.service';
|
import { LinkService } from '../core/cache/builders/link.service';
|
||||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
|
import {
|
||||||
|
ITEMTEMPLATE_PATH,
|
||||||
|
COLLECTION_EDIT_PATH,
|
||||||
|
COLLECTION_CREATE_PATH
|
||||||
|
} from './collection-page-routing-paths';
|
||||||
import { CollectionPageAdministratorGuard } from './collection-page-administrator.guard';
|
import { CollectionPageAdministratorGuard } from './collection-page-administrator.guard';
|
||||||
|
|
||||||
export const COLLECTION_PARENT_PARAMETER = 'parent';
|
|
||||||
|
|
||||||
export function getCollectionPageRoute(collectionId: string) {
|
|
||||||
return new URLCombiner(getCollectionModulePath(), collectionId).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCollectionEditPath(id: string) {
|
|
||||||
return new URLCombiner(getCollectionModulePath(), id, COLLECTION_EDIT_PATH).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCollectionCreatePath() {
|
|
||||||
return new URLCombiner(getCollectionModulePath(), COLLECTION_CREATE_PATH).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
const COLLECTION_CREATE_PATH = 'create';
|
|
||||||
const COLLECTION_EDIT_PATH = 'edit';
|
|
||||||
const ITEMTEMPLATE_PATH = 'itemtemplate';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
|
@@ -87,7 +87,7 @@ export class CollectionPageComponent implements OnInit {
|
|||||||
scope: id,
|
scope: id,
|
||||||
pagination: dto.paginationConfig,
|
pagination: dto.paginationConfig,
|
||||||
sort: dto.sortConfig,
|
sort: dto.sortConfig,
|
||||||
dsoType: DSpaceObjectType.ITEM
|
dsoTypes: [DSpaceObjectType.ITEM]
|
||||||
})).pipe(toDSpaceObjectListRD()) as Observable<RemoteData<PaginatedList<Item>>>
|
})).pipe(toDSpaceObjectListRD()) as Observable<RemoteData<PaginatedList<Item>>>
|
||||||
}),
|
}),
|
||||||
startWith(undefined) // Make sure switching pages shows loading component
|
startWith(undefined) // Make sure switching pages shows loading component
|
||||||
|
@@ -30,24 +30,27 @@ describe('CollectionRolesComponent', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
Object.assign(new Collection(), {
|
Object.assign(new Collection(), {
|
||||||
_links: {
|
_links: {
|
||||||
'irrelevant': {
|
irrelevant: {
|
||||||
href: 'irrelevant link',
|
href: 'irrelevant link',
|
||||||
},
|
},
|
||||||
'adminGroup': {
|
adminGroup: {
|
||||||
href: 'adminGroup link',
|
href: 'adminGroup link',
|
||||||
},
|
},
|
||||||
'submittersGroup': {
|
submittersGroup: {
|
||||||
href: 'submittersGroup link',
|
href: 'submittersGroup link',
|
||||||
},
|
},
|
||||||
'itemReadGroup': {
|
itemReadGroup: {
|
||||||
href: 'itemReadGroup link',
|
href: 'itemReadGroup link',
|
||||||
},
|
},
|
||||||
'bitstreamReadGroup': {
|
bitstreamReadGroup: {
|
||||||
href: 'bitstreamReadGroup link',
|
href: 'bitstreamReadGroup link',
|
||||||
},
|
},
|
||||||
'workflowGroups/test': {
|
workflowGroups: [
|
||||||
href: 'test workflow group link',
|
{
|
||||||
},
|
name: 'test',
|
||||||
|
href: 'test workflow group link',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
@@ -5,7 +5,7 @@ import { first, map } from 'rxjs/operators';
|
|||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../core/shared/operators';
|
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../core/shared/operators';
|
||||||
import { ComcolRole } from '../../../shared/comcol-forms/edit-comcol-page/comcol-role/comcol-role';
|
import { HALLink } from '../../../core/shared/hal-link.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for managing a collection's roles
|
* Component for managing a collection's roles
|
||||||
@@ -31,19 +31,27 @@ export class CollectionRolesComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* The different roles for the collection, as an observable.
|
* The different roles for the collection, as an observable.
|
||||||
*/
|
*/
|
||||||
getComcolRoles(): Observable<ComcolRole[]> {
|
getComcolRoles(): Observable<HALLink[]> {
|
||||||
return this.collection$.pipe(
|
return this.collection$.pipe(
|
||||||
map((collection) =>
|
map((collection) => [
|
||||||
[
|
{
|
||||||
ComcolRole.COLLECTION_ADMIN,
|
name: 'collection-admin',
|
||||||
ComcolRole.SUBMITTERS,
|
href: collection._links.adminGroup.href,
|
||||||
ComcolRole.ITEM_READ,
|
},
|
||||||
ComcolRole.BITSTREAM_READ,
|
{
|
||||||
...Object.keys(collection._links)
|
name: 'submitters',
|
||||||
.filter((link) => link.startsWith('workflowGroups/'))
|
href: collection._links.submittersGroup.href,
|
||||||
.map((link) => new ComcolRole(link.substr('workflowGroups/'.length), link)),
|
},
|
||||||
]
|
{
|
||||||
),
|
name: 'item_read',
|
||||||
|
href: collection._links.itemReadGroup.href,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'bitstream_read',
|
||||||
|
href: collection._links.bitstreamReadGroup.href,
|
||||||
|
},
|
||||||
|
...collection._links.workflowGroups,
|
||||||
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ import { Component } from '@angular/core';
|
|||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
|
import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
|
||||||
import { Collection } from '../../core/shared/collection.model';
|
import { Collection } from '../../core/shared/collection.model';
|
||||||
import { getCollectionPageRoute } from '../collection-page-routing.module';
|
import { getCollectionPageRoute } from '../collection-page-routing-paths';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents the page where a user can edit an existing Collection
|
* Component that represents the page where a user can edit an existing Collection
|
||||||
|
@@ -9,8 +9,8 @@ import { ActivatedRoute } from '@angular/router';
|
|||||||
import { of as observableOf } from 'rxjs/internal/observable/of';
|
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||||
import { Collection } from '../../core/shared/collection.model';
|
import { Collection } from '../../core/shared/collection.model';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { getCollectionEditPath } from '../collection-page-routing.module';
|
|
||||||
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
|
||||||
|
import { getCollectionEditRoute } from '../collection-page-routing-paths';
|
||||||
|
|
||||||
describe('EditItemTemplatePageComponent', () => {
|
describe('EditItemTemplatePageComponent', () => {
|
||||||
let comp: EditItemTemplatePageComponent;
|
let comp: EditItemTemplatePageComponent;
|
||||||
@@ -45,7 +45,7 @@ describe('EditItemTemplatePageComponent', () => {
|
|||||||
describe('getCollectionEditUrl', () => {
|
describe('getCollectionEditUrl', () => {
|
||||||
it('should return the collection\'s edit url', () => {
|
it('should return the collection\'s edit url', () => {
|
||||||
const url = comp.getCollectionEditUrl(collection);
|
const url = comp.getCollectionEditUrl(collection);
|
||||||
expect(url).toEqual(getCollectionEditPath(collection.uuid));
|
expect(url).toEqual(getCollectionEditRoute(collection.uuid));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -5,7 +5,7 @@ import { Collection } from '../../core/shared/collection.model';
|
|||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { first, map } from 'rxjs/operators';
|
import { first, map } from 'rxjs/operators';
|
||||||
import { ItemTemplateDataService } from '../../core/data/item-template-data.service';
|
import { ItemTemplateDataService } from '../../core/data/item-template-data.service';
|
||||||
import { getCollectionEditPath } from '../collection-page-routing.module';
|
import { getCollectionEditRoute } from '../collection-page-routing-paths';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-edit-item-template-page',
|
selector: 'ds-edit-item-template-page',
|
||||||
@@ -35,7 +35,7 @@ export class EditItemTemplatePageComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
getCollectionEditUrl(collection: Collection): string {
|
getCollectionEditUrl(collection: Collection): string {
|
||||||
if (collection) {
|
if (collection) {
|
||||||
return getCollectionEditPath(collection.uuid);
|
return getCollectionEditRoute(collection.uuid);
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
24
src/app/+community-page/community-page-routing-paths.ts
Normal file
24
src/app/+community-page/community-page-routing-paths.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
||||||
|
|
||||||
|
export const COMMUNITY_PARENT_PARAMETER = 'parent';
|
||||||
|
|
||||||
|
export const COMMUNITY_MODULE_PATH = 'communities';
|
||||||
|
|
||||||
|
export function getCommunityModuleRoute() {
|
||||||
|
return `/${COMMUNITY_MODULE_PATH}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCommunityPageRoute(communityId: string) {
|
||||||
|
return new URLCombiner(getCommunityModuleRoute(), communityId).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCommunityEditRoute(id: string) {
|
||||||
|
return new URLCombiner(getCommunityModuleRoute(), id, COMMUNITY_EDIT_PATH).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCommunityCreateRoute() {
|
||||||
|
return new URLCombiner(getCommunityModuleRoute(), COMMUNITY_CREATE_PATH).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const COMMUNITY_CREATE_PATH = 'create';
|
||||||
|
export const COMMUNITY_EDIT_PATH = 'edit';
|
@@ -7,30 +7,12 @@ import { CreateCommunityPageComponent } from './create-community-page/create-com
|
|||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { CreateCommunityPageGuard } from './create-community-page/create-community-page.guard';
|
import { CreateCommunityPageGuard } from './create-community-page/create-community-page.guard';
|
||||||
import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
|
import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
|
||||||
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
|
||||||
import { getCommunityModulePath } from '../app-routing.module';
|
|
||||||
import { CommunityBreadcrumbResolver } from '../core/breadcrumbs/community-breadcrumb.resolver';
|
import { CommunityBreadcrumbResolver } from '../core/breadcrumbs/community-breadcrumb.resolver';
|
||||||
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
|
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
|
||||||
import { LinkService } from '../core/cache/builders/link.service';
|
import { LinkService } from '../core/cache/builders/link.service';
|
||||||
|
import { COMMUNITY_EDIT_PATH, COMMUNITY_CREATE_PATH } from './community-page-routing-paths';
|
||||||
import { CommunityPageAdministratorGuard } from './community-page-administrator.guard';
|
import { CommunityPageAdministratorGuard } from './community-page-administrator.guard';
|
||||||
|
|
||||||
export const COMMUNITY_PARENT_PARAMETER = 'parent';
|
|
||||||
|
|
||||||
export function getCommunityPageRoute(communityId: string) {
|
|
||||||
return new URLCombiner(getCommunityModulePath(), communityId).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCommunityEditPath(id: string) {
|
|
||||||
return new URLCombiner(getCommunityModulePath(), id, COMMUNITY_EDIT_PATH).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCommunityCreatePath() {
|
|
||||||
return new URLCombiner(getCommunityModulePath(), COMMUNITY_CREATE_PATH).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
const COMMUNITY_CREATE_PATH = 'create';
|
|
||||||
const COMMUNITY_EDIT_PATH = 'edit';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<ds-comcol-role
|
<ds-comcol-role
|
||||||
*ngFor="let comcolRole of getComcolRoles()"
|
*ngFor="let comcolRole of getComcolRoles$() | async"
|
||||||
[dso]="community$ | async"
|
[dso]="community$ | async"
|
||||||
[comcolRole]="comcolRole"
|
[comcolRole]="comcolRole"
|
||||||
>
|
>
|
||||||
|
@@ -4,8 +4,8 @@ import { Observable } from 'rxjs';
|
|||||||
import { first, map } from 'rxjs/operators';
|
import { first, map } from 'rxjs/operators';
|
||||||
import { Community } from '../../../core/shared/community.model';
|
import { Community } from '../../../core/shared/community.model';
|
||||||
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../core/shared/operators';
|
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../core/shared/operators';
|
||||||
import { ComcolRole } from '../../../shared/comcol-forms/edit-comcol-page/comcol-role/comcol-role';
|
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { HALLink } from '../../../core/shared/hal-link.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for managing a community's roles
|
* Component for managing a community's roles
|
||||||
@@ -31,10 +31,15 @@ export class CommunityRolesComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* The different roles for the community.
|
* The different roles for the community.
|
||||||
*/
|
*/
|
||||||
getComcolRoles(): ComcolRole[] {
|
getComcolRoles$(): Observable<HALLink[]> {
|
||||||
return [
|
return this.community$.pipe(
|
||||||
ComcolRole.COMMUNITY_ADMIN,
|
map((community) => [
|
||||||
];
|
{
|
||||||
|
name: 'community-admin',
|
||||||
|
href: community._links.adminGroup.href,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@@ -2,7 +2,7 @@ import { Component } from '@angular/core';
|
|||||||
import { Community } from '../../core/shared/community.model';
|
import { Community } from '../../core/shared/community.model';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
|
import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
|
||||||
import { getCommunityPageRoute } from '../community-page-routing.module';
|
import { getCommunityPageRoute } from '../community-page-routing-paths';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents the page where a user can edit an existing Community
|
* Component that represents the page where a user can edit an existing Community
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
<ds-submission-import-external></ds-submission-import-external>
|
@@ -0,0 +1,26 @@
|
|||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { ImportExternalPageComponent } from './import-external-page.component';
|
||||||
|
|
||||||
|
describe('ImportExternalPageComponent', () => {
|
||||||
|
let component: ImportExternalPageComponent;
|
||||||
|
let fixture: ComponentFixture<ImportExternalPageComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ImportExternalPageComponent ],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ImportExternalPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create ImportExternalPageComponent', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,13 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component representing the external import page of the submission.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-import-external-page',
|
||||||
|
templateUrl: './import-external-page.component.html',
|
||||||
|
styleUrls: ['./import-external-page.component.scss']
|
||||||
|
})
|
||||||
|
export class ImportExternalPageComponent {
|
||||||
|
|
||||||
|
}
|
29
src/app/+import-external-page/import-external-page.module.ts
Normal file
29
src/app/+import-external-page/import-external-page.module.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
import { CoreModule } from '../core/core.module';
|
||||||
|
import { ImportExternalRoutingModule } from './import-external-routing.module';
|
||||||
|
import { SubmissionModule } from '../submission/submission.module';
|
||||||
|
import { ImportExternalPageComponent } from './import-external-page.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SharedModule,
|
||||||
|
CoreModule.forRoot(),
|
||||||
|
ImportExternalRoutingModule,
|
||||||
|
SubmissionModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ImportExternalPageComponent
|
||||||
|
],
|
||||||
|
entryComponents: [ ]
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This module handles all components that are necessary for the submission external import page
|
||||||
|
*/
|
||||||
|
export class ImportExternalPageModule {
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,24 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
|
import { SubmissionImportExternalComponent } from '../submission/import-external/submission-import-external.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild([
|
||||||
|
{
|
||||||
|
canActivate: [ AuthenticatedGuard ],
|
||||||
|
path: '',
|
||||||
|
component: SubmissionImportExternalComponent,
|
||||||
|
pathMatch: 'full',
|
||||||
|
data: {
|
||||||
|
title: 'submission.import-external.page.title'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
],
|
||||||
|
providers: [ ]
|
||||||
|
})
|
||||||
|
export class ImportExternalRoutingModule {
|
||||||
|
|
||||||
|
}
|
@@ -11,7 +11,6 @@ import { ItemDataService } from '../../../core/data/item-data.service';
|
|||||||
import { AuthService } from '../../../core/auth/auth.service';
|
import { AuthService } from '../../../core/auth/auth.service';
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { getBitstreamModulePath } from '../../../app-routing.module';
|
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { Bundle } from '../../../core/shared/bundle.model';
|
import { Bundle } from '../../../core/shared/bundle.model';
|
||||||
import { BundleDataService } from '../../../core/data/bundle-data.service';
|
import { BundleDataService } from '../../../core/data/bundle-data.service';
|
||||||
@@ -19,8 +18,9 @@ import {
|
|||||||
getFirstSucceededRemoteDataPayload
|
getFirstSucceededRemoteDataPayload
|
||||||
} from '../../../core/shared/operators';
|
} from '../../../core/shared/operators';
|
||||||
import { UploaderComponent } from '../../../shared/uploader/uploader.component';
|
import { UploaderComponent } from '../../../shared/uploader/uploader.component';
|
||||||
import { getItemEditPath } from '../../item-page-routing.module';
|
|
||||||
import { RequestService } from '../../../core/data/request.service';
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
|
import { getBitstreamModuleRoute } from '../../../app-routing-paths';
|
||||||
|
import { getItemEditRoute } from '../../item-page-routing-paths';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-upload-bitstream',
|
selector: 'ds-upload-bitstream',
|
||||||
@@ -171,7 +171,7 @@ export class UploadBitstreamComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// Bring over the item ID as a query parameter
|
// Bring over the item ID as a query parameter
|
||||||
const queryParams = { itemId: this.itemId };
|
const queryParams = { itemId: this.itemId };
|
||||||
this.router.navigate([getBitstreamModulePath(), bitstream.id, 'edit'], { queryParams: queryParams });
|
this.router.navigate([getBitstreamModuleRoute(), bitstream.id, 'edit'], { queryParams: queryParams });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -196,7 +196,7 @@ export class UploadBitstreamComponent implements OnInit, OnDestroy {
|
|||||||
* When cancel is clicked, navigate back to the item's edit bitstreams page
|
* When cancel is clicked, navigate back to the item's edit bitstreams page
|
||||||
*/
|
*/
|
||||||
onCancel() {
|
onCancel() {
|
||||||
this.router.navigate([getItemEditPath(this.itemId), 'bitstreams']);
|
this.router.navigate([getItemEditRoute(this.itemId), 'bitstreams']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -6,7 +6,7 @@ import { Item } from '../../core/shared/item.model';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { getItemPageRoute } from '../item-page-routing.module';
|
import { getItemPageRoute } from '../item-page-routing-paths';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-edit-item-page',
|
selector: 'ds-edit-item-page',
|
||||||
|
@@ -0,0 +1,7 @@
|
|||||||
|
export const ITEM_EDIT_WITHDRAW_PATH = 'withdraw';
|
||||||
|
export const ITEM_EDIT_REINSTATE_PATH = 'reinstate';
|
||||||
|
export const ITEM_EDIT_PRIVATE_PATH = 'private';
|
||||||
|
export const ITEM_EDIT_PUBLIC_PATH = 'public';
|
||||||
|
export const ITEM_EDIT_DELETE_PATH = 'delete';
|
||||||
|
export const ITEM_EDIT_MOVE_PATH = 'move';
|
||||||
|
export const ITEM_EDIT_AUTHORIZATIONS_PATH = 'authorizations';
|
@@ -20,17 +20,18 @@ import { ResourcePolicyResolver } from '../../shared/resource-policies/resolvers
|
|||||||
import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
|
import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
|
||||||
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
|
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
|
||||||
import { I18nBreadcrumbsService } from '../../core/breadcrumbs/i18n-breadcrumbs.service';
|
import { I18nBreadcrumbsService } from '../../core/breadcrumbs/i18n-breadcrumbs.service';
|
||||||
|
import {
|
||||||
|
ITEM_EDIT_AUTHORIZATIONS_PATH,
|
||||||
|
ITEM_EDIT_MOVE_PATH,
|
||||||
|
ITEM_EDIT_DELETE_PATH,
|
||||||
|
ITEM_EDIT_PUBLIC_PATH,
|
||||||
|
ITEM_EDIT_PRIVATE_PATH,
|
||||||
|
ITEM_EDIT_REINSTATE_PATH,
|
||||||
|
ITEM_EDIT_WITHDRAW_PATH
|
||||||
|
} from './edit-item-page.routing-paths';
|
||||||
import { ItemPageReinstateGuard } from './item-page-reinstate.guard';
|
import { ItemPageReinstateGuard } from './item-page-reinstate.guard';
|
||||||
import { ItemPageWithdrawGuard } from './item-page-withdraw.guard';
|
import { ItemPageWithdrawGuard } from './item-page-withdraw.guard';
|
||||||
|
|
||||||
export const ITEM_EDIT_WITHDRAW_PATH = 'withdraw';
|
|
||||||
export const ITEM_EDIT_REINSTATE_PATH = 'reinstate';
|
|
||||||
export const ITEM_EDIT_PRIVATE_PATH = 'private';
|
|
||||||
export const ITEM_EDIT_PUBLIC_PATH = 'public';
|
|
||||||
export const ITEM_EDIT_DELETE_PATH = 'delete';
|
|
||||||
export const ITEM_EDIT_MOVE_PATH = 'move';
|
|
||||||
export const ITEM_EDIT_AUTHORIZATIONS_PATH = 'authorizations';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Routing module that handles the routing for the Edit Item page administrator functionality
|
* Routing module that handles the routing for the Edit Item page administrator functionality
|
||||||
*/
|
*/
|
||||||
|
@@ -125,7 +125,7 @@ export class ItemCollectionMapperComponent implements OnInit {
|
|||||||
switchMap(([itemCollectionsRD, owningCollectionRD, searchOptions]) => {
|
switchMap(([itemCollectionsRD, owningCollectionRD, searchOptions]) => {
|
||||||
return this.searchService.search(Object.assign(new PaginatedSearchOptions(searchOptions), {
|
return this.searchService.search(Object.assign(new PaginatedSearchOptions(searchOptions), {
|
||||||
query: this.buildQuery([...itemCollectionsRD.payload.page, owningCollectionRD.payload], searchOptions.query),
|
query: this.buildQuery([...itemCollectionsRD.payload.page, owningCollectionRD.payload], searchOptions.query),
|
||||||
dsoType: DSpaceObjectType.COLLECTION
|
dsoTypes: [DSpaceObjectType.COLLECTION]
|
||||||
}), 10000).pipe(
|
}), 10000).pipe(
|
||||||
toDSpaceObjectListRD(),
|
toDSpaceObjectListRD(),
|
||||||
startWith(undefined)
|
startWith(undefined)
|
||||||
|
@@ -16,7 +16,6 @@ import { NotificationsService } from '../../../shared/notifications/notification
|
|||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { ItemDeleteComponent } from './item-delete.component';
|
import { ItemDeleteComponent } from './item-delete.component';
|
||||||
import { getItemEditPath } from '../../item-page-routing.module';
|
|
||||||
import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils';
|
||||||
import { VarDirective } from '../../../shared/utils/var.directive';
|
import { VarDirective } from '../../../shared/utils/var.directive';
|
||||||
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
||||||
@@ -26,6 +25,7 @@ import { RemoteData } from '../../../core/data/remote-data';
|
|||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
import { EntityTypeService } from '../../../core/data/entity-type.service';
|
import { EntityTypeService } from '../../../core/data/entity-type.service';
|
||||||
|
import { getItemEditRoute } from '../../item-page-routing-paths';
|
||||||
|
|
||||||
let comp: ItemDeleteComponent;
|
let comp: ItemDeleteComponent;
|
||||||
let fixture: ComponentFixture<ItemDeleteComponent>;
|
let fixture: ComponentFixture<ItemDeleteComponent>;
|
||||||
@@ -233,7 +233,7 @@ describe('ItemDeleteComponent', () => {
|
|||||||
describe('notify', () => {
|
describe('notify', () => {
|
||||||
it('should navigate to the item edit page on failed deletion of the item', () => {
|
it('should navigate to the item edit page on failed deletion of the item', () => {
|
||||||
comp.notify(false);
|
comp.notify(false);
|
||||||
expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditPath('fake-id')]);
|
expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute('fake-id')]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { defaultIfEmpty, filter, first, map, switchMap, take } from 'rxjs/operators';
|
import { defaultIfEmpty, filter, first, map, switchMap, take } from 'rxjs/operators';
|
||||||
import { AbstractSimpleItemActionComponent } from '../simple-item-action/abstract-simple-item-action.component';
|
import { AbstractSimpleItemActionComponent } from '../simple-item-action/abstract-simple-item-action.component';
|
||||||
import { getItemEditPath } from '../../item-page-routing.module';
|
|
||||||
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { combineLatest as observableCombineLatest, combineLatest, Observable, of as observableOf } from 'rxjs';
|
import { combineLatest as observableCombineLatest, combineLatest, Observable, of as observableOf } from 'rxjs';
|
||||||
import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model';
|
import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model';
|
||||||
@@ -22,6 +21,7 @@ import { EntityTypeService } from '../../../core/data/entity-type.service';
|
|||||||
import { LinkService } from '../../../core/cache/builders/link.service';
|
import { LinkService } from '../../../core/cache/builders/link.service';
|
||||||
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
|
import { getItemEditRoute } from '../../item-page-routing-paths';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-delete',
|
selector: 'ds-item-delete',
|
||||||
@@ -345,7 +345,7 @@ export class ItemDeleteComponent
|
|||||||
this.router.navigate(['']);
|
this.router.navigate(['']);
|
||||||
} else {
|
} else {
|
||||||
this.notificationsService.error(this.translateService.get('item.edit.' + this.messageKey + '.error'));
|
this.notificationsService.error(this.translateService.get('item.edit.' + this.messageKey + '.error'));
|
||||||
this.router.navigate([getItemEditPath(this.item.id)]);
|
this.router.navigate([getItemEditRoute(this.item.id)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,6 @@ import { NotificationsService } from '../../../shared/notifications/notification
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
import { getItemEditPath } from '../../item-page-routing.module';
|
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
@@ -18,6 +17,7 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio
|
|||||||
import { SearchService } from '../../../core/shared/search/search.service';
|
import { SearchService } from '../../../core/shared/search/search.service';
|
||||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
||||||
import { SearchResult } from '../../../shared/search/search-result.model';
|
import { SearchResult } from '../../../shared/search/search-result.model';
|
||||||
|
import { getItemEditRoute } from '../../item-page-routing-paths';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-move',
|
selector: 'ds-item-move',
|
||||||
@@ -79,7 +79,7 @@ export class ItemMoveComponent implements OnInit {
|
|||||||
loadSuggestions(query): void {
|
loadSuggestions(query): void {
|
||||||
this.collectionSearchResults = this.searchService.search(new PaginatedSearchOptions({
|
this.collectionSearchResults = this.searchService.search(new PaginatedSearchOptions({
|
||||||
pagination: this.pagination,
|
pagination: this.pagination,
|
||||||
dsoType: DSpaceObjectType.COLLECTION,
|
dsoTypes: [DSpaceObjectType.COLLECTION],
|
||||||
query: query
|
query: query
|
||||||
})).pipe(
|
})).pipe(
|
||||||
first(),
|
first(),
|
||||||
@@ -116,7 +116,7 @@ export class ItemMoveComponent implements OnInit {
|
|||||||
this.processing = true;
|
this.processing = true;
|
||||||
this.itemDataService.moveToCollection(this.itemId, this.selectedCollection).pipe(first()).subscribe(
|
this.itemDataService.moveToCollection(this.itemId, this.selectedCollection).pipe(first()).subscribe(
|
||||||
(response: RestResponse) => {
|
(response: RestResponse) => {
|
||||||
this.router.navigate([getItemEditPath(this.itemId)]);
|
this.router.navigate([getItemEditRoute(this.itemId)]);
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
this.notificationsService.success(this.translateService.get('item.edit.move.success'));
|
this.notificationsService.success(this.translateService.get('item.edit.move.success'));
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||||
import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
@@ -6,7 +6,7 @@ import { ItemOperation } from '../item-operation/itemOperation.model';
|
|||||||
import { distinctUntilChanged, first, map } from 'rxjs/operators';
|
import { distinctUntilChanged, first, map } from 'rxjs/operators';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { getItemEditPath, getItemPageRoute } from '../../item-page-routing.module';
|
import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths';
|
||||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||||
import { hasValue } from '../../../shared/empty.util';
|
import { hasValue } from '../../../shared/empty.util';
|
||||||
@@ -126,7 +126,7 @@ export class ItemStatusComponent implements OnInit {
|
|||||||
* @returns {string} url
|
* @returns {string} url
|
||||||
*/
|
*/
|
||||||
getCurrentUrl(item: Item): string {
|
getCurrentUrl(item: Item): string {
|
||||||
return getItemEditPath(item.id);
|
return getItemEditRoute(item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
trackOperation(index: number, operation: ItemOperation) {
|
trackOperation(index: number, operation: ItemOperation) {
|
||||||
|
@@ -15,12 +15,12 @@ import { RemoteData } from '../../../core/data/remote-data';
|
|||||||
import { AbstractSimpleItemActionComponent } from './abstract-simple-item-action.component';
|
import { AbstractSimpleItemActionComponent } from './abstract-simple-item-action.component';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { getItemEditPath } from '../../item-page-routing.module';
|
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
import {
|
import {
|
||||||
createSuccessfulRemoteDataObject,
|
createSuccessfulRemoteDataObject,
|
||||||
createSuccessfulRemoteDataObject$
|
createSuccessfulRemoteDataObject$
|
||||||
} from '../../../shared/remote-data.utils';
|
} from '../../../shared/remote-data.utils';
|
||||||
|
import { getItemEditRoute } from '../../item-page-routing-paths';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test component that implements the AbstractSimpleItemActionComponent used to test the
|
* Test component that implements the AbstractSimpleItemActionComponent used to test the
|
||||||
@@ -136,14 +136,14 @@ describe('AbstractSimpleItemActionComponent', () => {
|
|||||||
comp.processRestResponse(successfulRestResponse);
|
comp.processRestResponse(successfulRestResponse);
|
||||||
|
|
||||||
expect(notificationsServiceStub.success).toHaveBeenCalled();
|
expect(notificationsServiceStub.success).toHaveBeenCalled();
|
||||||
expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditPath(mockItem.id)]);
|
expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute(mockItem.id)]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should process a RestResponse to navigate and display success notification', () => {
|
it('should process a RestResponse to navigate and display success notification', () => {
|
||||||
comp.processRestResponse(failRestResponse);
|
comp.processRestResponse(failRestResponse);
|
||||||
|
|
||||||
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
||||||
expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditPath(mockItem.id)]);
|
expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute(mockItem.id)]);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -9,8 +9,8 @@ import {Observable} from 'rxjs';
|
|||||||
import {getSucceededRemoteData} from '../../../core/shared/operators';
|
import {getSucceededRemoteData} from '../../../core/shared/operators';
|
||||||
import {first, map} from 'rxjs/operators';
|
import {first, map} from 'rxjs/operators';
|
||||||
import {findSuccessfulAccordingTo} from '../edit-item-operators';
|
import {findSuccessfulAccordingTo} from '../edit-item-operators';
|
||||||
import {getItemEditPath} from '../../item-page-routing.module';
|
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
|
import { getItemEditRoute } from '../../item-page-routing-paths';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to render and handle simple item edit actions such as withdrawal and reinstatement.
|
* Component to render and handle simple item edit actions such as withdrawal and reinstatement.
|
||||||
@@ -73,11 +73,11 @@ export class AbstractSimpleItemActionComponent implements OnInit {
|
|||||||
this.itemDataService.findById(this.item.id).pipe(
|
this.itemDataService.findById(this.item.id).pipe(
|
||||||
findSuccessfulAccordingTo(this.predicate)).subscribe(() => {
|
findSuccessfulAccordingTo(this.predicate)).subscribe(() => {
|
||||||
this.notificationsService.success(this.translateService.get('item.edit.' + this.messageKey + '.success'));
|
this.notificationsService.success(this.translateService.get('item.edit.' + this.messageKey + '.success'));
|
||||||
this.router.navigate([getItemEditPath(this.item.id)]);
|
this.router.navigate([getItemEditRoute(this.item.id)]);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.notificationsService.error(this.translateService.get('item.edit.' + this.messageKey + '.error'));
|
this.notificationsService.error(this.translateService.get('item.edit.' + this.messageKey + '.error'));
|
||||||
this.router.navigate([getItemEditPath(this.item.id)]);
|
this.router.navigate([getItemEditRoute(this.item.id)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,29 +1,87 @@
|
|||||||
<ds-metadata-field-wrapper [label]="label | translate">
|
<ds-metadata-field-wrapper [label]="label | translate">
|
||||||
<div class="file-section row" *ngFor="let file of (bitstreams$ | async); let last=last;">
|
<div *ngVar="(originals$ | async)?.payload as originals">
|
||||||
<div class="col-3">
|
<h5 class="simple-view-element-header">{{"item.page.filesection.original.bundle" | translate}}</h5>
|
||||||
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
|
<ds-pagination *ngIf="originals?.page?.length > 0"
|
||||||
</div>
|
[hideGear]="true"
|
||||||
<div class="col-7">
|
[hidePagerWhenSinglePage]="true"
|
||||||
<dl class="row">
|
[paginationOptions]="originalOptions"
|
||||||
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
|
[pageInfoState]="originals"
|
||||||
<dd class="col-md-8">{{file.name}}</dd>
|
[collectionSize]="originals?.totalElements"
|
||||||
|
[disableRouteParameterUpdate]="true"
|
||||||
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
|
(pageChange)="switchOriginalPage($event)">
|
||||||
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
|
|
||||||
|
|
||||||
|
|
||||||
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
|
|
||||||
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
|
<div class="file-section row" *ngFor="let file of originals?.page;">
|
||||||
|
<div class="col-3">
|
||||||
|
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
|
||||||
|
</div>
|
||||||
|
<div class="col-7">
|
||||||
|
<dl class="row">
|
||||||
|
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
|
||||||
|
<dd class="col-md-8">{{file.name}}</dd>
|
||||||
|
|
||||||
|
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
|
||||||
|
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
|
||||||
|
|
||||||
|
|
||||||
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
|
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
|
||||||
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
|
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
|
||||||
<ds-file-download-link [href]="file._links.content.href" [download]="file.name">
|
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
|
||||||
{{"item.page.filesection.download" | translate}}
|
</dl>
|
||||||
</ds-file-download-link>
|
</div>
|
||||||
</div>
|
<div class="col-2">
|
||||||
|
<ds-file-download-link [href]="file._links.content.href" [download]="file.name">
|
||||||
|
{{"item.page.filesection.download" | translate}}
|
||||||
|
</ds-file-download-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ds-pagination>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngVar="(licenses$ | async)?.payload as licenses">
|
||||||
|
<h5 class="simple-view-element-header">{{"item.page.filesection.license.bundle" | translate}}</h5>
|
||||||
|
<ds-pagination *ngIf="licenses?.page?.length > 0"
|
||||||
|
[hideGear]="true"
|
||||||
|
[hidePagerWhenSinglePage]="true"
|
||||||
|
[paginationOptions]="licenseOptions"
|
||||||
|
[pageInfoState]="licenses"
|
||||||
|
[collectionSize]="licenses?.totalElements"
|
||||||
|
[disableRouteParameterUpdate]="true"
|
||||||
|
(pageChange)="switchLicensePage($event)">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="file-section row" *ngFor="let file of licenses?.page;">
|
||||||
|
<div class="col-3">
|
||||||
|
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
|
||||||
|
</div>
|
||||||
|
<div class="col-7">
|
||||||
|
<dl class="row">
|
||||||
|
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
|
||||||
|
<dd class="col-md-8">{{file.name}}</dd>
|
||||||
|
|
||||||
|
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
|
||||||
|
<dd class="col-md-8">{{(file.sizeBytes) | dsFileSize }}</dd>
|
||||||
|
|
||||||
|
|
||||||
|
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
|
||||||
|
<dd class="col-md-8">{{(file.format | async)?.payload?.description}}</dd>
|
||||||
|
|
||||||
|
|
||||||
|
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
|
||||||
|
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<ds-file-download-link [href]="file._links.content.href" [download]="file.name">
|
||||||
|
{{"item.page.filesection.download" | translate}}
|
||||||
|
</ds-file-download-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ds-pagination>
|
||||||
</div>
|
</div>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
|
@@ -0,0 +1,117 @@
|
|||||||
|
import {FullFileSectionComponent} from './full-file-section.component';
|
||||||
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
import {createSuccessfulRemoteDataObject$} from '../../../../shared/remote-data.utils';
|
||||||
|
import {createPaginatedList} from '../../../../shared/testing/utils.test';
|
||||||
|
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
|
||||||
|
import {TranslateLoaderMock} from '../../../../shared/mocks/translate-loader.mock';
|
||||||
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
|
import {VarDirective} from '../../../../shared/utils/var.directive';
|
||||||
|
import {FileSizePipe} from '../../../../shared/utils/file-size-pipe';
|
||||||
|
import {MetadataFieldWrapperComponent} from '../../../field-components/metadata-field-wrapper/metadata-field-wrapper.component';
|
||||||
|
import {BitstreamDataService} from '../../../../core/data/bitstream-data.service';
|
||||||
|
import {NO_ERRORS_SCHEMA} from '@angular/core';
|
||||||
|
import {Bitstream} from '../../../../core/shared/bitstream.model';
|
||||||
|
import {of as observableOf} from 'rxjs';
|
||||||
|
import {MockBitstreamFormat1} from '../../../../shared/mocks/item.mock';
|
||||||
|
import {By} from '@angular/platform-browser';
|
||||||
|
|
||||||
|
describe('FullFileSectionComponent', () => {
|
||||||
|
let comp: FullFileSectionComponent;
|
||||||
|
let fixture: ComponentFixture<FullFileSectionComponent>;
|
||||||
|
|
||||||
|
const mockBitstream: Bitstream = Object.assign(new Bitstream(),
|
||||||
|
{
|
||||||
|
sizeBytes: 10201,
|
||||||
|
content: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/content',
|
||||||
|
format: observableOf(MockBitstreamFormat1),
|
||||||
|
bundleName: 'ORIGINAL',
|
||||||
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713'
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/content'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
id: 'cf9b0c8e-a1eb-4b65-afd0-567366448713',
|
||||||
|
uuid: 'cf9b0c8e-a1eb-4b65-afd0-567366448713',
|
||||||
|
type: 'bitstream',
|
||||||
|
metadata: {
|
||||||
|
'dc.title': [
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: 'test_word.docx'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const bitstreamDataService = jasmine.createSpyObj('bitstreamDataService', {
|
||||||
|
findAllByItemAndBundleName: createSuccessfulRemoteDataObject$(createPaginatedList([mockBitstream, mockBitstream, mockBitstream]))
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: TranslateLoaderMock
|
||||||
|
}
|
||||||
|
}), BrowserAnimationsModule],
|
||||||
|
declarations: [FullFileSectionComponent, VarDirective, FileSizePipe, MetadataFieldWrapperComponent],
|
||||||
|
providers: [
|
||||||
|
{provide: BitstreamDataService, useValue: bitstreamDataService}
|
||||||
|
],
|
||||||
|
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
fixture = TestBed.createComponent(FullFileSectionComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('when the full file section gets loaded with bitstreams available', () => {
|
||||||
|
it ('should contain a list with bitstreams', () => {
|
||||||
|
const fileSection = fixture.debugElement.queryAll(By.css('.file-section'));
|
||||||
|
expect(fileSection.length).toEqual(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when we press the pageChange button for original bundle', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.switchOriginalPage(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it ('should give the value to the currentpage', () => {
|
||||||
|
expect(comp.originalOptions.currentPage).toBe(2);
|
||||||
|
})
|
||||||
|
it ('should call the next function on the originalCurrentPage', (done) => {
|
||||||
|
comp.originalCurrentPage$.subscribe((event) => {
|
||||||
|
expect(event).toEqual(2);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when we press the pageChange button for license bundle', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.switchLicensePage(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it ('should give the value to the currentpage', () => {
|
||||||
|
expect(comp.licenseOptions.currentPage).toBe(2);
|
||||||
|
})
|
||||||
|
it ('should call the next function on the licenseCurrentPage', (done) => {
|
||||||
|
comp.licenseCurrentPage$.subscribe((event) => {
|
||||||
|
expect(event).toEqual(2);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@@ -1,13 +1,15 @@
|
|||||||
import { Component, Injector, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { map, startWith } from 'rxjs/operators';
|
|
||||||
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
|
||||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
|
|
||||||
import { followLink } from '../../../../shared/utils/follow-link-config.model';
|
import { followLink } from '../../../../shared/utils/follow-link-config.model';
|
||||||
import { FileSectionComponent } from '../../../simple/field-components/file-section/file-section.component';
|
import { FileSectionComponent } from '../../../simple/field-components/file-section/file-section.component';
|
||||||
|
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
|
||||||
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the file section of the item
|
* This component renders the file section of the item
|
||||||
@@ -25,7 +27,23 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
|
|||||||
|
|
||||||
label: string;
|
label: string;
|
||||||
|
|
||||||
bitstreams$: Observable<Bitstream[]>;
|
originals$: Observable<RemoteData<PaginatedList<Bitstream>>>;
|
||||||
|
licenses$: Observable<RemoteData<PaginatedList<Bitstream>>>;
|
||||||
|
|
||||||
|
pageSize = 5;
|
||||||
|
originalOptions = Object.assign(new PaginationComponentOptions(),{
|
||||||
|
id: 'original-bitstreams-options',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: this.pageSize
|
||||||
|
});
|
||||||
|
originalCurrentPage$ = new BehaviorSubject<number>(1);
|
||||||
|
|
||||||
|
licenseOptions = Object.assign(new PaginationComponentOptions(),{
|
||||||
|
id: 'license-bitstreams-options',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: this.pageSize
|
||||||
|
});
|
||||||
|
licenseCurrentPage$ = new BehaviorSubject<number>(1);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
bitstreamDataService: BitstreamDataService
|
bitstreamDataService: BitstreamDataService
|
||||||
@@ -34,40 +52,45 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
super.ngOnInit();
|
this.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(): void {
|
initialize(): void {
|
||||||
// TODO pagination
|
this.originals$ = this.originalCurrentPage$.pipe(
|
||||||
const originals$ = this.bitstreamDataService.findAllByItemAndBundleName(
|
switchMap((pageNumber: number) => this.bitstreamDataService.findAllByItemAndBundleName(
|
||||||
this.item,
|
this.item,
|
||||||
'ORIGINAL',
|
'ORIGINAL',
|
||||||
{ elementsPerPage: Number.MAX_SAFE_INTEGER },
|
{ elementsPerPage: this.pageSize, currentPage: pageNumber },
|
||||||
followLink( 'format')
|
followLink( 'format')
|
||||||
).pipe(
|
))
|
||||||
getFirstSucceededRemoteListPayload(),
|
|
||||||
startWith([])
|
|
||||||
);
|
);
|
||||||
const licenses$ = this.bitstreamDataService.findAllByItemAndBundleName(
|
|
||||||
this.item,
|
this.licenses$ = this.licenseCurrentPage$.pipe(
|
||||||
'LICENSE',
|
switchMap((pageNumber: number) => this.bitstreamDataService.findAllByItemAndBundleName(
|
||||||
{ elementsPerPage: Number.MAX_SAFE_INTEGER },
|
this.item,
|
||||||
followLink( 'format')
|
'LICENSE',
|
||||||
).pipe(
|
{ elementsPerPage: this.pageSize, currentPage: pageNumber },
|
||||||
getFirstSucceededRemoteListPayload(),
|
followLink( 'format')
|
||||||
startWith([])
|
))
|
||||||
);
|
|
||||||
this.bitstreams$ = observableCombineLatest(originals$, licenses$).pipe(
|
|
||||||
map(([o, l]) => [...o, ...l]),
|
|
||||||
map((files: Bitstream[]) =>
|
|
||||||
files.map(
|
|
||||||
(original) => {
|
|
||||||
original.thumbnail = this.bitstreamDataService.getMatchingThumbnail(this.item, original);
|
|
||||||
return original;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the current page for the original bundle bitstreams
|
||||||
|
* @param page
|
||||||
|
*/
|
||||||
|
switchOriginalPage(page: number) {
|
||||||
|
this.originalOptions.currentPage = page;
|
||||||
|
this.originalCurrentPage$.next(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the current page for the license bundle bitstreams
|
||||||
|
* @param page
|
||||||
|
*/
|
||||||
|
switchLicensePage(page: number) {
|
||||||
|
this.licenseOptions.currentPage = page;
|
||||||
|
this.licenseCurrentPage$.next(page);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
18
src/app/+item-page/item-page-routing-paths.ts
Normal file
18
src/app/+item-page/item-page-routing-paths.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
||||||
|
|
||||||
|
export const ITEM_MODULE_PATH = 'items';
|
||||||
|
|
||||||
|
export function getItemModuleRoute() {
|
||||||
|
return `/${ITEM_MODULE_PATH}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getItemPageRoute(itemId: string) {
|
||||||
|
return new URLCombiner(getItemModuleRoute(), itemId).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getItemEditRoute(id: string) {
|
||||||
|
return new URLCombiner(getItemModuleRoute(), id, ITEM_EDIT_PATH).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ITEM_EDIT_PATH = 'edit';
|
||||||
|
export const UPLOAD_BITSTREAM_PATH = 'bitstreams/new';
|
@@ -4,26 +4,14 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { ItemPageComponent } from './simple/item-page.component';
|
import { ItemPageComponent } from './simple/item-page.component';
|
||||||
import { FullItemPageComponent } from './full/full-item-page.component';
|
import { FullItemPageComponent } from './full/full-item-page.component';
|
||||||
import { ItemPageResolver } from './item-page.resolver';
|
import { ItemPageResolver } from './item-page.resolver';
|
||||||
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
|
||||||
import { getItemModulePath } from '../app-routing.module';
|
|
||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { ItemBreadcrumbResolver } from '../core/breadcrumbs/item-breadcrumb.resolver';
|
import { ItemBreadcrumbResolver } from '../core/breadcrumbs/item-breadcrumb.resolver';
|
||||||
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
|
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
|
||||||
import { LinkService } from '../core/cache/builders/link.service';
|
import { LinkService } from '../core/cache/builders/link.service';
|
||||||
import { UploadBitstreamComponent } from './bitstreams/upload/upload-bitstream.component';
|
import { UploadBitstreamComponent } from './bitstreams/upload/upload-bitstream.component';
|
||||||
|
import { UPLOAD_BITSTREAM_PATH, ITEM_EDIT_PATH } from './item-page-routing-paths';
|
||||||
import { ItemPageAdministratorGuard } from './item-page-administrator.guard';
|
import { ItemPageAdministratorGuard } from './item-page-administrator.guard';
|
||||||
|
|
||||||
export function getItemPageRoute(itemId: string) {
|
|
||||||
return new URLCombiner(getItemModulePath(), itemId).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getItemEditPath(id: string) {
|
|
||||||
return new URLCombiner(getItemModulePath(), id, ITEM_EDIT_PATH).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
const ITEM_EDIT_PATH = 'edit';
|
|
||||||
const UPLOAD_BITSTREAM_PATH = 'bitstreams/new';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
|
@@ -6,6 +6,13 @@
|
|||||||
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
|
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
|
||||||
<span *ngIf="!last" innerHTML="{{separator}}"></span>
|
<span *ngIf="!last" innerHTML="{{separator}}"></span>
|
||||||
</ds-file-download-link>
|
</ds-file-download-link>
|
||||||
|
<ds-loading *ngIf="isLoading" message="{{'loading.default' | translate}}" [showMessage]="false"></ds-loading>
|
||||||
|
<div *ngIf="!isLastPage" class="mt-1" id="view-more">
|
||||||
|
<a class="bitstream-view-more btn btn-outline-secondary btn-sm" [routerLink]="" (click)="getNextPage()">{{'item.page.bitstreams.view-more' | translate}}</a>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isLastPage && currentPage != 1" class="mt-1" id="collapse">
|
||||||
|
<a class="bitstream-collapse btn btn-outline-secondary btn-sm" [routerLink]="" (click)="currentPage = undefined; getNextPage();">{{'item.page.bitstreams.collapse' | translate}}</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@@ -0,0 +1,169 @@
|
|||||||
|
import {FileSectionComponent} from './file-section.component';
|
||||||
|
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
|
||||||
|
import {TranslateLoaderMock} from '../../../../shared/mocks/translate-loader.mock';
|
||||||
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
|
import {VarDirective} from '../../../../shared/utils/var.directive';
|
||||||
|
import {NO_ERRORS_SCHEMA} from '@angular/core';
|
||||||
|
import {BitstreamDataService} from '../../../../core/data/bitstream-data.service';
|
||||||
|
import {createSuccessfulRemoteDataObject$} from '../../../../shared/remote-data.utils';
|
||||||
|
import {By} from '@angular/platform-browser';
|
||||||
|
import {Bitstream} from '../../../../core/shared/bitstream.model';
|
||||||
|
import {of as observableOf} from 'rxjs';
|
||||||
|
import {MockBitstreamFormat1} from '../../../../shared/mocks/item.mock';
|
||||||
|
import {FileSizePipe} from '../../../../shared/utils/file-size-pipe';
|
||||||
|
import {PageInfo} from '../../../../core/shared/page-info.model';
|
||||||
|
import {MetadataFieldWrapperComponent} from '../../../field-components/metadata-field-wrapper/metadata-field-wrapper.component';
|
||||||
|
import {createPaginatedList} from '../../../../shared/testing/utils.test';
|
||||||
|
|
||||||
|
describe('FileSectionComponent', () => {
|
||||||
|
let comp: FileSectionComponent;
|
||||||
|
let fixture: ComponentFixture<FileSectionComponent>;
|
||||||
|
|
||||||
|
const bitstreamDataService = jasmine.createSpyObj('bitstreamDataService', {
|
||||||
|
findAllByItemAndBundleName: createSuccessfulRemoteDataObject$(createPaginatedList([]))
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockBitstream: Bitstream = Object.assign(new Bitstream(),
|
||||||
|
{
|
||||||
|
sizeBytes: 10201,
|
||||||
|
content: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/content',
|
||||||
|
format: observableOf(MockBitstreamFormat1),
|
||||||
|
bundleName: 'ORIGINAL',
|
||||||
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713'
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/bitstreams/cf9b0c8e-a1eb-4b65-afd0-567366448713/content'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
id: 'cf9b0c8e-a1eb-4b65-afd0-567366448713',
|
||||||
|
uuid: 'cf9b0c8e-a1eb-4b65-afd0-567366448713',
|
||||||
|
type: 'bitstream',
|
||||||
|
metadata: {
|
||||||
|
'dc.title': [
|
||||||
|
{
|
||||||
|
language: null,
|
||||||
|
value: 'test_word.docx'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: TranslateLoaderMock
|
||||||
|
}
|
||||||
|
}), BrowserAnimationsModule],
|
||||||
|
declarations: [FileSectionComponent, VarDirective, FileSizePipe, MetadataFieldWrapperComponent],
|
||||||
|
providers: [
|
||||||
|
{provide: BitstreamDataService, useValue: bitstreamDataService}
|
||||||
|
],
|
||||||
|
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
fixture = TestBed.createComponent(FileSectionComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('when the bitstreams are loading', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.bitstreams$.next([mockBitstream]);
|
||||||
|
comp.isLoading = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display a loading component', () => {
|
||||||
|
const loading = fixture.debugElement.query(By.css('ds-loading'));
|
||||||
|
expect(loading.nativeElement).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the "Show more" button is clicked', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.bitstreams$.next([mockBitstream]);
|
||||||
|
comp.currentPage = 1;
|
||||||
|
comp.isLastPage = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call the service to retrieve more bitstreams', () => {
|
||||||
|
const viewMore = fixture.debugElement.query(By.css('.bitstream-view-more'));
|
||||||
|
viewMore.triggerEventHandler('click', null);
|
||||||
|
expect(bitstreamDataService.findAllByItemAndBundleName).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('one bitstream should be on the page', () => {
|
||||||
|
const viewMore = fixture.debugElement.query(By.css('.bitstream-view-more'));
|
||||||
|
viewMore.triggerEventHandler('click', null);
|
||||||
|
const fileDownloadLink = fixture.debugElement.queryAll(By.css('ds-file-download-link'));
|
||||||
|
expect(fileDownloadLink.length).toEqual(1);
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when it is then clicked again', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
bitstreamDataService.findAllByItemAndBundleName.and.returnValue(createSuccessfulRemoteDataObject$(createPaginatedList([mockBitstream])));
|
||||||
|
const viewMore = fixture.debugElement.query(By.css('.bitstream-view-more'));
|
||||||
|
viewMore.triggerEventHandler('click', null);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
})
|
||||||
|
it('should contain another bitstream', () => {
|
||||||
|
const fileDownloadLink = fixture.debugElement.queryAll(By.css('ds-file-download-link'));
|
||||||
|
expect(fileDownloadLink.length).toEqual(2);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when its the last page of bitstreams', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.bitstreams$.next([mockBitstream]);
|
||||||
|
comp.isLastPage = true;
|
||||||
|
comp.currentPage = 2;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not contain a view more link', () => {
|
||||||
|
const viewMore = fixture.debugElement.query(By.css('.bitstream-view-more'));
|
||||||
|
expect(viewMore).toBeNull();
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should contain a view less link', () => {
|
||||||
|
const viewLess = fixture.debugElement.query(By.css('.bitstream-collapse'));
|
||||||
|
expect(viewLess).toBeDefined();
|
||||||
|
})
|
||||||
|
|
||||||
|
it('clicking on the view less link should reset the pages and call getNextPage()', () => {
|
||||||
|
const pageInfo = Object.assign(new PageInfo(), {
|
||||||
|
elementsPerPage: 3,
|
||||||
|
totalElements: 5,
|
||||||
|
totalPages: 2,
|
||||||
|
currentPage: 1,
|
||||||
|
_links: {
|
||||||
|
self: {href: 'https://rest.api/core/bitstreams/'},
|
||||||
|
next: {href: 'https://rest.api/core/bitstreams?page=2'}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const PaginatedList = Object.assign(createPaginatedList([mockBitstream]), {
|
||||||
|
pageInfo: pageInfo
|
||||||
|
});
|
||||||
|
bitstreamDataService.findAllByItemAndBundleName.and.returnValue(createSuccessfulRemoteDataObject$(PaginatedList));
|
||||||
|
const viewLess = fixture.debugElement.query(By.css('.bitstream-collapse'));
|
||||||
|
viewLess.triggerEventHandler('click', null);
|
||||||
|
expect(bitstreamDataService.findAllByItemAndBundleName).toHaveBeenCalled();
|
||||||
|
expect(comp.currentPage).toBe(1);
|
||||||
|
expect(comp.isLastPage).toBeFalse();
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
@@ -1,10 +1,13 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
|
||||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
|
import { filter, takeWhile } from 'rxjs/operators';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { hasNoValue, hasValue } from '../../../../shared/empty.util';
|
||||||
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the file section of the item
|
* This component renders the file section of the item
|
||||||
@@ -22,7 +25,15 @@ export class FileSectionComponent implements OnInit {
|
|||||||
|
|
||||||
separator = '<br/>';
|
separator = '<br/>';
|
||||||
|
|
||||||
bitstreams$: Observable<Bitstream[]>;
|
bitstreams$: BehaviorSubject<Bitstream[]>;
|
||||||
|
|
||||||
|
currentPage: number;
|
||||||
|
|
||||||
|
isLoading: boolean;
|
||||||
|
|
||||||
|
isLastPage: boolean;
|
||||||
|
|
||||||
|
pageSize = 5;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected bitstreamDataService: BitstreamDataService
|
protected bitstreamDataService: BitstreamDataService
|
||||||
@@ -30,13 +41,31 @@ export class FileSectionComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.initialize();
|
this.getNextPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(): void {
|
/**
|
||||||
this.bitstreams$ = this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'ORIGINAL').pipe(
|
* This method will retrieve the next page of Bitstreams from the external BitstreamDataService call.
|
||||||
getFirstSucceededRemoteListPayload()
|
* It'll retrieve the currentPage from the class variables and it'll add the next page of bitstreams with the
|
||||||
);
|
* already existing one.
|
||||||
|
* If the currentPage variable is undefined, we'll set it to 1 and retrieve the first page of Bitstreams
|
||||||
|
*/
|
||||||
|
getNextPage(): void {
|
||||||
|
this.isLoading = true;
|
||||||
|
if (this.currentPage === undefined) {
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.bitstreams$ = new BehaviorSubject([]);
|
||||||
|
} else {
|
||||||
|
this.currentPage++;
|
||||||
|
}
|
||||||
|
this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'ORIGINAL', { currentPage: this.currentPage, elementsPerPage: this.pageSize }).pipe(
|
||||||
|
filter((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => hasValue(bitstreamsRD)),
|
||||||
|
takeWhile((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => hasNoValue(bitstreamsRD.payload) && hasNoValue(bitstreamsRD.error), true)
|
||||||
|
).subscribe((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => {
|
||||||
|
const current: Bitstream[] = this.bitstreams$.getValue();
|
||||||
|
this.bitstreams$.next([...current, ...bitstreamsRD.payload.page]);
|
||||||
|
this.isLoading = false;
|
||||||
|
this.isLastPage = this.currentPage === bitstreamsRD.payload.totalPages;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,11 @@
|
|||||||
|
<div>
|
||||||
|
<div class="modal-header">{{'dso-selector.create.submission.head' | translate}}
|
||||||
|
<button type="button" class="close" (click)="close()" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<ds-collection-dropdown (selectionChange)="selectObject($event.collection)">
|
||||||
|
</ds-collection-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -0,0 +1,164 @@
|
|||||||
|
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CollectionSelectorComponent } from './collection-selector.component';
|
||||||
|
import { CollectionDropdownComponent } from 'src/app/shared/collection-dropdown/collection-dropdown.component';
|
||||||
|
import { Collection } from 'src/app/core/shared/collection.model';
|
||||||
|
import { of, Observable } from 'rxjs';
|
||||||
|
import { RemoteData } from 'src/app/core/data/remote-data';
|
||||||
|
import { Community } from 'src/app/core/shared/community.model';
|
||||||
|
import { FindListOptions } from 'src/app/core/data/request.models';
|
||||||
|
import { FollowLinkConfig } from 'src/app/shared/utils/follow-link-config.model';
|
||||||
|
import { PaginatedList } from 'src/app/core/data/paginated-list';
|
||||||
|
import { createSuccessfulRemoteDataObject } from 'src/app/shared/remote-data.utils';
|
||||||
|
import { PageInfo } from 'src/app/core/shared/page-info.model';
|
||||||
|
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
||||||
|
import { TranslateLoaderMock } from 'src/app/shared/mocks/translate-loader.mock';
|
||||||
|
import { CollectionDataService } from 'src/app/core/data/collection-data.service';
|
||||||
|
import { ChangeDetectorRef, ElementRef, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { hot } from 'jasmine-marbles';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
describe('CollectionSelectorComponent', () => {
|
||||||
|
let component: CollectionSelectorComponent;
|
||||||
|
let fixture: ComponentFixture<CollectionSelectorComponent>;
|
||||||
|
const modal = jasmine.createSpyObj('modal', ['close', 'dismiss']);
|
||||||
|
|
||||||
|
const community: Community = Object.assign(new Community(), {
|
||||||
|
id: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88',
|
||||||
|
uuid: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88',
|
||||||
|
name: 'Community 1'
|
||||||
|
});
|
||||||
|
|
||||||
|
const collections: Collection[] = [
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88',
|
||||||
|
name: 'Collection 1',
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
key: 'dc.title',
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'Community 1-Collection 1'
|
||||||
|
}],
|
||||||
|
parentCommunity: of(
|
||||||
|
new RemoteData(false, false, true, undefined, community, 200)
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: '59ee713b-ee53-4220-8c3f-9860dc84fe33',
|
||||||
|
name: 'Collection 2',
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
key: 'dc.title',
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'Community 1-Collection 2'
|
||||||
|
}],
|
||||||
|
parentCommunity: of(
|
||||||
|
new RemoteData(false, false, true, undefined, community, 200)
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: 'e9dbf393-7127-415f-8919-55be34a6e9ed',
|
||||||
|
name: 'Collection 3',
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
key: 'dc.title',
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'Community 1-Collection 3'
|
||||||
|
}],
|
||||||
|
parentCommunity: of(
|
||||||
|
new RemoteData(false, false, true, undefined, community, 200)
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: '59da2ff0-9bf4-45bf-88be-e35abd33f304',
|
||||||
|
name: 'Collection 4',
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
key: 'dc.title',
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'Community 1-Collection 4'
|
||||||
|
}],
|
||||||
|
parentCommunity: of(
|
||||||
|
new RemoteData(false, false, true, undefined, community, 200)
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
Object.assign(new Collection(), {
|
||||||
|
id: 'a5159760-f362-4659-9e81-e3253ad91ede',
|
||||||
|
name: 'Collection 5',
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
key: 'dc.title',
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'Community 1-Collection 5'
|
||||||
|
}],
|
||||||
|
parentCommunity: of(
|
||||||
|
new RemoteData(false, false, true, undefined, community, 200)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
// tslint:disable-next-line: max-classes-per-file
|
||||||
|
const collectionDataServiceMock = {
|
||||||
|
getAuthorizedCollection(query: string, options: FindListOptions = {}, ...linksToFollow: Array<FollowLinkConfig<Collection>>): Observable<RemoteData<PaginatedList<Collection>>> {
|
||||||
|
return hot( 'a|', {
|
||||||
|
a: createSuccessfulRemoteDataObject(
|
||||||
|
new PaginatedList(new PageInfo(), collections)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: TranslateLoaderMock
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
declarations: [ CollectionSelectorComponent, CollectionDropdownComponent ],
|
||||||
|
providers: [
|
||||||
|
{provide: CollectionDataService, useValue: collectionDataServiceMock},
|
||||||
|
{provide: ChangeDetectorRef, useValue: {}},
|
||||||
|
{provide: ElementRef, userValue: {}},
|
||||||
|
{provide: NgbActiveModal, useValue: modal},
|
||||||
|
{provide: ActivatedRoute, useValue: {}}
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CollectionSelectorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call selectObject', fakeAsync(() => {
|
||||||
|
spyOn(component, 'selectObject');
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
const collectionItem = fixture.debugElement.query(By.css('.collection-item:nth-child(2)'));
|
||||||
|
collectionItem.triggerEventHandler('click', {
|
||||||
|
preventDefault: () => {/**/
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(component.selectObject).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should close the dialog', () => {
|
||||||
|
component.close();
|
||||||
|
expect((component as any).activeModal.close).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,33 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component displays the dialog that shows the list of selectable collections
|
||||||
|
* on the MyDSpace page
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-collection-selector',
|
||||||
|
templateUrl: './collection-selector.component.html',
|
||||||
|
styleUrls: ['./collection-selector.component.scss']
|
||||||
|
})
|
||||||
|
export class CollectionSelectorComponent {
|
||||||
|
|
||||||
|
constructor(protected activeModal: NgbActiveModal) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called when an element has been selected from collection list.
|
||||||
|
* Its close the active modal and send selected value to the component container
|
||||||
|
* @param dso The selected DSpaceObject
|
||||||
|
*/
|
||||||
|
selectObject(dso: DSpaceObject) {
|
||||||
|
this.activeModal.close(dso);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the modal
|
||||||
|
*/
|
||||||
|
close() {
|
||||||
|
this.activeModal.close();
|
||||||
|
}
|
||||||
|
}
|
@@ -3,13 +3,19 @@
|
|||||||
<ds-uploader *ngIf="uploadFilesOptions.url !== ''"
|
<ds-uploader *ngIf="uploadFilesOptions.url !== ''"
|
||||||
[uploadFilesOptions]="uploadFilesOptions"
|
[uploadFilesOptions]="uploadFilesOptions"
|
||||||
(onCompleteItem)="onCompleteItem($event)"
|
(onCompleteItem)="onCompleteItem($event)"
|
||||||
(onUploadError)="onUploadError()"></ds-uploader>
|
(onUploadError)="onUploadError($event)"
|
||||||
|
(onFileSelected)="afterFileLoaded($event)"></ds-uploader>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="add">
|
<div class="add">
|
||||||
<button class="btn btn-lg btn-primary mt-1 ml-2" (click)="openDialog()" role="button">
|
<button class="btn btn-lg btn-primary mt-1 ml-2" (click)="openDialog()" role="button" title="{{'mydspace.new-submission' | translate}}">
|
||||||
<i class="fa fa-plus-circle" aria-hidden="true"></i> {{'mydspace.new-submission' | translate}}
|
<i class="fa fa-plus-circle" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="add">
|
||||||
|
<a class="btn btn-lg btn-primary mt-1 ml-2" [routerLink]="['/import-external']" role="button" title="{{'mydspace.new-submission-external' | translate}}">
|
||||||
|
<i class="fa fa-file-import" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectorRef, Component, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectorRef, Component, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { async, ComponentFixture, inject, TestBed, tick, fakeAsync } from '@angular/core/testing';
|
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
@@ -15,7 +15,6 @@ import { createTestComponent } from '../../shared/testing/utils.test';
|
|||||||
import { MyDSpaceNewSubmissionComponent } from './my-dspace-new-submission.component';
|
import { MyDSpaceNewSubmissionComponent } from './my-dspace-new-submission.component';
|
||||||
import { AppState } from '../../app.reducer';
|
import { AppState } from '../../app.reducer';
|
||||||
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
|
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
|
||||||
import { getMockTranslateService } from '../../shared/mocks/translate.service.mock';
|
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
|
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
@@ -23,10 +22,27 @@ import { getMockScrollToService } from '../../shared/mocks/scroll-to-service.moc
|
|||||||
import { UploaderService } from '../../shared/uploader/uploader.service';
|
import { UploaderService } from '../../shared/uploader/uploader.service';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { HostWindowService } from '../../shared/host-window.service';
|
||||||
|
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
|
||||||
|
import { UploaderComponent } from 'src/app/shared/uploader/uploader.component';
|
||||||
|
|
||||||
describe('MyDSpaceNewSubmissionComponent test', () => {
|
describe('MyDSpaceNewSubmissionComponent test', () => {
|
||||||
|
|
||||||
const translateService: any = getMockTranslateService();
|
const translateService: TranslateService = jasmine.createSpyObj('translateService', {
|
||||||
|
get: (key: string): any => { observableOf(key) },
|
||||||
|
instant: jasmine.createSpy('instant')
|
||||||
|
});
|
||||||
|
|
||||||
|
const uploader: any = jasmine.createSpyObj('uploader', {
|
||||||
|
clearQueue: jasmine.createSpy('clearQueue')
|
||||||
|
});
|
||||||
|
|
||||||
|
const modalService = {
|
||||||
|
open: () => {
|
||||||
|
return { result: new Promise((res, rej) => {/****/}) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const store: Store<AppState> = jasmine.createSpyObj('store', {
|
const store: Store<AppState> = jasmine.createSpyObj('store', {
|
||||||
/* tslint:disable:no-empty */
|
/* tslint:disable:no-empty */
|
||||||
dispatch: {},
|
dispatch: {},
|
||||||
@@ -56,14 +72,11 @@ describe('MyDSpaceNewSubmissionComponent test', () => {
|
|||||||
{ provide: ScrollToService, useValue: getMockScrollToService() },
|
{ provide: ScrollToService, useValue: getMockScrollToService() },
|
||||||
{ provide: Store, useValue: store },
|
{ provide: Store, useValue: store },
|
||||||
{ provide: TranslateService, useValue: translateService },
|
{ provide: TranslateService, useValue: translateService },
|
||||||
{
|
{ provide: NgbModal, useValue: modalService },
|
||||||
provide: NgbModal, useValue: {
|
|
||||||
open: () => {/*comment*/}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
MyDSpaceNewSubmissionComponent,
|
MyDSpaceNewSubmissionComponent,
|
||||||
UploaderService
|
UploaderService,
|
||||||
|
{ provide: HostWindowService, useValue: new HostWindowServiceStub(800) },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -100,6 +113,10 @@ describe('MyDSpaceNewSubmissionComponent test', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(MyDSpaceNewSubmissionComponent);
|
fixture = TestBed.createComponent(MyDSpaceNewSubmissionComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
|
comp.uploadFilesOptions.authToken = 'user-auth-token';
|
||||||
|
comp.uploadFilesOptions.url = 'https://fake.upload-api.url';
|
||||||
|
comp.uploaderComponent = TestBed.createComponent(UploaderComponent).componentInstance;
|
||||||
|
comp.uploaderComponent.uploader = uploader;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call app.openDialog', () => {
|
it('should call app.openDialog', () => {
|
||||||
@@ -111,6 +128,12 @@ describe('MyDSpaceNewSubmissionComponent test', () => {
|
|||||||
});
|
});
|
||||||
expect(comp.openDialog).toHaveBeenCalled();
|
expect(comp.openDialog).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should show a collection selector if only one file are uploaded', () => {
|
||||||
|
spyOn((comp as any).modalService, 'open').and.returnValue({ result: new Promise((res, rej) => {/****/}) });
|
||||||
|
comp.afterFileLoaded(['']);
|
||||||
|
expect((comp as any).modalService.open).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
|
import { ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
|
||||||
|
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { first } from 'rxjs/operators';
|
import { first } from 'rxjs/operators';
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
import { SubmissionState } from '../../submission/submission.reducers';
|
|
||||||
import { AuthService } from '../../core/auth/auth.service';
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
@@ -15,9 +14,10 @@ import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
|||||||
import { NotificationType } from '../../shared/notifications/models/notification-type';
|
import { NotificationType } from '../../shared/notifications/models/notification-type';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import { SearchResult } from '../../shared/search/search-result.model';
|
import { SearchResult } from '../../shared/search/search-result.model';
|
||||||
import { Router } from '@angular/router';
|
import { CreateItemParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { CollectionSelectorComponent } from '../collection-selector/collection-selector.component';
|
||||||
import { CreateItemParentSelectorComponent } from 'src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component';
|
import { UploaderComponent } from '../../shared/uploader/uploader.component';
|
||||||
|
import { UploaderError } from '../../shared/uploader/uploader-error.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component represents the whole mydspace page header
|
* This component represents the whole mydspace page header
|
||||||
@@ -43,6 +43,11 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
|
|||||||
*/
|
*/
|
||||||
private sub: Subscription;
|
private sub: Subscription;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to uploaderComponent
|
||||||
|
*/
|
||||||
|
@ViewChild(UploaderComponent, { static: false }) uploaderComponent: UploaderComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize instance variables
|
* Initialize instance variables
|
||||||
*
|
*
|
||||||
@@ -52,14 +57,14 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
|
|||||||
* @param {NotificationsService} notificationsService
|
* @param {NotificationsService} notificationsService
|
||||||
* @param {Store<SubmissionState>} store
|
* @param {Store<SubmissionState>} store
|
||||||
* @param {TranslateService} translate
|
* @param {TranslateService} translate
|
||||||
|
* @param {Router} router
|
||||||
|
* @param {NgbModal} modalService
|
||||||
*/
|
*/
|
||||||
constructor(private authService: AuthService,
|
constructor(private authService: AuthService,
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private halService: HALEndpointService,
|
private halService: HALEndpointService,
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
private store: Store<SubmissionState>,
|
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private router: Router,
|
|
||||||
private modalService: NgbModal) {
|
private modalService: NgbModal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +72,7 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
|
|||||||
* Initialize url and Bearer token
|
* Initialize url and Bearer token
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.uploadFilesOptions.autoUpload = false;
|
||||||
this.sub = this.halService.getEndpoint('workspaceitems').pipe(first()).subscribe((url) => {
|
this.sub = this.halService.getEndpoint('workspaceitems').pipe(first()).subscribe((url) => {
|
||||||
this.uploadFilesOptions.url = url;
|
this.uploadFilesOptions.url = url;
|
||||||
this.uploadFilesOptions.authToken = this.authService.buildAuthHeader();
|
this.uploadFilesOptions.authToken = this.authService.buildAuthHeader();
|
||||||
@@ -106,8 +112,12 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
|
|||||||
/**
|
/**
|
||||||
* Method called on file upload error
|
* Method called on file upload error
|
||||||
*/
|
*/
|
||||||
public onUploadError() {
|
public onUploadError(error: UploaderError) {
|
||||||
this.notificationsService.error(null, this.translate.get('mydspace.upload.upload-failed'));
|
let errorMessageKey = 'mydspace.upload.upload-failed';
|
||||||
|
if (hasValue(error.status) && error.status === 422) {
|
||||||
|
errorMessageKey = 'mydspace.upload.upload-failed-manyentries';
|
||||||
|
}
|
||||||
|
this.notificationsService.error(null, this.translate.get(errorMessageKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,6 +128,28 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
|
|||||||
this.modalService.open(CreateItemParentSelectorComponent);
|
this.modalService.open(CreateItemParentSelectorComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method invoked after all file are loaded from upload plugin
|
||||||
|
*/
|
||||||
|
afterFileLoaded(items) {
|
||||||
|
const uploader = this.uploaderComponent.uploader;
|
||||||
|
if (hasValue(items) && items.length > 1) {
|
||||||
|
this.notificationsService.error(null, this.translate.get('mydspace.upload.upload-failed-moreonefile'));
|
||||||
|
uploader.clearQueue();
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
} else {
|
||||||
|
const modalRef = this.modalService.open(CollectionSelectorComponent);
|
||||||
|
// When the dialog are closes its takes the collection selected and
|
||||||
|
// uploads choosed file after adds owningCollection parameter
|
||||||
|
modalRef.result.then( (result) => {
|
||||||
|
uploader.onBuildItemForm = (fileItem: any, form: any) => {
|
||||||
|
form.append('owningCollection', result.uuid);
|
||||||
|
};
|
||||||
|
uploader.uploadAll();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsubscribe from the subscription
|
* Unsubscribe from the subscription
|
||||||
*/
|
*/
|
||||||
|
@@ -20,6 +20,7 @@ import { SearchResultListElementComponent } from '../shared/object-list/search-r
|
|||||||
import { ItemSearchResultListElementSubmissionComponent } from '../shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component';
|
import { ItemSearchResultListElementSubmissionComponent } from '../shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component';
|
||||||
import { WorkflowItemSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component';
|
import { WorkflowItemSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component';
|
||||||
import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component';
|
import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component';
|
||||||
|
import { CollectionSelectorComponent } from './collection-selector/collection-selector.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -40,7 +41,8 @@ import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/
|
|||||||
ClaimedTaskSearchResultDetailElementComponent,
|
ClaimedTaskSearchResultDetailElementComponent,
|
||||||
PoolSearchResultDetailElementComponent,
|
PoolSearchResultDetailElementComponent,
|
||||||
MyDSpaceNewSubmissionComponent,
|
MyDSpaceNewSubmissionComponent,
|
||||||
ItemSearchResultListElementSubmissionComponent
|
ItemSearchResultListElementSubmissionComponent,
|
||||||
|
CollectionSelectorComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
MyDSpaceGuard,
|
MyDSpaceGuard,
|
||||||
@@ -57,7 +59,8 @@ import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/
|
|||||||
WorkflowItemSearchResultDetailElementComponent,
|
WorkflowItemSearchResultDetailElementComponent,
|
||||||
ClaimedTaskSearchResultDetailElementComponent,
|
ClaimedTaskSearchResultDetailElementComponent,
|
||||||
PoolSearchResultDetailElementComponent,
|
PoolSearchResultDetailElementComponent,
|
||||||
ItemSearchResultListElementSubmissionComponent
|
ItemSearchResultListElementSubmissionComponent,
|
||||||
|
CollectionSelectorComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -0,0 +1,22 @@
|
|||||||
|
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
||||||
|
import { getWorkflowItemModuleRoute } from '../app-routing-paths';
|
||||||
|
|
||||||
|
export function getWorkflowItemPageRoute(wfiId: string) {
|
||||||
|
return new URLCombiner(getWorkflowItemModuleRoute(), wfiId).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWorkflowItemEditRoute(wfiId: string) {
|
||||||
|
return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_ITEM_EDIT_PATH).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWorkflowItemDeleteRoute(wfiId: string) {
|
||||||
|
return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_ITEM_DELETE_PATH).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWorkflowItemSendBackRoute(wfiId: string) {
|
||||||
|
return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_ITEM_SEND_BACK_PATH).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WORKFLOW_ITEM_EDIT_PATH = 'edit';
|
||||||
|
export const WORKFLOW_ITEM_DELETE_PATH = 'delete';
|
||||||
|
export const WORKFLOW_ITEM_SEND_BACK_PATH = 'sendback';
|
@@ -3,31 +3,14 @@ import { RouterModule } from '@angular/router';
|
|||||||
|
|
||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { SubmissionEditComponent } from '../submission/edit/submission-edit.component';
|
import { SubmissionEditComponent } from '../submission/edit/submission-edit.component';
|
||||||
import { URLCombiner } from '../core/url-combiner/url-combiner';
|
|
||||||
import { getWorkflowItemModulePath } from '../app-routing.module';
|
|
||||||
import { WorkflowItemDeleteComponent } from './workflow-item-delete/workflow-item-delete.component';
|
import { WorkflowItemDeleteComponent } from './workflow-item-delete/workflow-item-delete.component';
|
||||||
import { WorkflowItemPageResolver } from './workflow-item-page.resolver';
|
import { WorkflowItemPageResolver } from './workflow-item-page.resolver';
|
||||||
import { WorkflowItemSendBackComponent } from './workflow-item-send-back/workflow-item-send-back.component';
|
import { WorkflowItemSendBackComponent } from './workflow-item-send-back/workflow-item-send-back.component';
|
||||||
|
import {
|
||||||
export function getWorkflowItemPageRoute(wfiId: string) {
|
WORKFLOW_ITEM_SEND_BACK_PATH,
|
||||||
return new URLCombiner(getWorkflowItemModulePath(), wfiId).toString();
|
WORKFLOW_ITEM_DELETE_PATH,
|
||||||
}
|
WORKFLOW_ITEM_EDIT_PATH
|
||||||
|
} from './workflowitems-edit-page-routing-paths';
|
||||||
export function getWorkflowItemEditPath(wfiId: string) {
|
|
||||||
return new URLCombiner(getWorkflowItemModulePath(), wfiId, WORKFLOW_ITEM_EDIT_PATH).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getWorkflowItemDeletePath(wfiId: string) {
|
|
||||||
return new URLCombiner(getWorkflowItemModulePath(), wfiId, WORKFLOW_ITEM_DELETE_PATH).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getWorkflowItemSendBackPath(wfiId: string) {
|
|
||||||
return new URLCombiner(getWorkflowItemModulePath(), wfiId, WORKFLOW_ITEM_SEND_BACK_PATH).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
const WORKFLOW_ITEM_EDIT_PATH = 'edit';
|
|
||||||
const WORKFLOW_ITEM_DELETE_PATH = 'delete';
|
|
||||||
const WORKFLOW_ITEM_SEND_BACK_PATH = 'sendback';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
67
src/app/app-routing-paths.ts
Normal file
67
src/app/app-routing-paths.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { DSpaceObject } from './core/shared/dspace-object.model';
|
||||||
|
import { Community } from './core/shared/community.model';
|
||||||
|
import { Collection } from './core/shared/collection.model';
|
||||||
|
import { Item } from './core/shared/item.model';
|
||||||
|
import { getCommunityPageRoute } from './+community-page/community-page-routing-paths';
|
||||||
|
import { getCollectionPageRoute } from './+collection-page/collection-page-routing-paths';
|
||||||
|
import { getItemPageRoute } from './+item-page/item-page-routing-paths';
|
||||||
|
|
||||||
|
export const BITSTREAM_MODULE_PATH = 'bitstreams';
|
||||||
|
|
||||||
|
export function getBitstreamModuleRoute() {
|
||||||
|
return `/${BITSTREAM_MODULE_PATH}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ADMIN_MODULE_PATH = 'admin';
|
||||||
|
|
||||||
|
export function getAdminModuleRoute() {
|
||||||
|
return `/${ADMIN_MODULE_PATH}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PROFILE_MODULE_PATH = 'profile';
|
||||||
|
|
||||||
|
export function getProfileModuleRoute() {
|
||||||
|
return `/${PROFILE_MODULE_PATH}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const REGISTER_PATH = 'register';
|
||||||
|
|
||||||
|
export function getRegisterRoute() {
|
||||||
|
return `/${REGISTER_PATH}`;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FORGOT_PASSWORD_PATH = 'forgot';
|
||||||
|
|
||||||
|
export function getForgotPasswordRoute() {
|
||||||
|
return `/${FORGOT_PASSWORD_PATH}`;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WORKFLOW_ITEM_MODULE_PATH = 'workflowitems';
|
||||||
|
|
||||||
|
export function getWorkflowItemModuleRoute() {
|
||||||
|
return `/${WORKFLOW_ITEM_MODULE_PATH}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDSORoute(dso: DSpaceObject): string {
|
||||||
|
switch ((dso as any).type) {
|
||||||
|
case Community.type.value:
|
||||||
|
return getCommunityPageRoute(dso.uuid);
|
||||||
|
case Collection.type.value:
|
||||||
|
return getCollectionPageRoute(dso.uuid);
|
||||||
|
case Item.type.value:
|
||||||
|
return getItemPageRoute(dso.uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UNAUTHORIZED_PATH = 'unauthorized';
|
||||||
|
|
||||||
|
export function getUnauthorizedRoute() {
|
||||||
|
return `/${UNAUTHORIZED_PATH}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const INFO_MODULE_PATH = 'info';
|
||||||
|
export function getInfoModulePath() {
|
||||||
|
return `/${INFO_MODULE_PATH}`;
|
||||||
|
}
|
@@ -1,132 +1,77 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { AuthBlockingGuard } from './core/auth/auth-blocking.guard';
|
||||||
|
|
||||||
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
||||||
import { AuthenticatedGuard } from './core/auth/authenticated.guard';
|
import { AuthenticatedGuard } from './core/auth/authenticated.guard';
|
||||||
import { DSpaceObject } from './core/shared/dspace-object.model';
|
|
||||||
import { Community } from './core/shared/community.model';
|
|
||||||
import { getCommunityPageRoute } from './+community-page/community-page-routing.module';
|
|
||||||
import { Collection } from './core/shared/collection.model';
|
|
||||||
import { Item } from './core/shared/item.model';
|
|
||||||
import { getItemPageRoute } from './+item-page/item-page-routing.module';
|
|
||||||
import { getCollectionPageRoute } from './+collection-page/collection-page-routing.module';
|
|
||||||
import { SiteAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
|
import { SiteAdministratorGuard } from './core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
|
||||||
import { UnauthorizedComponent } from './unauthorized/unauthorized.component';
|
import { UnauthorizedComponent } from './unauthorized/unauthorized.component';
|
||||||
|
import {
|
||||||
|
UNAUTHORIZED_PATH,
|
||||||
|
WORKFLOW_ITEM_MODULE_PATH,
|
||||||
|
FORGOT_PASSWORD_PATH,
|
||||||
|
REGISTER_PATH,
|
||||||
|
PROFILE_MODULE_PATH,
|
||||||
|
ADMIN_MODULE_PATH,
|
||||||
|
BITSTREAM_MODULE_PATH,
|
||||||
|
INFO_MODULE_PATH
|
||||||
|
} from './app-routing-paths';
|
||||||
|
import { COLLECTION_MODULE_PATH } from './+collection-page/collection-page-routing-paths';
|
||||||
|
import { COMMUNITY_MODULE_PATH } from './+community-page/community-page-routing-paths';
|
||||||
|
import { ITEM_MODULE_PATH } from './+item-page/item-page-routing-paths';
|
||||||
|
import { ReloadGuard } from './core/reload/reload.guard';
|
||||||
|
import { EndUserAgreementCurrentUserGuard } from './core/end-user-agreement/end-user-agreement-current-user.guard';
|
||||||
import { SiteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard';
|
import { SiteRegisterGuard } from './core/data/feature-authorization/feature-authorization-guard/site-register.guard';
|
||||||
|
|
||||||
const ITEM_MODULE_PATH = 'items';
|
|
||||||
|
|
||||||
export function getItemModulePath() {
|
|
||||||
return `/${ITEM_MODULE_PATH}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const COLLECTION_MODULE_PATH = 'collections';
|
|
||||||
|
|
||||||
export function getCollectionModulePath() {
|
|
||||||
return `/${COLLECTION_MODULE_PATH}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const COMMUNITY_MODULE_PATH = 'communities';
|
|
||||||
|
|
||||||
export function getCommunityModulePath() {
|
|
||||||
return `/${COMMUNITY_MODULE_PATH}`;
|
|
||||||
}
|
|
||||||
const BITSTREAM_MODULE_PATH = 'bitstreams';
|
|
||||||
export function getBitstreamModulePath() {
|
|
||||||
return `/${BITSTREAM_MODULE_PATH}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ADMIN_MODULE_PATH = 'admin';
|
|
||||||
|
|
||||||
export function getAdminModulePath() {
|
|
||||||
return `/${ADMIN_MODULE_PATH}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PROFILE_MODULE_PATH = 'profile';
|
|
||||||
|
|
||||||
export function getProfileModulePath() {
|
|
||||||
return `/${PROFILE_MODULE_PATH}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const REGISTER_PATH = 'register';
|
|
||||||
|
|
||||||
export function getRegisterPath() {
|
|
||||||
return `/${REGISTER_PATH}`;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const FORGOT_PASSWORD_PATH = 'forgot';
|
|
||||||
|
|
||||||
export function getForgotPasswordPath() {
|
|
||||||
return `/${FORGOT_PASSWORD_PATH}`;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const WORKFLOW_ITEM_MODULE_PATH = 'workflowitems';
|
|
||||||
|
|
||||||
export function getWorkflowItemModulePath() {
|
|
||||||
return `/${WORKFLOW_ITEM_MODULE_PATH}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDSOPath(dso: DSpaceObject): string {
|
|
||||||
switch ((dso as any).type) {
|
|
||||||
case Community.type.value:
|
|
||||||
return getCommunityPageRoute(dso.uuid);
|
|
||||||
case Collection.type.value:
|
|
||||||
return getCollectionPageRoute(dso.uuid);
|
|
||||||
case Item.type.value:
|
|
||||||
return getItemPageRoute(dso.uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const UNAUTHORIZED_PATH = 'unauthorized';
|
|
||||||
|
|
||||||
export function getUnauthorizedPath() {
|
|
||||||
return `/${UNAUTHORIZED_PATH}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forRoot([
|
RouterModule.forRoot([
|
||||||
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
{ path: '', canActivate: [AuthBlockingGuard],
|
||||||
{ path: 'reload/:rnd', redirectTo: '/home', pathMatch: 'full' },
|
children: [
|
||||||
{ path: 'home', loadChildren: './+home-page/home-page.module#HomePageModule', data: { showBreadcrumbs: false } },
|
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
||||||
{ path: 'community-list', loadChildren: './community-list-page/community-list-page.module#CommunityListPageModule' },
|
{ path: 'reload/:rnd', component: PageNotFoundComponent, pathMatch: 'full', canActivate: [ReloadGuard] },
|
||||||
{ path: 'id', loadChildren: './+lookup-by-id/lookup-by-id.module#LookupIdModule' },
|
{ path: 'home', loadChildren: './+home-page/home-page.module#HomePageModule', data: { showBreadcrumbs: false }, canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
{ path: 'handle', loadChildren: './+lookup-by-id/lookup-by-id.module#LookupIdModule' },
|
{ path: 'community-list', loadChildren: './community-list-page/community-list-page.module#CommunityListPageModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
{ path: REGISTER_PATH, loadChildren: './register-page/register-page.module#RegisterPageModule', canActivate: [SiteRegisterGuard] },
|
{ path: 'id', loadChildren: './+lookup-by-id/lookup-by-id.module#LookupIdModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
{ path: FORGOT_PASSWORD_PATH, loadChildren: './forgot-password/forgot-password.module#ForgotPasswordModule' },
|
{ path: 'handle', loadChildren: './+lookup-by-id/lookup-by-id.module#LookupIdModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
{ path: COMMUNITY_MODULE_PATH, loadChildren: './+community-page/community-page.module#CommunityPageModule' },
|
{ path: REGISTER_PATH, loadChildren: './register-page/register-page.module#RegisterPageModule', canActivate: [SiteRegisterGuard] },
|
||||||
{ path: COLLECTION_MODULE_PATH, loadChildren: './+collection-page/collection-page.module#CollectionPageModule' },
|
{ path: FORGOT_PASSWORD_PATH, loadChildren: './forgot-password/forgot-password.module#ForgotPasswordModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
{ path: ITEM_MODULE_PATH, loadChildren: './+item-page/item-page.module#ItemPageModule' },
|
{ path: COMMUNITY_MODULE_PATH, loadChildren: './+community-page/community-page.module#CommunityPageModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
{ path: BITSTREAM_MODULE_PATH, loadChildren: './+bitstream-page/bitstream-page.module#BitstreamPageModule' },
|
{ path: COLLECTION_MODULE_PATH, loadChildren: './+collection-page/collection-page.module#CollectionPageModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
{
|
{ path: ITEM_MODULE_PATH, loadChildren: './+item-page/item-page.module#ItemPageModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
path: 'mydspace',
|
{ path: BITSTREAM_MODULE_PATH, loadChildren: './+bitstream-page/bitstream-page.module#BitstreamPageModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
loadChildren: './+my-dspace-page/my-dspace-page.module#MyDSpacePageModule',
|
{
|
||||||
canActivate: [AuthenticatedGuard]
|
path: 'mydspace',
|
||||||
},
|
loadChildren: './+my-dspace-page/my-dspace-page.module#MyDSpacePageModule',
|
||||||
{ path: 'search', loadChildren: './+search-page/search-page-routing.module#SearchPageRoutingModule' },
|
canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard]
|
||||||
{ path: 'browse', loadChildren: './+browse-by/browse-by.module#BrowseByModule'},
|
},
|
||||||
{ path: ADMIN_MODULE_PATH, loadChildren: './+admin/admin.module#AdminModule', canActivate: [SiteAdministratorGuard] },
|
{ path: 'search', loadChildren: './+search-page/search-page-routing.module#SearchPageRoutingModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
{ path: 'login', loadChildren: './+login-page/login-page.module#LoginPageModule' },
|
{ path: 'browse', loadChildren: './+browse-by/browse-by.module#BrowseByModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
{ path: 'logout', loadChildren: './+logout-page/logout-page.module#LogoutPageModule' },
|
{ path: ADMIN_MODULE_PATH, loadChildren: './+admin/admin.module#AdminModule', canActivate: [SiteAdministratorGuard, EndUserAgreementCurrentUserGuard] },
|
||||||
{ path: 'submit', loadChildren: './+submit-page/submit-page.module#SubmitPageModule' },
|
{ path: 'login', loadChildren: './+login-page/login-page.module#LoginPageModule' },
|
||||||
{
|
{ path: 'logout', loadChildren: './+logout-page/logout-page.module#LogoutPageModule' },
|
||||||
path: 'workspaceitems',
|
{ path: 'submit', loadChildren: './+submit-page/submit-page.module#SubmitPageModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
loadChildren: './+workspaceitems-edit-page/workspaceitems-edit-page.module#WorkspaceitemsEditPageModule'
|
{ path: 'import-external', loadChildren: './+import-external-page/import-external-page.module#ImportExternalPageModule', canActivate: [EndUserAgreementCurrentUserGuard] },
|
||||||
},
|
{
|
||||||
{
|
path: 'workspaceitems',
|
||||||
path: WORKFLOW_ITEM_MODULE_PATH,
|
loadChildren: './+workspaceitems-edit-page/workspaceitems-edit-page.module#WorkspaceitemsEditPageModule',
|
||||||
loadChildren: './+workflowitems-edit-page/workflowitems-edit-page.module#WorkflowItemsEditPageModule'
|
canActivate: [EndUserAgreementCurrentUserGuard]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: PROFILE_MODULE_PATH,
|
path: WORKFLOW_ITEM_MODULE_PATH,
|
||||||
loadChildren: './profile-page/profile-page.module#ProfilePageModule', canActivate: [AuthenticatedGuard]
|
loadChildren: './+workflowitems-edit-page/workflowitems-edit-page.module#WorkflowItemsEditPageModule',
|
||||||
},
|
canActivate: [EndUserAgreementCurrentUserGuard]
|
||||||
{ path: 'processes', loadChildren: './process-page/process-page.module#ProcessPageModule', canActivate: [AuthenticatedGuard] },
|
},
|
||||||
{ path: UNAUTHORIZED_PATH, component: UnauthorizedComponent },
|
{
|
||||||
{ path: '**', pathMatch: 'full', component: PageNotFoundComponent },
|
path: PROFILE_MODULE_PATH,
|
||||||
],
|
loadChildren: './profile-page/profile-page.module#ProfilePageModule', canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard]
|
||||||
|
},
|
||||||
|
{ path: 'processes', loadChildren: './process-page/process-page.module#ProcessPageModule', canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] },
|
||||||
|
{ path: INFO_MODULE_PATH, loadChildren: './info/info.module#InfoModule' },
|
||||||
|
{ path: UNAUTHORIZED_PATH, component: UnauthorizedComponent },
|
||||||
|
{ path: '**', pathMatch: 'full', component: PageNotFoundComponent },
|
||||||
|
]}
|
||||||
|
],
|
||||||
{
|
{
|
||||||
onSameUrlNavigation: 'reload',
|
onSameUrlNavigation: 'reload',
|
||||||
})
|
})
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div class="outer-wrapper">
|
<div class="outer-wrapper" *ngIf="isNotAuthBlocking$ | async; else authLoader">
|
||||||
<ds-admin-sidebar></ds-admin-sidebar>
|
<ds-admin-sidebar></ds-admin-sidebar>
|
||||||
<div class="inner-wrapper" [@slideSidebarPadding]="{
|
<div class="inner-wrapper" [@slideSidebarPadding]="{
|
||||||
value: (!(sidebarVisible | async) ? 'hidden' : (slideSidebarOver | async) ? 'shown' : 'expanded'),
|
value: (!(sidebarVisible | async) ? 'hidden' : (slideSidebarOver | async) ? 'shown' : 'expanded'),
|
||||||
@@ -23,3 +23,8 @@
|
|||||||
<ds-footer></ds-footer>
|
<ds-footer></ds-footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ng-template #authLoader>
|
||||||
|
<div class="text-center ds-full-screen-loader d-flex align-items-center flex-column justify-content-center">
|
||||||
|
<ds-loading [showMessage]="false"></ds-loading>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
@@ -47,3 +47,7 @@ ds-admin-sidebar {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: $sidebar-z-index;
|
z-index: $sidebar-z-index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ds-full-screen-loader {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
@@ -1,9 +1,8 @@
|
|||||||
|
import * as ngrx from '@ngrx/store';
|
||||||
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
import { Store, StoreModule } from '@ngrx/store';
|
import { Store, StoreModule } from '@ngrx/store';
|
||||||
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
||||||
@@ -32,11 +31,11 @@ import { RouterMock } from './shared/mocks/router.mock';
|
|||||||
import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';
|
import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';
|
||||||
import { storeModuleConfig } from './app.reducer';
|
import { storeModuleConfig } from './app.reducer';
|
||||||
import { LocaleService } from './core/locale/locale.service';
|
import { LocaleService } from './core/locale/locale.service';
|
||||||
|
import { authReducer } from './core/auth/auth.reducer';
|
||||||
|
import { cold } from 'jasmine-marbles';
|
||||||
|
|
||||||
let comp: AppComponent;
|
let comp: AppComponent;
|
||||||
let fixture: ComponentFixture<AppComponent>;
|
let fixture: ComponentFixture<AppComponent>;
|
||||||
let de: DebugElement;
|
|
||||||
let el: HTMLElement;
|
|
||||||
const menuService = new MenuServiceStub();
|
const menuService = new MenuServiceStub();
|
||||||
|
|
||||||
describe('App component', () => {
|
describe('App component', () => {
|
||||||
@@ -52,7 +51,7 @@ describe('App component', () => {
|
|||||||
return TestBed.configureTestingModule({
|
return TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
StoreModule.forRoot({}, storeModuleConfig),
|
StoreModule.forRoot(authReducer, storeModuleConfig),
|
||||||
TranslateModule.forRoot({
|
TranslateModule.forRoot({
|
||||||
loader: {
|
loader: {
|
||||||
provide: TranslateLoader,
|
provide: TranslateLoader,
|
||||||
@@ -82,12 +81,19 @@ describe('App component', () => {
|
|||||||
|
|
||||||
// synchronous beforeEach
|
// synchronous beforeEach
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(AppComponent);
|
spyOnProperty(ngrx, 'select').and.callFake(() => {
|
||||||
|
return () => {
|
||||||
|
return () => cold('a', {
|
||||||
|
a: {
|
||||||
|
core: { auth: { loading: false } }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(AppComponent);
|
||||||
comp = fixture.componentInstance; // component test instance
|
comp = fixture.componentInstance; // component test instance
|
||||||
// query for the <div class='outer-wrapper'> by CSS element selector
|
fixture.detectChanges();
|
||||||
de = fixture.debugElement.query(By.css('div.outer-wrapper'));
|
|
||||||
el = de.nativeElement;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create component', inject([AppComponent], (app: AppComponent) => {
|
it('should create component', inject([AppComponent], (app: AppComponent) => {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { delay, filter, map, take } from 'rxjs/operators';
|
import { delay, map, distinctUntilChanged } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
@@ -19,9 +19,8 @@ import { MetadataService } from './core/metadata/metadata.service';
|
|||||||
import { HostWindowResizeAction } from './shared/host-window.actions';
|
import { HostWindowResizeAction } from './shared/host-window.actions';
|
||||||
import { HostWindowState } from './shared/search/host-window.reducer';
|
import { HostWindowState } from './shared/search/host-window.reducer';
|
||||||
import { NativeWindowRef, NativeWindowService } from './core/services/window.service';
|
import { NativeWindowRef, NativeWindowService } from './core/services/window.service';
|
||||||
import { isAuthenticated } from './core/auth/selectors';
|
import { isAuthenticationBlocking } from './core/auth/selectors';
|
||||||
import { AuthService } from './core/auth/auth.service';
|
import { AuthService } from './core/auth/auth.service';
|
||||||
import variables from '../styles/_exposed_variables.scss';
|
|
||||||
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
||||||
import { MenuService } from './shared/menu/menu.service';
|
import { MenuService } from './shared/menu/menu.service';
|
||||||
import { MenuID } from './shared/menu/initial-menus-state';
|
import { MenuID } from './shared/menu/initial-menus-state';
|
||||||
@@ -53,6 +52,11 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
notificationOptions = environment.notifications;
|
notificationOptions = environment.notifications;
|
||||||
models;
|
models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the authentication is currently blocking the UI
|
||||||
|
*/
|
||||||
|
isNotAuthBlocking$: Observable<boolean>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(NativeWindowService) private _window: NativeWindowRef,
|
@Inject(NativeWindowService) private _window: NativeWindowRef,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
@@ -90,16 +94,15 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.isNotAuthBlocking$ = this.store.pipe(select(isAuthenticationBlocking)).pipe(
|
||||||
|
map((isBlocking: boolean) => isBlocking === false),
|
||||||
|
distinctUntilChanged()
|
||||||
|
);
|
||||||
const env: string = environment.production ? 'Production' : 'Development';
|
const env: string = environment.production ? 'Production' : 'Development';
|
||||||
const color: string = environment.production ? 'red' : 'green';
|
const color: string = environment.production ? 'red' : 'green';
|
||||||
console.info(`Environment: %c${env}`, `color: ${color}; font-weight: bold;`);
|
console.info(`Environment: %c${env}`, `color: ${color}; font-weight: bold;`);
|
||||||
this.dispatchWindowSize(this._window.nativeWindow.innerWidth, this._window.nativeWindow.innerHeight);
|
this.dispatchWindowSize(this._window.nativeWindow.innerWidth, this._window.nativeWindow.innerHeight);
|
||||||
|
|
||||||
// Whether is not authenticathed try to retrieve a possible stored auth token
|
|
||||||
this.store.pipe(select(isAuthenticated),
|
|
||||||
take(1),
|
|
||||||
filter((authenticated) => !authenticated)
|
|
||||||
).subscribe((authenticated) => this.authService.checkAuthenticationToken());
|
|
||||||
this.sidebarVisible = this.menuService.isMenuVisible(MenuID.ADMIN);
|
this.sidebarVisible = this.menuService.isMenuVisible(MenuID.ADMIN);
|
||||||
|
|
||||||
this.collapsedSidebarWidth = this.cssService.getVariable('collapsedSidebarWidth');
|
this.collapsedSidebarWidth = this.cssService.getVariable('collapsedSidebarWidth');
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
import { NgModule } from '@angular/core';
|
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { EffectsModule } from '@ngrx/effects';
|
import { EffectsModule } from '@ngrx/effects';
|
||||||
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
|
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||||
import { MetaReducer, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store';
|
import { MetaReducer, Store, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store';
|
||||||
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
||||||
import { DYNAMIC_MATCHER_PROVIDERS } from '@ng-dynamic-forms/core';
|
import { DYNAMIC_MATCHER_PROVIDERS } from '@ng-dynamic-forms/core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
@@ -21,6 +21,7 @@ import { AppComponent } from './app.component';
|
|||||||
import { appEffects } from './app.effects';
|
import { appEffects } from './app.effects';
|
||||||
import { appMetaReducers, debugMetaReducers } from './app.metareducers';
|
import { appMetaReducers, debugMetaReducers } from './app.metareducers';
|
||||||
import { appReducers, AppState, storeModuleConfig } from './app.reducer';
|
import { appReducers, AppState, storeModuleConfig } from './app.reducer';
|
||||||
|
import { CheckAuthenticationTokenAction } from './core/auth/auth.actions';
|
||||||
|
|
||||||
import { CoreModule } from './core/core.module';
|
import { CoreModule } from './core/core.module';
|
||||||
import { ClientCookieService } from './core/services/client-cookie.service';
|
import { ClientCookieService } from './core/services/client-cookie.service';
|
||||||
@@ -91,6 +92,15 @@ const PROVIDERS = [
|
|||||||
useClass: DSpaceRouterStateSerializer
|
useClass: DSpaceRouterStateSerializer
|
||||||
},
|
},
|
||||||
ClientCookieService,
|
ClientCookieService,
|
||||||
|
// Check the authentication token when the app initializes
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
useFactory: (store: Store<AppState>,) => {
|
||||||
|
return () => store.dispatch(new CheckAuthenticationTokenAction());
|
||||||
|
},
|
||||||
|
deps: [ Store ],
|
||||||
|
multi: true
|
||||||
|
},
|
||||||
...DYNAMIC_MATCHER_PROVIDERS,
|
...DYNAMIC_MATCHER_PROVIDERS,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user