mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
Merge remote-tracking branch 'dspace/master' into w2p-62589_Item-mapper-update
Conflicts: src/app/core/data/collection-data.service.ts
This commit is contained in:
@@ -3,7 +3,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<img class="mb-4 login-logo" src="assets/images/dspace-logo.png">
|
<img class="mb-4 login-logo" src="assets/images/dspace-logo.png">
|
||||||
<h1 class="h3 mb-0 font-weight-normal">{{"login.form.header" | translate}}</h1>
|
<h1 class="h3 mb-0 font-weight-normal">{{"login.form.header" | translate}}</h1>
|
||||||
<ds-log-in></ds-log-in>
|
<ds-log-in
|
||||||
|
[isStandalonePage]="true"></ds-log-in>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -81,7 +81,6 @@ const components = [
|
|||||||
SearchFilterService,
|
SearchFilterService,
|
||||||
SearchFixedFilterService,
|
SearchFixedFilterService,
|
||||||
ConfigurationSearchPageGuard,
|
ConfigurationSearchPageGuard,
|
||||||
SearchFilterService,
|
|
||||||
SearchConfigurationService
|
SearchConfigurationService
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
|
@@ -23,11 +23,14 @@ import { AppState } from '../../app.reducer';
|
|||||||
import { ClientCookieService } from '../services/client-cookie.service';
|
import { ClientCookieService } from '../services/client-cookie.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { getMockRemoteDataBuildService } from '../../shared/mocks/mock-remote-data-build.service';
|
import { getMockRemoteDataBuildService } from '../../shared/mocks/mock-remote-data-build.service';
|
||||||
|
import { routeServiceStub } from '../../shared/testing/route-service-stub';
|
||||||
|
import { RouteService } from '../services/route.service';
|
||||||
|
|
||||||
describe('AuthService test', () => {
|
describe('AuthService test', () => {
|
||||||
|
|
||||||
let mockStore: Store<AuthState>;
|
let mockStore: Store<AuthState>;
|
||||||
let authService: AuthService;
|
let authService: AuthService;
|
||||||
|
let routeServiceMock: RouteService;
|
||||||
let authRequest;
|
let authRequest;
|
||||||
let window;
|
let window;
|
||||||
let routerStub;
|
let routerStub;
|
||||||
@@ -74,6 +77,7 @@ describe('AuthService test', () => {
|
|||||||
{ provide: NativeWindowService, useValue: window },
|
{ provide: NativeWindowService, useValue: window },
|
||||||
{ provide: REQUEST, useValue: {} },
|
{ provide: REQUEST, useValue: {} },
|
||||||
{ provide: Router, useValue: routerStub },
|
{ provide: Router, useValue: routerStub },
|
||||||
|
{ provide: RouteService, useValue: routeServiceStub },
|
||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
{ provide: Store, useValue: mockStore },
|
{ provide: Store, useValue: mockStore },
|
||||||
{ provide: RemoteDataBuildService, useValue: rdbService },
|
{ provide: RemoteDataBuildService, useValue: rdbService },
|
||||||
@@ -138,6 +142,7 @@ describe('AuthService test', () => {
|
|||||||
{ provide: AuthRequestService, useValue: authRequest },
|
{ provide: AuthRequestService, useValue: authRequest },
|
||||||
{ provide: REQUEST, useValue: {} },
|
{ provide: REQUEST, useValue: {} },
|
||||||
{ provide: Router, useValue: routerStub },
|
{ provide: Router, useValue: routerStub },
|
||||||
|
{ provide: RouteService, useValue: routeServiceStub },
|
||||||
{ provide: RemoteDataBuildService, useValue: rdbService },
|
{ provide: RemoteDataBuildService, useValue: rdbService },
|
||||||
CookieService,
|
CookieService,
|
||||||
AuthService
|
AuthService
|
||||||
@@ -145,13 +150,13 @@ describe('AuthService test', () => {
|
|||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(inject([CookieService, AuthRequestService, Store, Router], (cookieService: CookieService, authReqService: AuthRequestService, store: Store<AppState>, router: Router) => {
|
beforeEach(inject([CookieService, AuthRequestService, Store, Router, RouteService], (cookieService: CookieService, authReqService: AuthRequestService, store: Store<AppState>, router: Router, routeService: RouteService) => {
|
||||||
store
|
store
|
||||||
.subscribe((state) => {
|
.subscribe((state) => {
|
||||||
(state as any).core = Object.create({});
|
(state as any).core = Object.create({});
|
||||||
(state as any).core.auth = authenticatedState;
|
(state as any).core.auth = authenticatedState;
|
||||||
});
|
});
|
||||||
authService = new AuthService({}, window, undefined, authReqService, router, cookieService, store, rdbService);
|
authService = new AuthService({}, window, undefined, authReqService, router, routeService, cookieService, store, rdbService);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should return true when user is logged in', () => {
|
it('should return true when user is logged in', () => {
|
||||||
@@ -189,6 +194,7 @@ describe('AuthService test', () => {
|
|||||||
{ provide: AuthRequestService, useValue: authRequest },
|
{ provide: AuthRequestService, useValue: authRequest },
|
||||||
{ provide: REQUEST, useValue: {} },
|
{ provide: REQUEST, useValue: {} },
|
||||||
{ provide: Router, useValue: routerStub },
|
{ provide: Router, useValue: routerStub },
|
||||||
|
{ provide: RouteService, useValue: routeServiceStub },
|
||||||
{ provide: RemoteDataBuildService, useValue: rdbService },
|
{ provide: RemoteDataBuildService, useValue: rdbService },
|
||||||
ClientCookieService,
|
ClientCookieService,
|
||||||
CookieService,
|
CookieService,
|
||||||
@@ -197,7 +203,7 @@ describe('AuthService test', () => {
|
|||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(inject([ClientCookieService, AuthRequestService, Store, Router], (cookieService: ClientCookieService, authReqService: AuthRequestService, store: Store<AppState>, router: Router) => {
|
beforeEach(inject([ClientCookieService, AuthRequestService, Store, Router, RouteService], (cookieService: ClientCookieService, authReqService: AuthRequestService, store: Store<AppState>, router: Router, routeService: RouteService) => {
|
||||||
const expiredToken: AuthTokenInfo = new AuthTokenInfo('test_token');
|
const expiredToken: AuthTokenInfo = new AuthTokenInfo('test_token');
|
||||||
expiredToken.expires = Date.now() - (1000 * 60 * 60);
|
expiredToken.expires = Date.now() - (1000 * 60 * 60);
|
||||||
authenticatedState = {
|
authenticatedState = {
|
||||||
@@ -212,11 +218,14 @@ describe('AuthService test', () => {
|
|||||||
(state as any).core = Object.create({});
|
(state as any).core = Object.create({});
|
||||||
(state as any).core.auth = authenticatedState;
|
(state as any).core.auth = authenticatedState;
|
||||||
});
|
});
|
||||||
authService = new AuthService({}, window, undefined, authReqService, router, cookieService, store, rdbService);
|
authService = new AuthService({}, window, undefined, authReqService, router, routeService, cookieService, store, rdbService);
|
||||||
storage = (authService as any).storage;
|
storage = (authService as any).storage;
|
||||||
|
routeServiceMock = TestBed.get(RouteService);
|
||||||
|
routerStub = TestBed.get(Router);
|
||||||
spyOn(storage, 'get');
|
spyOn(storage, 'get');
|
||||||
spyOn(storage, 'remove');
|
spyOn(storage, 'remove');
|
||||||
spyOn(storage, 'set');
|
spyOn(storage, 'set');
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should throw false when token is not valid', () => {
|
it('should throw false when token is not valid', () => {
|
||||||
@@ -238,5 +247,32 @@ describe('AuthService test', () => {
|
|||||||
expect(storage.remove).toHaveBeenCalled();
|
expect(storage.remove).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it ('should set redirect url to previous page', () => {
|
||||||
|
spyOn(routeServiceMock, 'getHistory').and.callThrough();
|
||||||
|
authService.redirectAfterLoginSuccess(true);
|
||||||
|
expect(routeServiceMock.getHistory).toHaveBeenCalled();
|
||||||
|
expect(routerStub.navigate).toHaveBeenCalledWith(['/collection/123']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it ('should set redirect url to current page', () => {
|
||||||
|
spyOn(routeServiceMock, 'getHistory').and.callThrough();
|
||||||
|
authService.redirectAfterLoginSuccess(false);
|
||||||
|
expect(routeServiceMock.getHistory).toHaveBeenCalled();
|
||||||
|
expect(routerStub.navigate).toHaveBeenCalledWith(['/home']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it ('should redirect to / and not to /login', () => {
|
||||||
|
spyOn(routeServiceMock, 'getHistory').and.returnValue(observableOf(['/login', '/login']));
|
||||||
|
authService.redirectAfterLoginSuccess(true);
|
||||||
|
expect(routeServiceMock.getHistory).toHaveBeenCalled();
|
||||||
|
expect(routerStub.navigate).toHaveBeenCalledWith(['/']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it ('should redirect to / when no redirect url is found', () => {
|
||||||
|
spyOn(routeServiceMock, 'getHistory').and.returnValue(observableOf(['']));
|
||||||
|
authService.redirectAfterLoginSuccess(true);
|
||||||
|
expect(routeServiceMock.getHistory).toHaveBeenCalled();
|
||||||
|
expect(routerStub.navigate).toHaveBeenCalledWith(['/']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -22,6 +22,7 @@ import { ResetAuthenticationMessagesAction, SetRedirectUrlAction } from './auth.
|
|||||||
import { NativeWindowRef, NativeWindowService } from '../services/window.service';
|
import { NativeWindowRef, NativeWindowService } from '../services/window.service';
|
||||||
import { Base64EncodeUrl } from '../../shared/utils/encode-decode.util';
|
import { Base64EncodeUrl } from '../../shared/utils/encode-decode.util';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
|
import {RouteService} from '../services/route.service';
|
||||||
|
|
||||||
export const LOGIN_ROUTE = '/login';
|
export const LOGIN_ROUTE = '/login';
|
||||||
export const LOGOUT_ROUTE = '/logout';
|
export const LOGOUT_ROUTE = '/logout';
|
||||||
@@ -45,6 +46,7 @@ export class AuthService {
|
|||||||
protected authRequestService: AuthRequestService,
|
protected authRequestService: AuthRequestService,
|
||||||
@Optional() @Inject(RESPONSE) private response: any,
|
@Optional() @Inject(RESPONSE) private response: any,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
|
protected routeService: RouteService,
|
||||||
protected storage: CookieService,
|
protected storage: CookieService,
|
||||||
protected store: Store<AppState>,
|
protected store: Store<AppState>,
|
||||||
protected rdbService: RemoteDataBuildService
|
protected rdbService: RemoteDataBuildService
|
||||||
@@ -337,7 +339,7 @@ export class AuthService {
|
|||||||
/**
|
/**
|
||||||
* Redirect to the route navigated before the login
|
* Redirect to the route navigated before the login
|
||||||
*/
|
*/
|
||||||
public redirectToPreviousUrl() {
|
public redirectAfterLoginSuccess(isStandalonePage: boolean) {
|
||||||
this.getRedirectUrl().pipe(
|
this.getRedirectUrl().pipe(
|
||||||
take(1))
|
take(1))
|
||||||
.subscribe((redirectUrl) => {
|
.subscribe((redirectUrl) => {
|
||||||
@@ -346,16 +348,37 @@ export class AuthService {
|
|||||||
this.clearRedirectUrl();
|
this.clearRedirectUrl();
|
||||||
this.router.onSameUrlNavigation = 'reload';
|
this.router.onSameUrlNavigation = 'reload';
|
||||||
const url = decodeURIComponent(redirectUrl);
|
const url = decodeURIComponent(redirectUrl);
|
||||||
this.router.navigateByUrl(url);
|
this.navigateToRedirectUrl(url);
|
||||||
/* TODO Reenable hard redirect when REST API can handle x-forwarded-for, see https://github.com/DSpace/DSpace/pull/2207 */
|
|
||||||
// this._window.nativeWindow.location.href = url;
|
|
||||||
} else {
|
} else {
|
||||||
|
// If redirectUrl is empty use history.
|
||||||
|
this.routeService.getHistory().pipe(
|
||||||
|
take(1)
|
||||||
|
).subscribe((history) => {
|
||||||
|
let redirUrl;
|
||||||
|
if (isStandalonePage) {
|
||||||
|
// For standalone login pages, use the previous route.
|
||||||
|
redirUrl = history[history.length - 2] || '';
|
||||||
|
} else {
|
||||||
|
redirUrl = history[history.length - 1] || '';
|
||||||
|
}
|
||||||
|
this.navigateToRedirectUrl(redirUrl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected navigateToRedirectUrl(url: string) {
|
||||||
|
// in case the user navigates directly to /login (via bookmark, etc), or the route history is not found.
|
||||||
|
if (isEmpty(url) || url.startsWith(LOGIN_ROUTE)) {
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
/* TODO Reenable hard redirect when REST API can handle x-forwarded-for, see https://github.com/DSpace/DSpace/pull/2207 */
|
/* TODO Reenable hard redirect when REST API can handle x-forwarded-for, see https://github.com/DSpace/DSpace/pull/2207 */
|
||||||
// this._window.nativeWindow.location.href = '/';
|
// this._window.nativeWindow.location.href = '/';
|
||||||
|
} else {
|
||||||
|
/* TODO Reenable hard redirect when REST API can handle x-forwarded-for, see https://github.com/DSpace/DSpace/pull/2207 */
|
||||||
|
// this._window.nativeWindow.location.href = url;
|
||||||
|
this.router.navigate([url]);
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -400,4 +423,5 @@ export class AuthService {
|
|||||||
this.store.dispatch(new SetRedirectUrlAction(''));
|
this.store.dispatch(new SetRedirectUrlAction(''));
|
||||||
this.storage.remove(REDIRECT_COOKIE);
|
this.storage.remove(REDIRECT_COOKIE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import { map, switchMap, take } from 'rxjs/operators';
|
import { filter, map, switchMap, take } from 'rxjs/operators';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { HttpHeaders } from '@angular/common/http';
|
import { HttpHeaders } from '@angular/common/http';
|
||||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||||
import { AuthStatus } from './models/auth-status.model';
|
import { AuthStatus } from './models/auth-status.model';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService, LOGIN_ROUTE } from './auth.service';
|
||||||
import { AuthTokenInfo } from './models/auth-token-info.model';
|
import { AuthTokenInfo } from './models/auth-token-info.model';
|
||||||
import { CheckAuthenticationTokenAction } from './auth.actions';
|
import { CheckAuthenticationTokenAction } from './auth.actions';
|
||||||
import { EPerson } from '../eperson/models/eperson.model';
|
import { EPerson } from '../eperson/models/eperson.model';
|
||||||
@@ -54,7 +54,7 @@ export class ServerAuthService extends AuthService {
|
|||||||
/**
|
/**
|
||||||
* Redirect to the route navigated before the login
|
* Redirect to the route navigated before the login
|
||||||
*/
|
*/
|
||||||
public redirectToPreviousUrl() {
|
public redirectAfterLoginSuccess(isStandalonePage: boolean) {
|
||||||
this.getRedirectUrl().pipe(
|
this.getRedirectUrl().pipe(
|
||||||
take(1))
|
take(1))
|
||||||
.subscribe((redirectUrl) => {
|
.subscribe((redirectUrl) => {
|
||||||
@@ -67,10 +67,15 @@ export class ServerAuthService extends AuthService {
|
|||||||
const url = decodeURIComponent(redirectUrl);
|
const url = decodeURIComponent(redirectUrl);
|
||||||
this.router.navigateByUrl(url);
|
this.router.navigateByUrl(url);
|
||||||
} else {
|
} else {
|
||||||
this.router.navigate(['/']);
|
// If redirectUrl is empty use history. For ssr the history array should contain the requested url.
|
||||||
|
this.routeService.getHistory().pipe(
|
||||||
|
filter((history) => history.length > 0),
|
||||||
|
take(1)
|
||||||
|
).subscribe((history) => {
|
||||||
|
this.navigateToRedirectUrl(history[history.length - 1] || '');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -26,6 +26,7 @@ import { GenericConstructor } from '../shared/generic-constructor';
|
|||||||
import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
|
import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
|
||||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
import { PaginatedSearchOptions } from '../../+search-page/paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../../+search-page/paginated-search-options.model';
|
||||||
|
import { SearchParam } from '../cache/models/search-param.model';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CollectionDataService extends ComColDataService<Collection> {
|
export class CollectionDataService extends ComColDataService<Collection> {
|
||||||
@@ -47,6 +48,36 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all collections the user is authorized to submit to
|
||||||
|
*
|
||||||
|
* @param options The [[FindAllOptions]] object
|
||||||
|
* @return Observable<RemoteData<PaginatedList<Collection>>>
|
||||||
|
* collection list
|
||||||
|
*/
|
||||||
|
getAuthorizedCollection(options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<Collection>>> {
|
||||||
|
const searchHref = 'findAuthorized';
|
||||||
|
|
||||||
|
return this.searchBy(searchHref, options).pipe(
|
||||||
|
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all collections the user is authorized to submit to, by community
|
||||||
|
*
|
||||||
|
* @param communityId The community id
|
||||||
|
* @param options The [[FindAllOptions]] object
|
||||||
|
* @return Observable<RemoteData<PaginatedList<Collection>>>
|
||||||
|
* collection list
|
||||||
|
*/
|
||||||
|
getAuthorizedCollectionByCommunity(communityId: string, options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<Collection>>> {
|
||||||
|
const searchHref = 'findAuthorizedByCommunity';
|
||||||
|
options.searchParams = [new SearchParam('uuid', communityId)];
|
||||||
|
|
||||||
|
return this.searchBy(searchHref, options).pipe(
|
||||||
|
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find whether there is a collection whom user has authorization to submit to
|
* Find whether there is a collection whom user has authorization to submit to
|
||||||
*
|
*
|
||||||
|
@@ -3,7 +3,7 @@ import { HttpHeaders } from '@angular/common/http';
|
|||||||
|
|
||||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||||
import { Observable, race as observableRace } from 'rxjs';
|
import { Observable, race as observableRace } from 'rxjs';
|
||||||
import { filter, find, map, mergeMap, take } from 'rxjs/operators';
|
import { filter, map, mergeMap, take } from 'rxjs/operators';
|
||||||
import { cloneDeep, remove } from 'lodash';
|
import { cloneDeep, remove } from 'lodash';
|
||||||
|
|
||||||
import { AppState } from '../../app.reducer';
|
import { AppState } from '../../app.reducer';
|
||||||
@@ -262,8 +262,9 @@ export class RequestService {
|
|||||||
*/
|
*/
|
||||||
private clearRequestsOnTheirWayToTheStore(request: GetRequest) {
|
private clearRequestsOnTheirWayToTheStore(request: GetRequest) {
|
||||||
this.getByHref(request.href).pipe(
|
this.getByHref(request.href).pipe(
|
||||||
find((re: RequestEntry) => hasValue(re)))
|
filter((re: RequestEntry) => hasValue(re)),
|
||||||
.subscribe((re: RequestEntry) => {
|
take(1)
|
||||||
|
).subscribe((re: RequestEntry) => {
|
||||||
if (!re.responsePending) {
|
if (!re.responsePending) {
|
||||||
remove(this.requestsOnTheirWayToTheStore, (item) => item === request.href);
|
remove(this.requestsOnTheirWayToTheStore, (item) => item === request.href);
|
||||||
}
|
}
|
||||||
|
@@ -128,7 +128,10 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService
|
|||||||
// Iterate over all workspaceitem's sections
|
// Iterate over all workspaceitem's sections
|
||||||
Object.keys(item.sections)
|
Object.keys(item.sections)
|
||||||
.forEach((sectionId) => {
|
.forEach((sectionId) => {
|
||||||
if (typeof item.sections[sectionId] === 'object' && isNotEmpty(item.sections[sectionId])) {
|
if (typeof item.sections[sectionId] === 'object' && (isNotEmpty(item.sections[sectionId]) &&
|
||||||
|
// When Upload section is disabled, add to submission only if there are files
|
||||||
|
(!item.sections[sectionId].hasOwnProperty('files') || isNotEmpty((item.sections[sectionId] as any).files)))) {
|
||||||
|
|
||||||
const normalizedSectionData = Object.create({});
|
const normalizedSectionData = Object.create({});
|
||||||
// Iterate over all sections property
|
// Iterate over all sections property
|
||||||
Object.keys(item.sections[sectionId])
|
Object.keys(item.sections[sectionId])
|
||||||
|
@@ -3,7 +3,8 @@
|
|||||||
<div ngbDropdown placement="bottom-right" class="d-inline-block" @fadeInOut>
|
<div ngbDropdown placement="bottom-right" class="d-inline-block" @fadeInOut>
|
||||||
<a href="#" id="dropdownLogin" (click)="$event.preventDefault()" ngbDropdownToggle class="px-1">{{ 'nav.login' | translate }}</a>
|
<a href="#" id="dropdownLogin" (click)="$event.preventDefault()" ngbDropdownToggle class="px-1">{{ 'nav.login' | translate }}</a>
|
||||||
<div id="loginDropdownMenu" [ngClass]="{'pl-3 pr-3': (loading | async)}" ngbDropdownMenu aria-labelledby="dropdownLogin">
|
<div id="loginDropdownMenu" [ngClass]="{'pl-3 pr-3': (loading | async)}" ngbDropdownMenu aria-labelledby="dropdownLogin">
|
||||||
<ds-log-in></ds-log-in>
|
<ds-log-in
|
||||||
|
[isStandalonePage]="false"></ds-log-in>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@@ -2,6 +2,7 @@ import { isObject, uniqueId } from 'lodash';
|
|||||||
import { hasValue, isNotEmpty } from '../../empty.util';
|
import { hasValue, isNotEmpty } from '../../empty.util';
|
||||||
import { FormFieldMetadataValueObject } from '../../form/builder/models/form-field-metadata-value.model';
|
import { FormFieldMetadataValueObject } from '../../form/builder/models/form-field-metadata-value.model';
|
||||||
import { ConfidenceType } from '../../../core/integration/models/confidence-type';
|
import { ConfidenceType } from '../../../core/integration/models/confidence-type';
|
||||||
|
import { PLACEHOLDER_PARENT_METADATA } from '../../form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model';
|
||||||
|
|
||||||
export interface ChipsItemIcon {
|
export interface ChipsItemIcon {
|
||||||
metadata: string;
|
metadata: string;
|
||||||
@@ -62,7 +63,7 @@ export class ChipsItem {
|
|||||||
if (this._item.hasOwnProperty(icon.metadata)
|
if (this._item.hasOwnProperty(icon.metadata)
|
||||||
&& (((typeof this._item[icon.metadata] === 'string') && hasValue(this._item[icon.metadata]))
|
&& (((typeof this._item[icon.metadata] === 'string') && hasValue(this._item[icon.metadata]))
|
||||||
|| (this._item[icon.metadata] as FormFieldMetadataValueObject).hasValue())
|
|| (this._item[icon.metadata] as FormFieldMetadataValueObject).hasValue())
|
||||||
&& !(this._item[icon.metadata] as FormFieldMetadataValueObject).hasPlaceholder()) {
|
&& !this.hasPlaceholder(this._item[icon.metadata])) {
|
||||||
if ((icon.visibleWhenAuthorityEmpty
|
if ((icon.visibleWhenAuthorityEmpty
|
||||||
|| (this._item[icon.metadata] as FormFieldMetadataValueObject).confidence !== ConfidenceType.CF_UNSET)
|
|| (this._item[icon.metadata] as FormFieldMetadataValueObject).confidence !== ConfidenceType.CF_UNSET)
|
||||||
&& isNotEmpty(icon.style)) {
|
&& isNotEmpty(icon.style)) {
|
||||||
@@ -109,4 +110,9 @@ export class ChipsItem {
|
|||||||
|
|
||||||
this.display = value;
|
this.display = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hasPlaceholder(value: any) {
|
||||||
|
return (typeof value === 'string') ? (value === PLACEHOLDER_PARENT_METADATA) :
|
||||||
|
(value as FormFieldMetadataValueObject).hasPlaceholder()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,8 @@
|
|||||||
|
|
||||||
<ng-container #componentViewContainer></ng-container>
|
<ng-container #componentViewContainer></ng-container>
|
||||||
|
|
||||||
<small *ngIf="hasHint" class="text-muted" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
|
<small *ngIf="hasHint && (!showErrorMessages || errorMessages.length === 0)"
|
||||||
|
class="text-muted" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
|
||||||
|
|
||||||
<div *ngIf="showErrorMessages" [ngClass]="[getClass('element', 'errors'), getClass('grid', 'errors')]">
|
<div *ngIf="showErrorMessages" [ngClass]="[getClass('element', 'errors'), getClass('grid', 'errors')]">
|
||||||
<small *ngFor="let message of errorMessages" class="invalid-feedback d-block">{{ message | translate:model.validators }}</small>
|
<small *ngFor="let message of errorMessages" class="invalid-feedback d-block">{{ message | translate:model.validators }}</small>
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
import { DynamicFormControlLayout, DynamicFormGroupModel, DynamicFormGroupModelConfig, serializable } from '@ng-dynamic-forms/core';
|
import { DynamicFormControlLayout, DynamicFormGroupModel, DynamicFormGroupModelConfig, serializable } from '@ng-dynamic-forms/core';
|
||||||
|
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
import { isNotEmpty } from '../../../../empty.util';
|
import { isNotEmpty } from '../../../../empty.util';
|
||||||
import { DsDynamicInputModel } from './ds-dynamic-input.model';
|
import { DsDynamicInputModel } from './ds-dynamic-input.model';
|
||||||
import { FormFieldMetadataValueObject } from '../../models/form-field-metadata-value.model';
|
import { FormFieldMetadataValueObject } from '../../models/form-field-metadata-value.model';
|
||||||
@@ -16,12 +19,16 @@ export class DynamicConcatModel extends DynamicFormGroupModel {
|
|||||||
@serializable() separator: string;
|
@serializable() separator: string;
|
||||||
@serializable() hasLanguages = false;
|
@serializable() hasLanguages = false;
|
||||||
isCustomGroup = true;
|
isCustomGroup = true;
|
||||||
|
valueUpdates: Subject<string>;
|
||||||
|
|
||||||
constructor(config: DynamicConcatModelConfig, layout?: DynamicFormControlLayout) {
|
constructor(config: DynamicConcatModelConfig, layout?: DynamicFormControlLayout) {
|
||||||
|
|
||||||
super(config, layout);
|
super(config, layout);
|
||||||
|
|
||||||
this.separator = config.separator + ' ';
|
this.separator = config.separator + ' ';
|
||||||
|
|
||||||
|
this.valueUpdates = new Subject<string>();
|
||||||
|
this.valueUpdates.subscribe((value: string) => this.value = value);
|
||||||
}
|
}
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
|
@@ -28,6 +28,7 @@ export class DsDynamicInputModel extends DynamicInputModel {
|
|||||||
constructor(config: DsDynamicInputModelConfig, layout?: DynamicFormControlLayout) {
|
constructor(config: DsDynamicInputModelConfig, layout?: DynamicFormControlLayout) {
|
||||||
super(config, layout);
|
super(config, layout);
|
||||||
|
|
||||||
|
this.hint = config.hint;
|
||||||
this.readOnly = config.readOnly;
|
this.readOnly = config.readOnly;
|
||||||
this.value = config.value;
|
this.value = config.value;
|
||||||
this.language = config.language;
|
this.language = config.language;
|
||||||
@@ -57,11 +58,7 @@ export class DsDynamicInputModel extends DynamicInputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get hasLanguages(): boolean {
|
get hasLanguages(): boolean {
|
||||||
if (this.languageCodes && this.languageCodes.length > 1) {
|
return this.languageCodes && this.languageCodes.length > 1;
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get language(): string {
|
get language(): string {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { DynamicFormControlLayout, DynamicFormGroupModel, DynamicInputModelConfig, serializable } from '@ng-dynamic-forms/core';
|
import { DynamicFormControlLayout, DynamicFormGroupModel, serializable } from '@ng-dynamic-forms/core';
|
||||||
import { DsDynamicInputModel, DsDynamicInputModelConfig } from './ds-dynamic-input.model';
|
import { DsDynamicInputModel } from './ds-dynamic-input.model';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { DynamicFormGroupModelConfig } from '@ng-dynamic-forms/core/src/model/form-group/dynamic-form-group.model';
|
import { DynamicFormGroupModelConfig } from '@ng-dynamic-forms/core/src/model/form-group/dynamic-form-group.model';
|
||||||
import { LanguageCode } from '../../models/form-field-language-value.model';
|
import { LanguageCode } from '../../models/form-field-language-value.model';
|
||||||
@@ -12,6 +12,7 @@ export interface DsDynamicQualdropModelConfig extends DynamicFormGroupModelConfi
|
|||||||
languageCodes?: LanguageCode[];
|
languageCodes?: LanguageCode[];
|
||||||
language?: string;
|
language?: string;
|
||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
|
hint?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DynamicQualdropModel extends DynamicFormGroupModel {
|
export class DynamicQualdropModel extends DynamicFormGroupModel {
|
||||||
@@ -20,6 +21,7 @@ export class DynamicQualdropModel extends DynamicFormGroupModel {
|
|||||||
@serializable() languageUpdates: Subject<string>;
|
@serializable() languageUpdates: Subject<string>;
|
||||||
@serializable() hasLanguages = false;
|
@serializable() hasLanguages = false;
|
||||||
@serializable() readOnly: boolean;
|
@serializable() readOnly: boolean;
|
||||||
|
@serializable() hint: string;
|
||||||
isCustomGroup = true;
|
isCustomGroup = true;
|
||||||
|
|
||||||
constructor(config: DsDynamicQualdropModelConfig, layout?: DynamicFormControlLayout) {
|
constructor(config: DsDynamicQualdropModelConfig, layout?: DynamicFormControlLayout) {
|
||||||
@@ -33,6 +35,8 @@ export class DynamicQualdropModel extends DynamicFormGroupModel {
|
|||||||
this.languageUpdates.subscribe((lang: string) => {
|
this.languageUpdates.subscribe((lang: string) => {
|
||||||
this.language = lang;
|
this.language = lang;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.hint = config.hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
|
@@ -20,11 +20,10 @@
|
|||||||
[disabled]="isInputDisabled()"
|
[disabled]="isInputDisabled()"
|
||||||
[placeholder]="model.placeholder | translate"
|
[placeholder]="model.placeholder | translate"
|
||||||
[readonly]="model.readOnly"
|
[readonly]="model.readOnly"
|
||||||
(change)="$event.preventDefault()"
|
(change)="onChange($event)"
|
||||||
(blur)="onBlurEvent($event); $event.stopPropagation(); sdRef.close();"
|
(blur)="onBlurEvent($event); $event.stopPropagation(); sdRef.close();"
|
||||||
(focus)="onFocusEvent($event); $event.stopPropagation(); sdRef.close();"
|
(focus)="onFocusEvent($event); $event.stopPropagation(); sdRef.close();"
|
||||||
(click)="$event.stopPropagation(); $event.stopPropagation(); sdRef.close();"
|
(click)="$event.stopPropagation(); $event.stopPropagation(); sdRef.close();">
|
||||||
(input)="onInput($event)">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--Lookup-name, second field-->
|
<!--Lookup-name, second field-->
|
||||||
@@ -40,11 +39,10 @@
|
|||||||
[disabled]="firstInputValue.length === 0 || isInputDisabled()"
|
[disabled]="firstInputValue.length === 0 || isInputDisabled()"
|
||||||
[placeholder]="model.secondPlaceholder | translate"
|
[placeholder]="model.secondPlaceholder | translate"
|
||||||
[readonly]="model.readOnly"
|
[readonly]="model.readOnly"
|
||||||
(change)="$event.preventDefault()"
|
(change)="onChange($event)"
|
||||||
(blur)="onBlurEvent($event); $event.stopPropagation(); sdRef.close();"
|
(blur)="onBlurEvent($event); $event.stopPropagation(); sdRef.close();"
|
||||||
(focus)="onFocusEvent($event); $event.stopPropagation(); sdRef.close();"
|
(focus)="onFocusEvent($event); $event.stopPropagation(); sdRef.close();"
|
||||||
(click)="$event.stopPropagation(); sdRef.close();"
|
(click)="$event.stopPropagation(); sdRef.close();">
|
||||||
(input)="onInput($event)">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto text-center">
|
<div class="col-auto text-center">
|
||||||
<button ngbDropdownAnchor
|
<button ngbDropdownAnchor
|
||||||
|
@@ -237,6 +237,12 @@ describe('Dynamic Lookup component', () => {
|
|||||||
|
|
||||||
it('should init component properly', () => {
|
it('should init component properly', () => {
|
||||||
expect(lookupComp.firstInputValue).toBe('');
|
expect(lookupComp.firstInputValue).toBe('');
|
||||||
|
const de = lookupFixture.debugElement.queryAll(By.css('button'));
|
||||||
|
const searchBtnEl = de[0].nativeElement;
|
||||||
|
const editBtnEl = de[1].nativeElement;
|
||||||
|
expect(searchBtnEl.disabled).toBe(true);
|
||||||
|
expect(editBtnEl.disabled).toBe(true);
|
||||||
|
expect(editBtnEl.textContent.trim()).toBe('form.edit');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return search results', fakeAsync(() => {
|
it('should return search results', fakeAsync(() => {
|
||||||
@@ -283,7 +289,7 @@ describe('Dynamic Lookup component', () => {
|
|||||||
lookupComp.firstInputValue = 'test';
|
lookupComp.firstInputValue = 'test';
|
||||||
lookupFixture.detectChanges();
|
lookupFixture.detectChanges();
|
||||||
|
|
||||||
lookupComp.onInput(new Event('input'));
|
lookupComp.onChange(new Event('change'));
|
||||||
expect(lookupComp.model.value).toEqual(new FormFieldMetadataValueObject('test'))
|
expect(lookupComp.model.value).toEqual(new FormFieldMetadataValueObject('test'))
|
||||||
|
|
||||||
}));
|
}));
|
||||||
@@ -293,10 +299,11 @@ describe('Dynamic Lookup component', () => {
|
|||||||
lookupComp.firstInputValue = 'test';
|
lookupComp.firstInputValue = 'test';
|
||||||
lookupFixture.detectChanges();
|
lookupFixture.detectChanges();
|
||||||
|
|
||||||
lookupComp.onInput(new Event('input'));
|
lookupComp.onChange(new Event('change'));
|
||||||
expect(lookupComp.model.value).not.toBeDefined();
|
expect(lookupComp.model.value).not.toBeDefined();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('and init model value is not empty', () => {
|
describe('and init model value is not empty', () => {
|
||||||
@@ -318,6 +325,19 @@ describe('Dynamic Lookup component', () => {
|
|||||||
it('should init component properly', () => {
|
it('should init component properly', () => {
|
||||||
expect(lookupComp.firstInputValue).toBe('test');
|
expect(lookupComp.firstInputValue).toBe('test');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should have search button disabled on edit mode', () => {
|
||||||
|
lookupComp.editMode = true;
|
||||||
|
lookupFixture.detectChanges();
|
||||||
|
|
||||||
|
const de = lookupFixture.debugElement.queryAll(By.css('button'));
|
||||||
|
const searchBtnEl = de[0].nativeElement;
|
||||||
|
const saveBtnEl = de[1].nativeElement;
|
||||||
|
expect(searchBtnEl.disabled).toBe(true);
|
||||||
|
expect(saveBtnEl.disabled).toBe(false);
|
||||||
|
expect(saveBtnEl.textContent.trim()).toBe('form.save');
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -340,7 +360,14 @@ describe('Dynamic Lookup component', () => {
|
|||||||
});
|
});
|
||||||
it('should render two input element', () => {
|
it('should render two input element', () => {
|
||||||
const de = lookupFixture.debugElement.queryAll(By.css('input.form-control'));
|
const de = lookupFixture.debugElement.queryAll(By.css('input.form-control'));
|
||||||
|
const deBtn = lookupFixture.debugElement.queryAll(By.css('button'));
|
||||||
|
const searchBtnEl = deBtn[0].nativeElement;
|
||||||
|
const editBtnEl = deBtn[1].nativeElement;
|
||||||
|
|
||||||
expect(de.length).toBe(2);
|
expect(de.length).toBe(2);
|
||||||
|
expect(searchBtnEl.disabled).toBe(true);
|
||||||
|
expect(editBtnEl.disabled).toBe(true);
|
||||||
|
expect(editBtnEl.textContent.trim()).toBe('form.edit');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -418,6 +445,19 @@ describe('Dynamic Lookup component', () => {
|
|||||||
expect(lookupComp.firstInputValue).toBe('Name');
|
expect(lookupComp.firstInputValue).toBe('Name');
|
||||||
expect(lookupComp.secondInputValue).toBe('Lastname');
|
expect(lookupComp.secondInputValue).toBe('Lastname');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should have search button disabled on edit mode', () => {
|
||||||
|
lookupComp.editMode = true;
|
||||||
|
lookupFixture.detectChanges();
|
||||||
|
|
||||||
|
const de = lookupFixture.debugElement.queryAll(By.css('button'));
|
||||||
|
const searchBtnEl = de[0].nativeElement;
|
||||||
|
const saveBtnEl = de[1].nativeElement;
|
||||||
|
expect(searchBtnEl.disabled).toBe(true);
|
||||||
|
expect(saveBtnEl.disabled).toBe(false);
|
||||||
|
expect(saveBtnEl.textContent.trim()).toBe('form.save');
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -123,6 +123,15 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected updateModel(value) {
|
||||||
|
this.group.markAsDirty();
|
||||||
|
this.model.valueUpdates.next(value);
|
||||||
|
this.setInputsValue(value);
|
||||||
|
this.change.emit(value);
|
||||||
|
this.optionsList = null;
|
||||||
|
this.pageInfo = null;
|
||||||
|
}
|
||||||
|
|
||||||
public formatItemForInput(item: any, field: number): string {
|
public formatItemForInput(item: any, field: number): string {
|
||||||
if (isUndefined(item) || isNull(item)) {
|
if (isUndefined(item) || isNull(item)) {
|
||||||
return '';
|
return '';
|
||||||
@@ -159,7 +168,7 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isSearchDisabled() {
|
public isSearchDisabled() {
|
||||||
return isEmpty(this.firstInputValue);
|
return isEmpty(this.firstInputValue) || this.editMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public onBlurEvent(event: Event) {
|
public onBlurEvent(event: Event) {
|
||||||
@@ -170,12 +179,13 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
|
|||||||
this.focus.emit(event);
|
this.focus.emit(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onInput(event) {
|
public onChange(event) {
|
||||||
|
event.preventDefault();
|
||||||
if (!this.model.authorityOptions.closed) {
|
if (!this.model.authorityOptions.closed) {
|
||||||
if (isNotEmpty(this.getCurrentValue())) {
|
if (isNotEmpty(this.getCurrentValue())) {
|
||||||
const currentValue = new FormFieldMetadataValueObject(this.getCurrentValue());
|
const currentValue = new FormFieldMetadataValueObject(this.getCurrentValue());
|
||||||
if (!this.editMode) {
|
if (!this.editMode) {
|
||||||
this.onSelect(currentValue);
|
this.updateModel(currentValue);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.remove();
|
this.remove();
|
||||||
@@ -191,12 +201,7 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onSelect(event) {
|
public onSelect(event) {
|
||||||
this.group.markAsDirty();
|
this.updateModel(event);
|
||||||
this.model.valueUpdates.next(event);
|
|
||||||
this.setInputsValue(event);
|
|
||||||
this.change.emit(event);
|
|
||||||
this.optionsList = null;
|
|
||||||
this.pageInfo = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public openChange(isOpened: boolean) {
|
public openChange(isOpened: boolean) {
|
||||||
@@ -219,7 +224,7 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
|
|||||||
display: this.getCurrentValue(),
|
display: this.getCurrentValue(),
|
||||||
value: this.getCurrentValue()
|
value: this.getCurrentValue()
|
||||||
});
|
});
|
||||||
this.onSelect(newValue);
|
this.updateModel(newValue);
|
||||||
} else {
|
} else {
|
||||||
this.remove();
|
this.remove();
|
||||||
}
|
}
|
||||||
|
@@ -129,9 +129,11 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent
|
|||||||
|| this.selectedChipItem.item[model.name].value === PLACEHOLDER_PARENT_METADATA)
|
|| this.selectedChipItem.item[model.name].value === PLACEHOLDER_PARENT_METADATA)
|
||||||
? null
|
? null
|
||||||
: this.selectedChipItem.item[model.name];
|
: this.selectedChipItem.item[model.name];
|
||||||
if (isNotNull(value)) {
|
|
||||||
model.valueUpdates.next(this.formBuilderService.isInputModel(model) ? value.value : value);
|
const nextValue = (this.formBuilderService.isInputModel(model) && isNotNull(value) && (typeof value !== 'string')) ?
|
||||||
}
|
value.value : value;
|
||||||
|
model.valueUpdates.next(nextValue);
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -253,7 +255,7 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent
|
|||||||
} else {
|
} else {
|
||||||
return$ = observableOf(valueObj[fieldName]);
|
return$ = observableOf(valueObj[fieldName]);
|
||||||
}
|
}
|
||||||
return return$.pipe(map((entry) => ({[fieldName]: entry})));
|
return return$.pipe(map((entry) => ({ [fieldName]: entry })));
|
||||||
});
|
});
|
||||||
|
|
||||||
returnList.push(combineLatest(returnObj));
|
returnList.push(combineLatest(returnObj));
|
||||||
|
@@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } fro
|
|||||||
import { FormGroup } from '@angular/forms';
|
import { FormGroup } from '@angular/forms';
|
||||||
|
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
import { catchError, first, tap } from 'rxjs/operators';
|
import { catchError, distinctUntilChanged, first, tap } from 'rxjs/operators';
|
||||||
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import {
|
import {
|
||||||
DynamicFormControlComponent,
|
DynamicFormControlComponent,
|
||||||
@@ -71,7 +71,13 @@ export class DsDynamicScrollableDropdownComponent extends DynamicFormControlComp
|
|||||||
}
|
}
|
||||||
this.pageInfo = object.pageInfo;
|
this.pageInfo = object.pageInfo;
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
})
|
});
|
||||||
|
|
||||||
|
this.group.get(this.model.id).valueChanges.pipe(distinctUntilChanged())
|
||||||
|
.subscribe((value) => {
|
||||||
|
this.setCurrentValue(value);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputFormatter = (x: AuthorityValue): string => x.display || x.value;
|
inputFormatter = (x: AuthorityValue): string => x.display || x.value;
|
||||||
|
@@ -28,7 +28,8 @@
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
[authorityValue]="currentValue"
|
[authorityValue]="currentValue"
|
||||||
(whenClickOnConfidenceNotAccepted)="whenClickOnConfidenceNotAccepted($event)"></i>
|
(whenClickOnConfidenceNotAccepted)="whenClickOnConfidenceNotAccepted($event)"></i>
|
||||||
<input class="form-control"
|
<input #instance="ngbTypeahead"
|
||||||
|
class="form-control"
|
||||||
[attr.autoComplete]="model.autoComplete"
|
[attr.autoComplete]="model.autoComplete"
|
||||||
[class.is-invalid]="showErrorMessages"
|
[class.is-invalid]="showErrorMessages"
|
||||||
[dynamicId]="bindId && model.id"
|
[dynamicId]="bindId && model.id"
|
||||||
|
@@ -156,7 +156,7 @@ describe('DsDynamicTypeaheadComponent test suite', () => {
|
|||||||
inputElement.value = 'test value';
|
inputElement.value = 'test value';
|
||||||
inputElement.dispatchEvent(new Event('input'));
|
inputElement.dispatchEvent(new Event('input'));
|
||||||
|
|
||||||
expect((typeaheadComp.model as any).value).toEqual(new FormFieldMetadataValueObject('test value'))
|
expect(typeaheadComp.inputValue).toEqual(new FormFieldMetadataValueObject('test value'))
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -173,19 +173,56 @@ describe('DsDynamicTypeaheadComponent test suite', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit blur Event onBlur', () => {
|
it('should emit blur Event onBlur when popup is closed', () => {
|
||||||
spyOn(typeaheadComp.blur, 'emit');
|
spyOn(typeaheadComp.blur, 'emit');
|
||||||
|
spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(false);
|
||||||
typeaheadComp.onBlur(new Event('blur'));
|
typeaheadComp.onBlur(new Event('blur'));
|
||||||
expect(typeaheadComp.blur.emit).toHaveBeenCalled();
|
expect(typeaheadComp.blur.emit).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit change Event onBlur when AuthorityOptions.closed is false', () => {
|
it('should not emit blur Event onBlur when popup is opened', () => {
|
||||||
|
spyOn(typeaheadComp.blur, 'emit');
|
||||||
|
spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(true);
|
||||||
|
const input = typeaheadFixture.debugElement.query(By.css('input'));
|
||||||
|
|
||||||
|
input.nativeElement.blur();
|
||||||
|
expect(typeaheadComp.blur.emit).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit change Event onBlur when AuthorityOptions.closed is false and inputValue is changed', () => {
|
||||||
typeaheadComp.inputValue = 'test value';
|
typeaheadComp.inputValue = 'test value';
|
||||||
typeaheadFixture.detectChanges();
|
typeaheadFixture.detectChanges();
|
||||||
spyOn(typeaheadComp.blur, 'emit');
|
spyOn(typeaheadComp.blur, 'emit');
|
||||||
spyOn(typeaheadComp.change, 'emit');
|
spyOn(typeaheadComp.change, 'emit');
|
||||||
typeaheadComp.onBlur(new Event('blur'));
|
spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(false);
|
||||||
// expect(typeaheadComp.change.emit).toHaveBeenCalled();
|
typeaheadComp.onBlur(new Event('blur', ));
|
||||||
|
expect(typeaheadComp.change.emit).toHaveBeenCalled();
|
||||||
|
expect(typeaheadComp.blur.emit).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not emit change Event onBlur when AuthorityOptions.closed is false and inputValue is not changed', () => {
|
||||||
|
typeaheadComp.inputValue = 'test value';
|
||||||
|
typeaheadComp.model = new DynamicTypeaheadModel(TYPEAHEAD_TEST_MODEL_CONFIG);
|
||||||
|
(typeaheadComp.model as any).value = 'test value';
|
||||||
|
typeaheadFixture.detectChanges();
|
||||||
|
spyOn(typeaheadComp.blur, 'emit');
|
||||||
|
spyOn(typeaheadComp.change, 'emit');
|
||||||
|
spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(false);
|
||||||
|
typeaheadComp.onBlur(new Event('blur', ));
|
||||||
|
expect(typeaheadComp.change.emit).not.toHaveBeenCalled();
|
||||||
|
expect(typeaheadComp.blur.emit).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not emit change Event onBlur when AuthorityOptions.closed is false and inputValue is null', () => {
|
||||||
|
typeaheadComp.inputValue = null;
|
||||||
|
typeaheadComp.model = new DynamicTypeaheadModel(TYPEAHEAD_TEST_MODEL_CONFIG);
|
||||||
|
(typeaheadComp.model as any).value = 'test value';
|
||||||
|
typeaheadFixture.detectChanges();
|
||||||
|
spyOn(typeaheadComp.blur, 'emit');
|
||||||
|
spyOn(typeaheadComp.change, 'emit');
|
||||||
|
spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(false);
|
||||||
|
typeaheadComp.onBlur(new Event('blur', ));
|
||||||
|
expect(typeaheadComp.change.emit).not.toHaveBeenCalled();
|
||||||
expect(typeaheadComp.blur.emit).toHaveBeenCalled();
|
expect(typeaheadComp.blur.emit).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||||
import { FormGroup } from '@angular/forms';
|
import { FormGroup } from '@angular/forms';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -8,14 +8,13 @@ import {
|
|||||||
} from '@ng-dynamic-forms/core';
|
} from '@ng-dynamic-forms/core';
|
||||||
import { catchError, debounceTime, distinctUntilChanged, filter, map, merge, switchMap, tap } from 'rxjs/operators';
|
import { catchError, debounceTime, distinctUntilChanged, filter, map, merge, switchMap, tap } from 'rxjs/operators';
|
||||||
import { Observable, of as observableOf, Subject } from 'rxjs';
|
import { Observable, of as observableOf, Subject } from 'rxjs';
|
||||||
import { NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbTypeahead, NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
import { AuthorityService } from '../../../../../../core/integration/authority.service';
|
import { AuthorityService } from '../../../../../../core/integration/authority.service';
|
||||||
import { DynamicTypeaheadModel } from './dynamic-typeahead.model';
|
import { DynamicTypeaheadModel } from './dynamic-typeahead.model';
|
||||||
import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model';
|
import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model';
|
||||||
import { isEmpty, isNotEmpty } from '../../../../../empty.util';
|
import { isEmpty, isNotEmpty, isNotNull } from '../../../../../empty.util';
|
||||||
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
|
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
|
||||||
|
|
||||||
import { ConfidenceType } from '../../../../../../core/integration/models/confidence-type';
|
import { ConfidenceType } from '../../../../../../core/integration/models/confidence-type';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -32,6 +31,8 @@ export class DsDynamicTypeaheadComponent extends DynamicFormControlComponent imp
|
|||||||
@Output() change: EventEmitter<any> = new EventEmitter<any>();
|
@Output() change: EventEmitter<any> = new EventEmitter<any>();
|
||||||
@Output() focus: EventEmitter<any> = new EventEmitter<any>();
|
@Output() focus: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
|
@ViewChild('instance') instance: NgbTypeahead;
|
||||||
|
|
||||||
searching = false;
|
searching = false;
|
||||||
searchOptions: IntegrationSearchOptions;
|
searchOptions: IntegrationSearchOptions;
|
||||||
searchFailed = false;
|
searchFailed = false;
|
||||||
@@ -105,16 +106,26 @@ export class DsDynamicTypeaheadComponent extends DynamicFormControlComponent imp
|
|||||||
onInput(event) {
|
onInput(event) {
|
||||||
if (!this.model.authorityOptions.closed && isNotEmpty(event.target.value)) {
|
if (!this.model.authorityOptions.closed && isNotEmpty(event.target.value)) {
|
||||||
this.inputValue = new FormFieldMetadataValueObject(event.target.value);
|
this.inputValue = new FormFieldMetadataValueObject(event.target.value);
|
||||||
this.model.valueUpdates.next(this.inputValue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onBlur(event: Event) {
|
onBlur(event: Event) {
|
||||||
|
if (!this.instance.isPopupOpen()) {
|
||||||
if (!this.model.authorityOptions.closed && isNotEmpty(this.inputValue)) {
|
if (!this.model.authorityOptions.closed && isNotEmpty(this.inputValue)) {
|
||||||
|
if (isNotNull(this.inputValue) && this.model.value !== this.inputValue) {
|
||||||
|
this.model.valueUpdates.next(this.inputValue);
|
||||||
this.change.emit(this.inputValue);
|
this.change.emit(this.inputValue);
|
||||||
|
}
|
||||||
this.inputValue = null;
|
this.inputValue = null;
|
||||||
}
|
}
|
||||||
this.blur.emit(event);
|
this.blur.emit(event);
|
||||||
|
} else {
|
||||||
|
// prevent on blur propagation if typeahed suggestions are showed
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
// set focus on input again, this is to avoid to lose changes when no suggestion is selected
|
||||||
|
(event.target as HTMLInputElement).focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(event: Event) {
|
onChange(event: Event) {
|
||||||
@@ -141,4 +152,5 @@ export class DsDynamicTypeaheadComponent extends DynamicFormControlComponent imp
|
|||||||
this.click$.next(this.formatter(this.currentValue));
|
this.click$.next(this.formatter(this.currentValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -47,6 +47,7 @@ export class ConcatFieldParser extends FieldParser {
|
|||||||
|
|
||||||
const input1ModelConfig: DynamicInputModelConfig = this.initModel(id + CONCAT_FIRST_INPUT_SUFFIX, label, false, false);
|
const input1ModelConfig: DynamicInputModelConfig = this.initModel(id + CONCAT_FIRST_INPUT_SUFFIX, label, false, false);
|
||||||
const input2ModelConfig: DynamicInputModelConfig = this.initModel(id + CONCAT_SECOND_INPUT_SUFFIX, label, true, false);
|
const input2ModelConfig: DynamicInputModelConfig = this.initModel(id + CONCAT_SECOND_INPUT_SUFFIX, label, true, false);
|
||||||
|
input2ModelConfig.hint = ' ';
|
||||||
|
|
||||||
if (this.configData.mandatory) {
|
if (this.configData.mandatory) {
|
||||||
input1ModelConfig.required = true;
|
input1ModelConfig.required = true;
|
||||||
|
@@ -190,6 +190,8 @@ export abstract class FieldParser {
|
|||||||
|
|
||||||
controlModel.placeholder = this.configData.label;
|
controlModel.placeholder = this.configData.label;
|
||||||
|
|
||||||
|
controlModel.hint = this.configData.hints;
|
||||||
|
|
||||||
if (this.configData.mandatory && setErrors) {
|
if (this.configData.mandatory && setErrors) {
|
||||||
this.markAsRequired(controlModel);
|
this.markAsRequired(controlModel);
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,7 @@ export class OneboxFieldParser extends FieldParser {
|
|||||||
const clsGroup = {
|
const clsGroup = {
|
||||||
element: {
|
element: {
|
||||||
control: 'form-row',
|
control: 'form-row',
|
||||||
|
hint: 'ds-form-qualdrop-hint'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -54,8 +55,10 @@ export class OneboxFieldParser extends FieldParser {
|
|||||||
inputSelectGroup.id = newId.replace(/\./g, '_') + QUALDROP_GROUP_SUFFIX;
|
inputSelectGroup.id = newId.replace(/\./g, '_') + QUALDROP_GROUP_SUFFIX;
|
||||||
inputSelectGroup.group = [];
|
inputSelectGroup.group = [];
|
||||||
inputSelectGroup.legend = this.configData.label;
|
inputSelectGroup.legend = this.configData.label;
|
||||||
|
inputSelectGroup.hint = this.configData.hints;
|
||||||
|
|
||||||
const selectModelConfig: DynamicSelectModelConfig<any> = this.initModel(newId + QUALDROP_METADATA_SUFFIX, label);
|
const selectModelConfig: DynamicSelectModelConfig<any> = this.initModel(newId + QUALDROP_METADATA_SUFFIX, label);
|
||||||
|
selectModelConfig.hint = null;
|
||||||
this.setOptions(selectModelConfig);
|
this.setOptions(selectModelConfig);
|
||||||
if (isNotEmpty(fieldValue)) {
|
if (isNotEmpty(fieldValue)) {
|
||||||
selectModelConfig.value = fieldValue.metadata;
|
selectModelConfig.value = fieldValue.metadata;
|
||||||
@@ -63,6 +66,7 @@ export class OneboxFieldParser extends FieldParser {
|
|||||||
inputSelectGroup.group.push(new DynamicSelectModel(selectModelConfig, clsSelect));
|
inputSelectGroup.group.push(new DynamicSelectModel(selectModelConfig, clsSelect));
|
||||||
|
|
||||||
const inputModelConfig: DsDynamicInputModelConfig = this.initModel(newId + QUALDROP_VALUE_SUFFIX, label, true);
|
const inputModelConfig: DsDynamicInputModelConfig = this.initModel(newId + QUALDROP_VALUE_SUFFIX, label, true);
|
||||||
|
inputModelConfig.hint = null;
|
||||||
this.setValues(inputModelConfig, fieldValue);
|
this.setValues(inputModelConfig, fieldValue);
|
||||||
|
|
||||||
inputSelectGroup.readOnly = selectModelConfig.disabled && inputModelConfig.readOnly;
|
inputSelectGroup.readOnly = selectModelConfig.disabled && inputModelConfig.readOnly;
|
||||||
|
@@ -42,3 +42,8 @@
|
|||||||
.right-addon input {
|
.right-addon input {
|
||||||
padding-right: $spacer * 2.25;
|
padding-right: $spacer * 2.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ds-form-qualdrop-hint {
|
||||||
|
top: -$spacer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
@@ -95,6 +95,7 @@ describe('LogInComponent', () => {
|
|||||||
// verify Store.dispatch() is invoked
|
// verify Store.dispatch() is invoked
|
||||||
expect(page.navigateSpy.calls.any()).toBe(true, 'Store.dispatch not invoked');
|
expect(page.navigateSpy.calls.any()).toBe(true, 'Store.dispatch not invoked');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { filter, map, takeWhile } from 'rxjs/operators';
|
import { filter, map, takeWhile } from 'rxjs/operators';
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
|
||||||
import { select, Store } from '@ngrx/store';
|
import { select, Store } from '@ngrx/store';
|
||||||
@@ -20,6 +20,7 @@ import { CoreState } from '../../core/core.reducers';
|
|||||||
import { isNotEmpty } from '../empty.util';
|
import { isNotEmpty } from '../empty.util';
|
||||||
import { fadeOut } from '../animations/fade';
|
import { fadeOut } from '../animations/fade';
|
||||||
import { AuthService } from '../../core/auth/auth.service';
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /users/sign-in
|
* /users/sign-in
|
||||||
@@ -81,10 +82,13 @@ export class LogInComponent implements OnDestroy, OnInit {
|
|||||||
*/
|
*/
|
||||||
private alive = true;
|
private alive = true;
|
||||||
|
|
||||||
|
@Input() isStandalonePage: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {AuthService} authService
|
* @param {AuthService} authService
|
||||||
* @param {FormBuilder} formBuilder
|
* @param {FormBuilder} formBuilder
|
||||||
|
* @param {Router} router
|
||||||
* @param {Store<State>} store
|
* @param {Store<State>} store
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
@@ -135,7 +139,7 @@ export class LogInComponent implements OnDestroy, OnInit {
|
|||||||
takeWhile(() => this.alive),
|
takeWhile(() => this.alive),
|
||||||
filter((authenticated) => authenticated))
|
filter((authenticated) => authenticated))
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.authService.redirectToPreviousUrl();
|
this.authService.redirectAfterLoginSuccess(this.isStandalonePage);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -188,4 +192,5 @@ export class LogInComponent implements OnDestroy, OnInit {
|
|||||||
// clear form
|
// clear form
|
||||||
this.form.reset();
|
this.form.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<a [class.disabled]="!(object.workflowitem | async)?.hasSucceeded"
|
<a [class.disabled]="!(object.workflowitem | async)?.hasSucceeded"
|
||||||
class="btn btn-primary mt-1 mb-3"
|
class="btn btn-primary mt-1 mb-3"
|
||||||
ngbTooltip="{{'submission.workflow.tasks.claimed.edit_help' | translate}}"
|
ngbTooltip="{{'submission.workflow.tasks.claimed.edit_help' | translate}}"
|
||||||
[routerLink]="['/workflowitems/' + (object.workflowitem | async)?.payload.id + '/' + object.id + '/edit']"
|
[routerLink]="['/workflowitems/' + (object.workflowitem | async)?.payload.id + '/edit']"
|
||||||
role="button">
|
role="button">
|
||||||
<i class="fa fa-edit"></i> {{'submission.workflow.tasks.claimed.edit' | translate}}
|
<i class="fa fa-edit"></i> {{'submission.workflow.tasks.claimed.edit' | translate}}
|
||||||
</a>
|
</a>
|
||||||
|
@@ -15,11 +15,11 @@
|
|||||||
<ng-container *ngVar="(bitstreams$ | async) as bitstreams">
|
<ng-container *ngVar="(bitstreams$ | async) as bitstreams">
|
||||||
<ds-metadata-field-wrapper [label]="('item.page.files' | translate)">
|
<ds-metadata-field-wrapper [label]="('item.page.files' | translate)">
|
||||||
<div *ngIf="bitstreams?.length > 0" class="file-section">
|
<div *ngIf="bitstreams?.length > 0" class="file-section">
|
||||||
<a *ngFor="let file of bitstreams; let last=last;" [href]="file?.content" target="_blank" [download]="file?.name">
|
<button class="btn btn-link" *ngFor="let file of bitstreams; let last=last;" (click)="downloadBitstreamFile(file?.uuid)">
|
||||||
<span>{{file?.name}}</span>
|
<span>{{file?.name}}</span>
|
||||||
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
|
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
|
||||||
<span *ngIf="!last" innerHTML="{{separator}}"></span>
|
<span *ngIf="!last" innerHTML="{{separator}}"></span>
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="bitstreams?.length === 0">
|
<ng-container *ngIf="bitstreams?.length === 0">
|
||||||
<span class="text-muted">{{('mydspace.results.no-files' | translate)}}</span>
|
<span class="text-muted">{{('mydspace.results.no-files' | translate)}}</span>
|
||||||
|
@@ -12,10 +12,20 @@ import { MockTranslateLoader } from '../../../mocks/mock-translate-loader';
|
|||||||
import { ItemDetailPreviewFieldComponent } from './item-detail-preview-field/item-detail-preview-field.component';
|
import { ItemDetailPreviewFieldComponent } from './item-detail-preview-field/item-detail-preview-field.component';
|
||||||
import { FileSizePipe } from '../../../utils/file-size-pipe';
|
import { FileSizePipe } from '../../../utils/file-size-pipe';
|
||||||
import { VarDirective } from '../../../utils/var.directive';
|
import { VarDirective } from '../../../utils/var.directive';
|
||||||
|
import { FileService } from '../../../../core/shared/file.service';
|
||||||
|
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
|
||||||
|
import { HALEndpointServiceStub } from '../../../testing/hal-endpoint-service-stub';
|
||||||
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 { PageInfo } from '../../../../core/shared/page-info.model';
|
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||||
|
|
||||||
|
function getMockFileService(): FileService {
|
||||||
|
return jasmine.createSpyObj('FileService', {
|
||||||
|
downloadFile: jasmine.createSpy('downloadFile'),
|
||||||
|
getFileNameFromResponseContentDisposition: jasmine.createSpy('getFileNameFromResponseContentDisposition')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let component: ItemDetailPreviewComponent;
|
let component: ItemDetailPreviewComponent;
|
||||||
let fixture: ComponentFixture<ItemDetailPreviewComponent>;
|
let fixture: ComponentFixture<ItemDetailPreviewComponent>;
|
||||||
|
|
||||||
@@ -62,6 +72,10 @@ describe('ItemDetailPreviewComponent', () => {
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
declarations: [ItemDetailPreviewComponent, ItemDetailPreviewFieldComponent, TruncatePipe, FileSizePipe, VarDirective],
|
declarations: [ItemDetailPreviewComponent, ItemDetailPreviewFieldComponent, TruncatePipe, FileSizePipe, VarDirective],
|
||||||
|
providers: [
|
||||||
|
{ provide: FileService, useValue: getMockFileService() },
|
||||||
|
{ provide: HALEndpointService, useValue: new HALEndpointServiceStub('workspaceitems') }
|
||||||
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(ItemDetailPreviewComponent, {
|
}).overrideComponent(ItemDetailPreviewComponent, {
|
||||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||||
|
@@ -1,12 +1,15 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { first } from 'rxjs/operators';
|
||||||
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
||||||
import { fadeInOut } from '../../../animations/fade';
|
import { fadeInOut } from '../../../animations/fade';
|
||||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
import { MyDSpaceResult } from '../../../../+my-dspace-page/my-dspace-result.model';
|
import { MyDSpaceResult } from '../../../../+my-dspace-page/my-dspace-result.model';
|
||||||
|
import { FileService } from '../../../../core/shared/file.service';
|
||||||
|
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component show metadata for the given item object in the detail view.
|
* This component show metadata for the given item object in the detail view.
|
||||||
@@ -54,6 +57,16 @@ export class ItemDetailPreviewComponent {
|
|||||||
*/
|
*/
|
||||||
public thumbnail$: Observable<Bitstream>;
|
public thumbnail$: Observable<Bitstream>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {FileService} fileService
|
||||||
|
* @param {HALEndpointService} halService
|
||||||
|
*/
|
||||||
|
constructor(private fileService: FileService,
|
||||||
|
private halService: HALEndpointService) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize all instance variables
|
* Initialize all instance variables
|
||||||
*/
|
*/
|
||||||
@@ -62,4 +75,15 @@ export class ItemDetailPreviewComponent {
|
|||||||
this.bitstreams$ = this.item.getFiles();
|
this.bitstreams$ = this.item.getFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform bitstream download
|
||||||
|
*/
|
||||||
|
public downloadBitstreamFile(uuid: string) {
|
||||||
|
this.halService.getEndpoint('bitstreams').pipe(
|
||||||
|
first())
|
||||||
|
.subscribe((url) => {
|
||||||
|
const fileUrl = `${url}/${uuid}/content`;
|
||||||
|
this.fileService.downloadFile(fileUrl);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ export class AuthServiceStub {
|
|||||||
|
|
||||||
token: AuthTokenInfo = new AuthTokenInfo('token_test');
|
token: AuthTokenInfo = new AuthTokenInfo('token_test');
|
||||||
private _tokenExpired = false;
|
private _tokenExpired = false;
|
||||||
|
private redirectUrl;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.token.expires = Date.now() + (1000 * 60 * 60);
|
this.token.expires = Date.now() + (1000 * 60 * 60);
|
||||||
@@ -88,7 +89,11 @@ export class AuthServiceStub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setRedirectUrl(url: string) {
|
setRedirectUrl(url: string) {
|
||||||
return;
|
this.redirectUrl = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRedirectUrl() {
|
||||||
|
return observableOf(this.redirectUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public storeToken(token: AuthTokenInfo) {
|
public storeToken(token: AuthTokenInfo) {
|
||||||
|
@@ -27,6 +27,9 @@ export const routeServiceStub: any = {
|
|||||||
},
|
},
|
||||||
getRouteDataValue: (param) => {
|
getRouteDataValue: (param) => {
|
||||||
return observableOf({})
|
return observableOf({})
|
||||||
|
},
|
||||||
|
getHistory: () => {
|
||||||
|
return observableOf(['/home','/collection/123','/home'])
|
||||||
}
|
}
|
||||||
/* tslint:enable:no-empty */
|
/* tslint:enable:no-empty */
|
||||||
};
|
};
|
||||||
|
@@ -10,10 +10,10 @@
|
|||||||
class="btn btn-outline-primary"
|
class="btn btn-outline-primary"
|
||||||
(blur)="onClose()"
|
(blur)="onClose()"
|
||||||
(click)="onClose()"
|
(click)="onClose()"
|
||||||
[disabled]="(disabled$ | async)"
|
[disabled]="(disabled$ | async) || (processingChange$ | async)"
|
||||||
ngbDropdownToggle>
|
ngbDropdownToggle>
|
||||||
<span *ngIf="(disabled$ | async)"><i class='fas fa-circle-notch fa-spin'></i></span>
|
<span *ngIf="(processingChange$ | async)"><i class='fas fa-circle-notch fa-spin'></i></span>
|
||||||
<span *ngIf="!(disabled$ | async)">{{ selectedCollectionName$ | async }}</span>
|
<span *ngIf="!(processingChange$ | async)">{{ selectedCollectionName$ | async }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div ngbDropdownMenu
|
<div ngbDropdownMenu
|
||||||
|
@@ -1,10 +1,4 @@
|
|||||||
import {
|
import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA, DebugElement, SimpleChange } from '@angular/core';
|
||||||
ChangeDetectorRef,
|
|
||||||
Component,
|
|
||||||
CUSTOM_ELEMENTS_SCHEMA,
|
|
||||||
DebugElement,
|
|
||||||
SimpleChange
|
|
||||||
} from '@angular/core';
|
|
||||||
import { async, ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing';
|
import { async, ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
@@ -14,12 +8,10 @@ import { filter } from 'rxjs/operators';
|
|||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
|
import { cold } from 'jasmine-marbles';
|
||||||
|
|
||||||
import { SubmissionServiceStub } from '../../../shared/testing/submission-service-stub';
|
import { SubmissionServiceStub } from '../../../shared/testing/submission-service-stub';
|
||||||
import {
|
import { mockSubmissionId, mockSubmissionRestResponse } from '../../../shared/mocks/mock-submission';
|
||||||
mockSubmissionId,
|
|
||||||
mockSubmissionRestResponse
|
|
||||||
} from '../../../shared/mocks/mock-submission';
|
|
||||||
import { SubmissionService } from '../../submission.service';
|
import { SubmissionService } from '../../submission.service';
|
||||||
import { SubmissionFormCollectionComponent } from './submission-form-collection.component';
|
import { SubmissionFormCollectionComponent } from './submission-form-collection.component';
|
||||||
import { CommunityDataService } from '../../../core/data/community-data.service';
|
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||||
@@ -27,16 +19,35 @@ import { SubmissionJsonPatchOperationsService } from '../../../core/submission/s
|
|||||||
import { SubmissionJsonPatchOperationsServiceStub } from '../../../shared/testing/submission-json-patch-operations-service-stub';
|
import { SubmissionJsonPatchOperationsServiceStub } from '../../../shared/testing/submission-json-patch-operations-service-stub';
|
||||||
import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder';
|
import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder';
|
||||||
import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
||||||
|
import { Community } from '../../../core/shared/community.model';
|
||||||
|
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 { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import {
|
import { createTestComponent } from '../../../shared/testing/utils';
|
||||||
createTestComponent
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
} from '../../../shared/testing/utils';
|
|
||||||
import { cold } from 'jasmine-marbles';
|
const subcommunities = [Object.assign(new Community(), {
|
||||||
import { SearchResult } from '../../../+search-page/search-result.model';
|
name: 'SubCommunity 1',
|
||||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
id: '123456789-1',
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
metadata: [
|
||||||
|
{
|
||||||
|
key: 'dc.title',
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'SubCommunity 1'
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
Object.assign(new Community(), {
|
||||||
|
name: 'SubCommunity 1',
|
||||||
|
id: '123456789s-1',
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
key: 'dc.title',
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'SubCommunity 1'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
const mockCommunity1Collection1 = Object.assign(new Collection(), {
|
const mockCommunity1Collection1 = Object.assign(new Collection(), {
|
||||||
name: 'Community 1-Collection 1',
|
name: 'Community 1-Collection 1',
|
||||||
@@ -82,20 +93,54 @@ const mockCommunity2Collection2 = Object.assign(new Collection(), {
|
|||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
const collectionResults = [mockCommunity1Collection1, mockCommunity1Collection2, mockCommunity2Collection1, mockCommunity2Collection2].map((collection: Collection) => Object.assign(new SearchResult<Collection>(), { indexableObject: collection }));
|
const mockCommunity = Object.assign(new Community(), {
|
||||||
const searchService = {
|
name: 'Community 1',
|
||||||
search: () => {
|
id: '123456789-1',
|
||||||
return observableOf(new RemoteData(true, true, true,
|
metadata: [
|
||||||
undefined, new PaginatedList(new PageInfo(), collectionResults)))
|
{
|
||||||
}
|
key: 'dc.title',
|
||||||
};
|
language: 'en_US',
|
||||||
|
value: 'Community 1'
|
||||||
|
}],
|
||||||
|
collections: observableOf(new RemoteData(true, true, true,
|
||||||
|
undefined, new PaginatedList(new PageInfo(), [mockCommunity1Collection1, mockCommunity1Collection2]))),
|
||||||
|
subcommunities: observableOf(new RemoteData(true, true, true,
|
||||||
|
undefined, new PaginatedList(new PageInfo(), subcommunities))),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockCommunity2 = Object.assign(new Community(), {
|
||||||
|
name: 'Community 2',
|
||||||
|
id: '123456789-2',
|
||||||
|
metadata: [
|
||||||
|
{
|
||||||
|
key: 'dc.title',
|
||||||
|
language: 'en_US',
|
||||||
|
value: 'Community 2'
|
||||||
|
}],
|
||||||
|
collections: observableOf(new RemoteData(true, true, true,
|
||||||
|
undefined, new PaginatedList(new PageInfo(), [mockCommunity2Collection1, mockCommunity2Collection2]))),
|
||||||
|
subcommunities: observableOf(new RemoteData(true, true, true,
|
||||||
|
undefined, new PaginatedList(new PageInfo(), []))),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockCommunity1Collection1Rd = observableOf(new RemoteData(true, true, true,
|
||||||
|
undefined, mockCommunity1Collection1));
|
||||||
|
|
||||||
|
const mockCommunityList = observableOf(new RemoteData(true, true, true,
|
||||||
|
undefined, new PaginatedList(new PageInfo(), [mockCommunity, mockCommunity2])));
|
||||||
|
|
||||||
|
const mockCommunityCollectionList = observableOf(new RemoteData(true, true, true,
|
||||||
|
undefined, new PaginatedList(new PageInfo(), [mockCommunity1Collection1, mockCommunity1Collection2])));
|
||||||
|
|
||||||
|
const mockCommunity2CollectionList = observableOf(new RemoteData(true, true, true,
|
||||||
|
undefined, new PaginatedList(new PageInfo(), [mockCommunity2Collection1, mockCommunity2Collection2])));
|
||||||
|
|
||||||
const mockCollectionList = [
|
const mockCollectionList = [
|
||||||
{
|
{
|
||||||
communities: [
|
communities: [
|
||||||
{
|
{
|
||||||
id: 'c0e4de93-f506-4990-a840-d406f6f2ada7',
|
id: '123456789-1',
|
||||||
name: 'Submission test'
|
name: 'Community 1'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
collection: {
|
collection: {
|
||||||
@@ -106,8 +151,8 @@ const mockCollectionList = [
|
|||||||
{
|
{
|
||||||
communities: [
|
communities: [
|
||||||
{
|
{
|
||||||
id: 'c0e4de93-f506-4990-a840-d406f6f2ada7',
|
id: '123456789-1',
|
||||||
name: 'Submission test'
|
name: 'Community 1'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
collection: {
|
collection: {
|
||||||
@@ -118,8 +163,8 @@ const mockCollectionList = [
|
|||||||
{
|
{
|
||||||
communities: [
|
communities: [
|
||||||
{
|
{
|
||||||
id: 'c0e4de93-f506-4990-a840-d406f6f2ada7',
|
id: '123456789-2',
|
||||||
name: 'Submission test'
|
name: 'Community 2'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
collection: {
|
collection: {
|
||||||
@@ -130,8 +175,8 @@ const mockCollectionList = [
|
|||||||
{
|
{
|
||||||
communities: [
|
communities: [
|
||||||
{
|
{
|
||||||
id: 'c0e4de93-f506-4990-a840-d406f6f2ada7',
|
id: '123456789-2',
|
||||||
name: 'Submission test'
|
name: 'Community 2'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
collection: {
|
collection: {
|
||||||
@@ -158,6 +203,12 @@ describe('SubmissionFormCollectionComponent Component', () => {
|
|||||||
const communityDataService: any = jasmine.createSpyObj('communityDataService', {
|
const communityDataService: any = jasmine.createSpyObj('communityDataService', {
|
||||||
findAll: jasmine.createSpy('findAll')
|
findAll: jasmine.createSpy('findAll')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const collectionDataService: any = jasmine.createSpyObj('collectionDataService', {
|
||||||
|
findById: jasmine.createSpy('findById'),
|
||||||
|
getAuthorizedCollectionByCommunity: jasmine.createSpy('getAuthorizedCollectionByCommunity')
|
||||||
|
});
|
||||||
|
|
||||||
const store: any = jasmine.createSpyObj('store', {
|
const store: any = jasmine.createSpyObj('store', {
|
||||||
dispatch: jasmine.createSpy('dispatch'),
|
dispatch: jasmine.createSpy('dispatch'),
|
||||||
select: jasmine.createSpy('select')
|
select: jasmine.createSpy('select')
|
||||||
@@ -179,15 +230,12 @@ describe('SubmissionFormCollectionComponent Component', () => {
|
|||||||
TestComponent
|
TestComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{ provide: CollectionDataService, useValue: collectionDataService },
|
||||||
provide: SubmissionJsonPatchOperationsService,
|
{ provide: SubmissionJsonPatchOperationsService, useClass: SubmissionJsonPatchOperationsServiceStub },
|
||||||
useClass: SubmissionJsonPatchOperationsServiceStub
|
|
||||||
},
|
|
||||||
{ provide: SubmissionService, useClass: SubmissionServiceStub },
|
{ provide: SubmissionService, useClass: SubmissionServiceStub },
|
||||||
{ provide: CommunityDataService, useValue: communityDataService },
|
{ provide: CommunityDataService, useValue: communityDataService },
|
||||||
{ provide: JsonPatchOperationsBuilder, useValue: jsonPatchOpBuilder },
|
{ provide: JsonPatchOperationsBuilder, useValue: jsonPatchOpBuilder },
|
||||||
{ provide: Store, useValue: store },
|
{ provide: Store, useValue: store },
|
||||||
{ provide: SearchService, useValue: searchService },
|
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
SubmissionFormCollectionComponent
|
SubmissionFormCollectionComponent
|
||||||
],
|
],
|
||||||
@@ -252,17 +300,21 @@ describe('SubmissionFormCollectionComponent Component', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should init collection list properly', () => {
|
it('should init collection list properly', () => {
|
||||||
|
communityDataService.findAll.and.returnValue(mockCommunityList);
|
||||||
|
collectionDataService.findById.and.returnValue(mockCommunity1Collection1Rd);
|
||||||
|
collectionDataService.getAuthorizedCollectionByCommunity.and.returnValues(mockCommunityCollectionList, mockCommunity2CollectionList);
|
||||||
|
|
||||||
comp.ngOnChanges({
|
comp.ngOnChanges({
|
||||||
currentCollectionId: new SimpleChange(null, collectionId, true)
|
currentCollectionId: new SimpleChange(null, collectionId, true)
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(comp.searchListCollection$).toBeObservable(cold('(b)', {
|
expect(comp.searchListCollection$).toBeObservable(cold('(ab)', {
|
||||||
|
a: [],
|
||||||
b: mockCollectionList
|
b: mockCollectionList
|
||||||
}));
|
}));
|
||||||
|
|
||||||
expect(comp.selectedCollectionName$).toBeObservable(cold('(ab|)', {
|
expect(comp.selectedCollectionName$).toBeObservable(cold('(a|)', {
|
||||||
a: '',
|
a: 'Community 1-Collection 1'
|
||||||
b: 'Community 1-Collection 1'
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -394,8 +446,6 @@ class TestComponent {
|
|||||||
definitionId = 'traditional';
|
definitionId = 'traditional';
|
||||||
submissionId = mockSubmissionId;
|
submissionId = mockSubmissionId;
|
||||||
|
|
||||||
onCollectionChange = () => {
|
onCollectionChange = () => { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -17,13 +17,16 @@ import {
|
|||||||
distinctUntilChanged,
|
distinctUntilChanged,
|
||||||
filter,
|
filter,
|
||||||
find,
|
find,
|
||||||
|
flatMap,
|
||||||
map,
|
map,
|
||||||
mergeMap,
|
mergeMap,
|
||||||
|
reduce,
|
||||||
startWith
|
startWith
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { CommunityDataService } from '../../../core/data/community-data.service';
|
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||||
|
import { Community } from '../../../core/shared/community.model';
|
||||||
import { hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
|
import { hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner';
|
||||||
@@ -32,12 +35,8 @@ import { PaginatedList } from '../../../core/data/paginated-list';
|
|||||||
import { SubmissionService } from '../../submission.service';
|
import { SubmissionService } from '../../submission.service';
|
||||||
import { SubmissionObject } from '../../../core/submission/models/submission-object.model';
|
import { SubmissionObject } from '../../../core/submission/models/submission-object.model';
|
||||||
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
|
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
|
||||||
import { SearchService } from '../../../+search-page/search-service/search.service';
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
|
import { FindAllOptions } from '../../../core/data/request.models';
|
||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
|
||||||
import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model';
|
|
||||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
|
||||||
import { SearchResult } from '../../../+search-page/search-result.model';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface to represent a collection entry
|
* An interface to represent a collection entry
|
||||||
@@ -95,6 +94,12 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
*/
|
*/
|
||||||
public disabled$ = new BehaviorSubject<boolean>(true);
|
public disabled$ = new BehaviorSubject<boolean>(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if a collection change operation is processing
|
||||||
|
* @type {BehaviorSubject<boolean>}
|
||||||
|
*/
|
||||||
|
public processingChange$ = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The search form control
|
* The search form control
|
||||||
* @type {FormControl}
|
* @type {FormControl}
|
||||||
@@ -148,17 +153,17 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
*
|
*
|
||||||
* @param {ChangeDetectorRef} cdr
|
* @param {ChangeDetectorRef} cdr
|
||||||
* @param {CommunityDataService} communityDataService
|
* @param {CommunityDataService} communityDataService
|
||||||
|
* @param {CollectionDataService} collectionDataService
|
||||||
* @param {JsonPatchOperationsBuilder} operationsBuilder
|
* @param {JsonPatchOperationsBuilder} operationsBuilder
|
||||||
* @param {SubmissionJsonPatchOperationsService} operationsService
|
* @param {SubmissionJsonPatchOperationsService} operationsService
|
||||||
* @param {SubmissionService} submissionService
|
* @param {SubmissionService} submissionService
|
||||||
* @param {SearchService} searchService
|
|
||||||
*/
|
*/
|
||||||
constructor(protected cdr: ChangeDetectorRef,
|
constructor(protected cdr: ChangeDetectorRef,
|
||||||
private communityDataService: CommunityDataService,
|
private communityDataService: CommunityDataService,
|
||||||
|
private collectionDataService: CollectionDataService,
|
||||||
private operationsBuilder: JsonPatchOperationsBuilder,
|
private operationsBuilder: JsonPatchOperationsBuilder,
|
||||||
private operationsService: SubmissionJsonPatchOperationsService,
|
private operationsService: SubmissionJsonPatchOperationsService,
|
||||||
private submissionService: SubmissionService,
|
private submissionService: SubmissionService) {
|
||||||
private searchService: SearchService) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -195,40 +200,37 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
&& hasValue(changes.currentCollectionId.currentValue)) {
|
&& hasValue(changes.currentCollectionId.currentValue)) {
|
||||||
this.selectedCollectionId = this.currentCollectionId;
|
this.selectedCollectionId = this.currentCollectionId;
|
||||||
|
|
||||||
// // @TODO replace with search/top browse endpoint
|
this.selectedCollectionName$ = this.collectionDataService.findById(this.currentCollectionId).pipe(
|
||||||
// // @TODO implement community/subcommunity hierarchy
|
find((collectionRD: RemoteData<Collection>) => isNotEmpty(collectionRD.payload)),
|
||||||
// const communities$ = this.communityDataService.findAll().pipe(
|
map((collectionRD: RemoteData<Collection>) => collectionRD.payload.name)
|
||||||
// find((communities: RemoteData<PaginatedList<Community>>) => isNotEmpty(communities.payload)),
|
|
||||||
// mergeMap((communities: RemoteData<PaginatedList<Community>>) => communities.payload.page));
|
|
||||||
|
|
||||||
const listCollection$: Observable<CollectionListEntry[]> = this.searchService.search(
|
|
||||||
new PaginatedSearchOptions({
|
|
||||||
dsoType: DSpaceObjectType.COLLECTION,
|
|
||||||
pagination: new PaginationComponentOptions(),
|
|
||||||
scope: 'c0e4de93-f506-4990-a840-d406f6f2ada7'
|
|
||||||
})
|
|
||||||
).pipe(
|
|
||||||
getSucceededRemoteData(),
|
|
||||||
map((collections: RemoteData<PaginatedList<SearchResult<Collection>>>) => collections.payload.page),
|
|
||||||
filter((collectionData: Array<SearchResult<Collection>>) => isNotEmpty(collectionData)),
|
|
||||||
map((collectionData: Array<SearchResult<Collection>>) => {
|
|
||||||
return collectionData.map((collection: SearchResult<Collection>) => {
|
|
||||||
return {
|
|
||||||
communities: [{
|
|
||||||
id: 'c0e4de93-f506-4990-a840-d406f6f2ada7',
|
|
||||||
name: 'Submission test'
|
|
||||||
}],
|
|
||||||
collection: { id: collection.indexableObject.id, name: collection.indexableObject.name }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.selectedCollectionName$ = listCollection$.pipe(
|
const findOptions: FindAllOptions = {
|
||||||
map((collectionData: CollectionListEntry[]) => collectionData.find((entry: CollectionListEntry) => entry.collection.id === this.selectedCollectionId)),
|
elementsPerPage: 1000
|
||||||
filter((entry: CollectionListEntry) => hasValue(entry.collection)),
|
};
|
||||||
map((entry: CollectionListEntry) => entry.collection.name),
|
|
||||||
startWith('')
|
// Retrieve collection list only when is the first change
|
||||||
|
if (changes.currentCollectionId.isFirstChange()) {
|
||||||
|
// @TODO replace with search/top browse endpoint
|
||||||
|
// @TODO implement community/subcommunity hierarchy
|
||||||
|
const communities$ = this.communityDataService.findAll(findOptions).pipe(
|
||||||
|
find((communities: RemoteData<PaginatedList<Community>>) => isNotEmpty(communities.payload)),
|
||||||
|
mergeMap((communities: RemoteData<PaginatedList<Community>>) => communities.payload.page));
|
||||||
|
|
||||||
|
const listCollection$ = communities$.pipe(
|
||||||
|
flatMap((communityData: Community) => {
|
||||||
|
return this.collectionDataService.getAuthorizedCollectionByCommunity(communityData.uuid, findOptions).pipe(
|
||||||
|
find((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending && collections.hasSucceeded),
|
||||||
|
mergeMap((collections: RemoteData<PaginatedList<Collection>>) => collections.payload.page),
|
||||||
|
filter((collectionData: Collection) => isNotEmpty(collectionData)),
|
||||||
|
map((collectionData: Collection) => ({
|
||||||
|
communities: [{ id: communityData.id, name: communityData.name }],
|
||||||
|
collection: { id: collectionData.id, name: collectionData.name }
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
reduce((acc: any, value: any) => [...acc, ...value], []),
|
||||||
|
startWith([])
|
||||||
);
|
);
|
||||||
|
|
||||||
const searchTerm$ = this.searchField.valueChanges.pipe(
|
const searchTerm$ = this.searchField.valueChanges.pipe(
|
||||||
@@ -248,6 +250,7 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize all instance variables
|
* Initialize all instance variables
|
||||||
@@ -271,7 +274,7 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
*/
|
*/
|
||||||
onSelect(event) {
|
onSelect(event) {
|
||||||
this.searchField.reset();
|
this.searchField.reset();
|
||||||
this.disabled$.next(true);
|
this.processingChange$.next(true);
|
||||||
this.operationsBuilder.replace(this.pathCombiner.getPath(), event.collection.id, true);
|
this.operationsBuilder.replace(this.pathCombiner.getPath(), event.collection.id, true);
|
||||||
this.subs.push(this.operationsService.jsonPatchByResourceID(
|
this.subs.push(this.operationsService.jsonPatchByResourceID(
|
||||||
this.submissionService.getSubmissionObjectLinkName(),
|
this.submissionService.getSubmissionObjectLinkName(),
|
||||||
@@ -283,7 +286,7 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
|
|||||||
this.selectedCollectionName$ = observableOf(event.collection.name);
|
this.selectedCollectionName$ = observableOf(event.collection.name);
|
||||||
this.collectionChange.emit(submissionObject[0]);
|
this.collectionChange.emit(submissionObject[0]);
|
||||||
this.submissionService.changeSubmissionCollection(this.submissionId, event.collection.id);
|
this.submissionService.changeSubmissionCollection(this.submissionId, event.collection.id);
|
||||||
this.disabled$.next(false);
|
this.processingChange$.next(false);
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@@ -361,7 +361,7 @@ const addError = (state: SubmissionObjectState, action: InertSectionErrorsAction
|
|||||||
* @param state
|
* @param state
|
||||||
* the current state
|
* the current state
|
||||||
* @param action
|
* @param action
|
||||||
* an RemoveSectionErrorsAction
|
* a RemoveSectionErrorsAction
|
||||||
* @return SubmissionObjectState
|
* @return SubmissionObjectState
|
||||||
* the new state, with the section's errors updated.
|
* the new state, with the section's errors updated.
|
||||||
*/
|
*/
|
||||||
@@ -416,7 +416,7 @@ function initSubmission(state: SubmissionObjectState, action: InitSubmissionForm
|
|||||||
* @param state
|
* @param state
|
||||||
* the current state
|
* the current state
|
||||||
* @param action
|
* @param action
|
||||||
* an ResetSubmissionFormAction
|
* a ResetSubmissionFormAction
|
||||||
* @return SubmissionObjectState
|
* @return SubmissionObjectState
|
||||||
* the new state, with the section removed.
|
* the new state, with the section removed.
|
||||||
*/
|
*/
|
||||||
@@ -439,7 +439,7 @@ function resetSubmission(state: SubmissionObjectState, action: ResetSubmissionFo
|
|||||||
* @param state
|
* @param state
|
||||||
* the current state
|
* the current state
|
||||||
* @param action
|
* @param action
|
||||||
* an CompleteInitSubmissionFormAction
|
* a CompleteInitSubmissionFormAction
|
||||||
* @return SubmissionObjectState
|
* @return SubmissionObjectState
|
||||||
* the new state, with the section removed.
|
* the new state, with the section removed.
|
||||||
*/
|
*/
|
||||||
@@ -461,7 +461,7 @@ function completeInit(state: SubmissionObjectState, action: CompleteInitSubmissi
|
|||||||
* @param state
|
* @param state
|
||||||
* the current state
|
* the current state
|
||||||
* @param action
|
* @param action
|
||||||
* an SaveSubmissionFormAction | SaveSubmissionSectionFormAction
|
* a SaveSubmissionFormAction | SaveSubmissionSectionFormAction
|
||||||
* | SaveForLaterSubmissionFormAction | SaveAndDepositSubmissionAction
|
* | SaveForLaterSubmissionFormAction | SaveAndDepositSubmissionAction
|
||||||
* @return SubmissionObjectState
|
* @return SubmissionObjectState
|
||||||
* the new state, with the flag set to true.
|
* the new state, with the flag set to true.
|
||||||
@@ -491,7 +491,7 @@ function saveSubmission(state: SubmissionObjectState,
|
|||||||
* @param state
|
* @param state
|
||||||
* the current state
|
* the current state
|
||||||
* @param action
|
* @param action
|
||||||
* an SaveSubmissionFormSuccessAction | SaveForLaterSubmissionFormSuccessAction
|
* a SaveSubmissionFormSuccessAction | SaveForLaterSubmissionFormSuccessAction
|
||||||
* | SaveSubmissionSectionFormSuccessAction | SaveSubmissionFormErrorAction
|
* | SaveSubmissionSectionFormSuccessAction | SaveSubmissionFormErrorAction
|
||||||
* | SaveForLaterSubmissionFormErrorAction | SaveSubmissionSectionFormErrorAction
|
* | SaveForLaterSubmissionFormErrorAction | SaveSubmissionSectionFormErrorAction
|
||||||
* @return SubmissionObjectState
|
* @return SubmissionObjectState
|
||||||
@@ -521,7 +521,7 @@ function completeSave(state: SubmissionObjectState,
|
|||||||
* @param state
|
* @param state
|
||||||
* the current state
|
* the current state
|
||||||
* @param action
|
* @param action
|
||||||
* an DepositSubmissionAction
|
* a DepositSubmissionAction
|
||||||
* @return SubmissionObjectState
|
* @return SubmissionObjectState
|
||||||
* the new state, with the deposit flag changed.
|
* the new state, with the deposit flag changed.
|
||||||
*/
|
*/
|
||||||
@@ -544,7 +544,7 @@ function startDeposit(state: SubmissionObjectState, action: DepositSubmissionAct
|
|||||||
* @param state
|
* @param state
|
||||||
* the current state
|
* the current state
|
||||||
* @param action
|
* @param action
|
||||||
* an DepositSubmissionSuccessAction or DepositSubmissionErrorAction
|
* a DepositSubmissionSuccessAction or a DepositSubmissionErrorAction
|
||||||
* @return SubmissionObjectState
|
* @return SubmissionObjectState
|
||||||
* the new state, with the deposit flag changed.
|
* the new state, with the deposit flag changed.
|
||||||
*/
|
*/
|
||||||
@@ -586,7 +586,7 @@ function changeCollection(state: SubmissionObjectState, action: ChangeSubmission
|
|||||||
* @param state
|
* @param state
|
||||||
* the current state
|
* the current state
|
||||||
* @param action
|
* @param action
|
||||||
* an SetActiveSectionAction
|
* a SetActiveSectionAction
|
||||||
* @return SubmissionObjectState
|
* @return SubmissionObjectState
|
||||||
* the new state, with the active section.
|
* the new state, with the active section.
|
||||||
*/
|
*/
|
||||||
@@ -676,7 +676,7 @@ function updateSectionData(state: SubmissionObjectState, action: UpdateSectionDa
|
|||||||
* @param state
|
* @param state
|
||||||
* the current state
|
* the current state
|
||||||
* @param action
|
* @param action
|
||||||
* an DisableSectionAction
|
* a DisableSectionAction
|
||||||
* @param enabled
|
* @param enabled
|
||||||
* enabled or disabled section.
|
* enabled or disabled section.
|
||||||
* @return SubmissionObjectState
|
* @return SubmissionObjectState
|
||||||
@@ -705,7 +705,7 @@ function changeSectionState(state: SubmissionObjectState, action: EnableSectionA
|
|||||||
* @param state
|
* @param state
|
||||||
* the current state
|
* the current state
|
||||||
* @param action
|
* @param action
|
||||||
* an SectionStatusChangeAction
|
* a SectionStatusChangeAction
|
||||||
* @return SubmissionObjectState
|
* @return SubmissionObjectState
|
||||||
* the new state, with the section new validity status.
|
* the new state, with the section new validity status.
|
||||||
*/
|
*/
|
||||||
@@ -769,7 +769,7 @@ function newFile(state: SubmissionObjectState, action: NewUploadedFileAction): S
|
|||||||
* @param state
|
* @param state
|
||||||
* the current state
|
* the current state
|
||||||
* @param action
|
* @param action
|
||||||
* a EditFileDataAction action
|
* an EditFileDataAction action
|
||||||
* @return SubmissionObjectState
|
* @return SubmissionObjectState
|
||||||
* the new state, with the edited file.
|
* the new state, with the edited file.
|
||||||
*/
|
*/
|
||||||
|
@@ -64,6 +64,12 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
|
|||||||
*/
|
*/
|
||||||
public isLoading = true;
|
public isLoading = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map representing all field on their way to be removed
|
||||||
|
* @type {Map}
|
||||||
|
*/
|
||||||
|
protected fieldsOnTheirWayToBeRemoved: Map<string, number[]> = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The form config
|
* The form config
|
||||||
* @type {SubmissionFormsModel}
|
* @type {SubmissionFormsModel}
|
||||||
@@ -295,6 +301,7 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
|
|||||||
}),
|
}),
|
||||||
distinctUntilChanged())
|
distinctUntilChanged())
|
||||||
.subscribe((sectionState: SubmissionSectionObject) => {
|
.subscribe((sectionState: SubmissionSectionObject) => {
|
||||||
|
this.fieldsOnTheirWayToBeRemoved = new Map();
|
||||||
this.updateForm(sectionState.data as WorkspaceitemSectionFormObject, sectionState.errors);
|
this.updateForm(sectionState.data as WorkspaceitemSectionFormObject, sectionState.errors);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -348,11 +355,24 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
|
|||||||
* the [[DynamicFormControlEvent]] emitted
|
* the [[DynamicFormControlEvent]] emitted
|
||||||
*/
|
*/
|
||||||
onRemove(event: DynamicFormControlEvent): void {
|
onRemove(event: DynamicFormControlEvent): void {
|
||||||
|
const fieldId = this.formBuilderService.getId(event.model);
|
||||||
|
const fieldIndex = this.formOperationsService.getArrayIndexFromEvent(event);
|
||||||
|
|
||||||
|
// Keep track that this field will be removed
|
||||||
|
if (this.fieldsOnTheirWayToBeRemoved.has(fieldId)) {
|
||||||
|
const indexes = this.fieldsOnTheirWayToBeRemoved.get(fieldId);
|
||||||
|
indexes.push(fieldIndex);
|
||||||
|
this.fieldsOnTheirWayToBeRemoved.set(fieldId, indexes);
|
||||||
|
} else {
|
||||||
|
this.fieldsOnTheirWayToBeRemoved.set(fieldId, [fieldIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
this.formOperationsService.dispatchOperationsFromEvent(
|
this.formOperationsService.dispatchOperationsFromEvent(
|
||||||
this.pathCombiner,
|
this.pathCombiner,
|
||||||
event,
|
event,
|
||||||
this.previousValue,
|
this.previousValue,
|
||||||
this.hasStoredValue(this.formBuilderService.getId(event.model), this.formOperationsService.getArrayIndexFromEvent(event)));
|
this.hasStoredValue(fieldId, fieldIndex));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -365,9 +385,23 @@ export class SubmissionSectionformComponent extends SectionModelComponent {
|
|||||||
*/
|
*/
|
||||||
hasStoredValue(fieldId, index): boolean {
|
hasStoredValue(fieldId, index): boolean {
|
||||||
if (isNotEmpty(this.sectionData.data)) {
|
if (isNotEmpty(this.sectionData.data)) {
|
||||||
return this.sectionData.data.hasOwnProperty(fieldId) && isNotEmpty(this.sectionData.data[fieldId][index]);
|
return this.sectionData.data.hasOwnProperty(fieldId) &&
|
||||||
|
isNotEmpty(this.sectionData.data[fieldId][index]) &&
|
||||||
|
!this.isFieldToRemove(fieldId, index);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the specified field is on the way to be removed
|
||||||
|
*
|
||||||
|
* @param fieldId
|
||||||
|
* the section data retrieved from the serverù
|
||||||
|
* @param index
|
||||||
|
* the section data retrieved from the server
|
||||||
|
*/
|
||||||
|
isFieldToRemove(fieldId, index) {
|
||||||
|
return this.fieldsOnTheirWayToBeRemoved.has(fieldId) && this.fieldsOnTheirWayToBeRemoved.get(fieldId).includes(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -155,14 +155,14 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
|
|||||||
filter((submissionObject: SubmissionObjectEntry) => isUndefined(this.collectionId) || this.collectionId !== submissionObject.collection),
|
filter((submissionObject: SubmissionObjectEntry) => isUndefined(this.collectionId) || this.collectionId !== submissionObject.collection),
|
||||||
tap((submissionObject: SubmissionObjectEntry) => this.collectionId = submissionObject.collection),
|
tap((submissionObject: SubmissionObjectEntry) => this.collectionId = submissionObject.collection),
|
||||||
flatMap((submissionObject: SubmissionObjectEntry) => this.collectionDataService.findById(submissionObject.collection)),
|
flatMap((submissionObject: SubmissionObjectEntry) => this.collectionDataService.findById(submissionObject.collection)),
|
||||||
find((rd: RemoteData<Collection>) => isNotUndefined((rd.payload))),
|
filter((rd: RemoteData<Collection>) => isNotUndefined((rd.payload))),
|
||||||
tap((collectionRemoteData: RemoteData<Collection>) => this.collectionName = collectionRemoteData.payload.name),
|
tap((collectionRemoteData: RemoteData<Collection>) => this.collectionName = collectionRemoteData.payload.name),
|
||||||
flatMap((collectionRemoteData: RemoteData<Collection>) => {
|
flatMap((collectionRemoteData: RemoteData<Collection>) => {
|
||||||
return this.resourcePolicyService.findByHref(
|
return this.resourcePolicyService.findByHref(
|
||||||
(collectionRemoteData.payload as any)._links.defaultAccessConditions
|
(collectionRemoteData.payload as any)._links.defaultAccessConditions
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
find((defaultAccessConditionsRemoteData: RemoteData<ResourcePolicy>) =>
|
filter((defaultAccessConditionsRemoteData: RemoteData<ResourcePolicy>) =>
|
||||||
defaultAccessConditionsRemoteData.hasSucceeded),
|
defaultAccessConditionsRemoteData.hasSucceeded),
|
||||||
tap((defaultAccessConditionsRemoteData: RemoteData<ResourcePolicy>) => {
|
tap((defaultAccessConditionsRemoteData: RemoteData<ResourcePolicy>) => {
|
||||||
if (isNotEmpty(defaultAccessConditionsRemoteData.payload)) {
|
if (isNotEmpty(defaultAccessConditionsRemoteData.payload)) {
|
||||||
@@ -171,7 +171,6 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
flatMap(() => config$),
|
flatMap(() => config$),
|
||||||
take(1),
|
|
||||||
flatMap((config: SubmissionUploadsModel) => {
|
flatMap((config: SubmissionUploadsModel) => {
|
||||||
this.availableAccessConditionOptions = isNotEmpty(config.accessConditionOptions) ? config.accessConditionOptions : [];
|
this.availableAccessConditionOptions = isNotEmpty(config.accessConditionOptions) ? config.accessConditionOptions : [];
|
||||||
|
|
||||||
|
@@ -197,7 +197,11 @@ export class SubmissionService {
|
|||||||
* The submission id
|
* The submission id
|
||||||
*/
|
*/
|
||||||
dispatchSave(submissionId) {
|
dispatchSave(submissionId) {
|
||||||
|
this.getSubmissionSaveProcessingStatus(submissionId).pipe(
|
||||||
|
find((isPending: boolean) => !isPending)
|
||||||
|
).subscribe(() => {
|
||||||
this.store.dispatch(new SaveSubmissionFormAction(submissionId));
|
this.store.dispatch(new SaveSubmissionFormAction(submissionId));
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -64,7 +64,7 @@ export function createTranslateLoader() {
|
|||||||
{
|
{
|
||||||
provide: SubmissionService,
|
provide: SubmissionService,
|
||||||
useClass: ServerSubmissionService
|
useClass: ServerSubmissionService
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ServerAppModule {
|
export class ServerAppModule {
|
||||||
|
@@ -67,7 +67,7 @@ export function startServer(bootstrap: Type<{}> | NgModuleFactory<{}>) {
|
|||||||
|
|
||||||
function onHandleError(parentZoneDelegate, currentZone, targetZone, error) {
|
function onHandleError(parentZoneDelegate, currentZone, targetZone, error) {
|
||||||
if (!res._headerSent) {
|
if (!res._headerSent) {
|
||||||
console.warn('Error in SSR, serving for direct CSR');
|
console.warn('Error in SSR, serving for direct CSR. Error details : ', error);
|
||||||
res.sendFile('index.csr.html', { root: './src' });
|
res.sendFile('index.csr.html', { root: './src' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@ $drop-zone-area-inner-z-index: 1021;
|
|||||||
$login-logo-height:72px;
|
$login-logo-height:72px;
|
||||||
$login-logo-width:72px;
|
$login-logo-width:72px;
|
||||||
$submission-header-z-index: 1001;
|
$submission-header-z-index: 1001;
|
||||||
$submission-footer-z-index: 1000;
|
$submission-footer-z-index: 999;
|
||||||
|
|
||||||
$main-z-index: 0;
|
$main-z-index: 0;
|
||||||
$nav-z-index: 10;
|
$nav-z-index: 10;
|
||||||
|
Reference in New Issue
Block a user