mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-17 15:03:07 +00:00
Merge remote-tracking branch 'origin/main' into CST-4506_item_embargo
This commit is contained in:
@@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule, FormArray, FormControl, FormGroup,Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
@@ -34,6 +34,7 @@ import { TranslateLoaderMock } from '../../../shared/testing/translate-loader.mo
|
||||
import { RouterMock } from '../../../shared/mocks/router.mock';
|
||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { ValidateGroupExists } from './validators/group-exists.validator';
|
||||
|
||||
describe('GroupFormComponent', () => {
|
||||
let component: GroupFormComponent;
|
||||
@@ -117,7 +118,69 @@ describe('GroupFormComponent', () => {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
builderService = getMockFormBuilderService();
|
||||
builderService = Object.assign(getMockFormBuilderService(),{
|
||||
createFormGroup(formModel, options = null) {
|
||||
const controls = {};
|
||||
formModel.forEach( model => {
|
||||
model.parent = parent;
|
||||
const controlModel = model;
|
||||
const controlState = { value: controlModel.value, disabled: controlModel.disabled };
|
||||
const controlOptions = this.createAbstractControlOptions(controlModel.validators, controlModel.asyncValidators, controlModel.updateOn);
|
||||
controls[model.id] = new FormControl(controlState, controlOptions);
|
||||
});
|
||||
return new FormGroup(controls, options);
|
||||
},
|
||||
createAbstractControlOptions(validatorsConfig = null, asyncValidatorsConfig = null, updateOn = null) {
|
||||
return {
|
||||
validators: validatorsConfig !== null ? this.getValidators(validatorsConfig) : null,
|
||||
};
|
||||
},
|
||||
getValidators(validatorsConfig) {
|
||||
return this.getValidatorFns(validatorsConfig);
|
||||
},
|
||||
getValidatorFns(validatorsConfig, validatorsToken = this._NG_VALIDATORS) {
|
||||
let validatorFns = [];
|
||||
if (this.isObject(validatorsConfig)) {
|
||||
validatorFns = Object.keys(validatorsConfig).map(validatorConfigKey => {
|
||||
const validatorConfigValue = validatorsConfig[validatorConfigKey];
|
||||
if (this.isValidatorDescriptor(validatorConfigValue)) {
|
||||
const descriptor = validatorConfigValue;
|
||||
return this.getValidatorFn(descriptor.name, descriptor.args, validatorsToken);
|
||||
}
|
||||
return this.getValidatorFn(validatorConfigKey, validatorConfigValue, validatorsToken);
|
||||
});
|
||||
}
|
||||
return validatorFns;
|
||||
},
|
||||
getValidatorFn(validatorName, validatorArgs = null, validatorsToken = this._NG_VALIDATORS) {
|
||||
let validatorFn;
|
||||
if (Validators.hasOwnProperty(validatorName)) { // Built-in Angular Validators
|
||||
validatorFn = Validators[validatorName];
|
||||
} else { // Custom Validators
|
||||
if (this._DYNAMIC_VALIDATORS && this._DYNAMIC_VALIDATORS.has(validatorName)) {
|
||||
validatorFn = this._DYNAMIC_VALIDATORS.get(validatorName);
|
||||
} else if (validatorsToken) {
|
||||
validatorFn = validatorsToken.find(validator => validator.name === validatorName);
|
||||
}
|
||||
}
|
||||
if (validatorFn === undefined) { // throw when no validator could be resolved
|
||||
throw new Error(`validator '${validatorName}' is not provided via NG_VALIDATORS, NG_ASYNC_VALIDATORS or DYNAMIC_FORM_VALIDATORS`);
|
||||
}
|
||||
if (validatorArgs !== null) {
|
||||
return validatorFn(validatorArgs);
|
||||
}
|
||||
return validatorFn;
|
||||
},
|
||||
isValidatorDescriptor(value) {
|
||||
if (this.isObject(value)) {
|
||||
return value.hasOwnProperty('name') && value.hasOwnProperty('args');
|
||||
}
|
||||
return false;
|
||||
},
|
||||
isObject(value) {
|
||||
return typeof value === 'object' && value !== null;
|
||||
}
|
||||
});
|
||||
translateService = getMockTranslateService();
|
||||
router = new RouterMock();
|
||||
notificationService = new NotificationsServiceStub();
|
||||
@@ -217,4 +280,72 @@ describe('GroupFormComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('check form validation', () => {
|
||||
let groupCommunity;
|
||||
|
||||
beforeEach(() => {
|
||||
groupName = 'testName';
|
||||
groupCommunity = 'testgroupCommunity';
|
||||
groupDescription = 'testgroupDescription';
|
||||
|
||||
expected = Object.assign(new Group(), {
|
||||
name: groupName,
|
||||
metadata: {
|
||||
'dc.description': [
|
||||
{
|
||||
value: groupDescription
|
||||
}
|
||||
],
|
||||
},
|
||||
});
|
||||
spyOn(component.submitForm, 'emit');
|
||||
|
||||
fixture.detectChanges();
|
||||
component.initialisePage();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
describe('groupName, groupCommunity and groupDescription should be required', () => {
|
||||
it('form should be invalid because the groupName is required', waitForAsync(() => {
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.formGroup.controls.groupName.valid).toBeFalse();
|
||||
expect(component.formGroup.controls.groupName.errors.required).toBeTrue();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('after inserting information groupName,groupCommunity and groupDescription not required', () => {
|
||||
beforeEach(() => {
|
||||
component.formGroup.controls.groupName.setValue('test');
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('groupName should be valid because the groupName is set', waitForAsync(() => {
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.formGroup.controls.groupName.valid).toBeTrue();
|
||||
expect(component.formGroup.controls.groupName.errors).toBeNull();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('after already utilized groupName', () => {
|
||||
beforeEach(() => {
|
||||
const groupsDataServiceStubWithGroup = Object.assign(groupsDataServiceStub,{
|
||||
searchGroups(query: string): Observable<RemoteData<PaginatedList<Group>>> {
|
||||
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [expected]));
|
||||
}
|
||||
});
|
||||
component.formGroup.controls.groupName.setValue('testName');
|
||||
component.formGroup.controls.groupName.setAsyncValidators(ValidateGroupExists.createValidator(groupsDataServiceStubWithGroup));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('groupName should not be valid because groupName is already taken', waitForAsync(() => {
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.formGroup.controls.groupName.valid).toBeFalse();
|
||||
expect(component.formGroup.controls.groupName.errors.groupExists).toBeTruthy();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component, EventEmitter, HostListener, OnDestroy, OnInit, Output } from '@angular/core';
|
||||
import { Component, EventEmitter, HostListener, OnDestroy, OnInit, Output, ChangeDetectorRef } from '@angular/core';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
of as observableOf,
|
||||
Subscription,
|
||||
} from 'rxjs';
|
||||
import { catchError, map, switchMap, take, filter } from 'rxjs/operators';
|
||||
import { catchError, map, switchMap, take, filter, debounceTime } from 'rxjs/operators';
|
||||
import { getCollectionEditRolesRoute } from '../../../collection-page/collection-page-routing-paths';
|
||||
import { getCommunityEditRolesRoute } from '../../../community-page/community-page-routing-paths';
|
||||
import { DSpaceObjectDataService } from '../../../core/data/dspace-object-data.service';
|
||||
@@ -45,6 +45,7 @@ import { NotificationsService } from '../../../shared/notifications/notification
|
||||
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||
import { NoContent } from '../../../core/shared/NoContent.model';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { ValidateGroupExists } from './validators/group-exists.validator';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-group-form',
|
||||
@@ -126,6 +127,12 @@ export class GroupFormComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
public AlertTypeEnum = AlertType;
|
||||
|
||||
/**
|
||||
* Subscription to email field value change
|
||||
*/
|
||||
groupNameValueChangeSubscribe: Subscription;
|
||||
|
||||
|
||||
constructor(public groupDataService: GroupDataService,
|
||||
private ePersonDataService: EPersonDataService,
|
||||
private dSpaceObjectDataService: DSpaceObjectDataService,
|
||||
@@ -136,7 +143,8 @@ export class GroupFormComponent implements OnInit, OnDestroy {
|
||||
protected router: Router,
|
||||
private authorizationService: AuthorizationDataService,
|
||||
private modalService: NgbModal,
|
||||
public requestService: RequestService) {
|
||||
public requestService: RequestService,
|
||||
protected changeDetectorRef: ChangeDetectorRef) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -192,6 +200,14 @@ export class GroupFormComponent implements OnInit, OnDestroy {
|
||||
this.groupDescription,
|
||||
];
|
||||
this.formGroup = this.formBuilderService.createFormGroup(this.formModel);
|
||||
|
||||
if (!!this.formGroup.controls.groupName) {
|
||||
this.formGroup.controls.groupName.setAsyncValidators(ValidateGroupExists.createValidator(this.groupDataService));
|
||||
this.groupNameValueChangeSubscribe = this.groupName.valueChanges.pipe(debounceTime(300)).subscribe(() => {
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
this.subs.push(
|
||||
observableCombineLatest(
|
||||
this.groupDataService.getActiveGroup(),
|
||||
@@ -201,6 +217,10 @@ export class GroupFormComponent implements OnInit, OnDestroy {
|
||||
).subscribe(([activeGroup, canEdit, linkedObject]) => {
|
||||
|
||||
if (activeGroup != null) {
|
||||
|
||||
// Disable group name exists validator
|
||||
this.formGroup.controls.groupName.clearAsyncValidators();
|
||||
|
||||
this.groupBeingEdited = activeGroup;
|
||||
|
||||
if (linkedObject?.name) {
|
||||
@@ -436,6 +456,11 @@ export class GroupFormComponent implements OnInit, OnDestroy {
|
||||
ngOnDestroy(): void {
|
||||
this.groupDataService.cancelEditGroup();
|
||||
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
||||
|
||||
if ( hasValue(this.groupNameValueChangeSubscribe) ) {
|
||||
this.groupNameValueChangeSubscribe.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -0,0 +1,33 @@
|
||||
import { AbstractControl, ValidationErrors } from '@angular/forms';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map} from 'rxjs/operators';
|
||||
|
||||
import { GroupDataService } from '../../../../core/eperson/group-data.service';
|
||||
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
|
||||
import { Group } from '../../../../core/eperson/models/group.model';
|
||||
|
||||
export class ValidateGroupExists {
|
||||
|
||||
/**
|
||||
* This method will create the validator with the groupDataService requested from component
|
||||
* @param groupDataService the service with DI in the component that this validator is being utilized.
|
||||
* @return Observable<ValidationErrors | null>
|
||||
*/
|
||||
static createValidator(groupDataService: GroupDataService) {
|
||||
return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
|
||||
return groupDataService.searchGroups(control.value, {
|
||||
currentPage: 1,
|
||||
elementsPerPage: 100
|
||||
})
|
||||
.pipe(
|
||||
getFirstSucceededRemoteListPayload(),
|
||||
map( (groups: Group[]) => {
|
||||
return groups.filter(group => group.name === control.value);
|
||||
}),
|
||||
map( (groups: Group[]) => {
|
||||
return groups.length > 0 ? { groupExists: true } : null;
|
||||
}),
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
@@ -36,7 +36,7 @@ const ENTRY_COMPONENTS = [
|
||||
export class AdminSearchModule {
|
||||
/**
|
||||
* NOTE: this method allows to resolve issue with components that using a custom decorator
|
||||
* which are not loaded during CSR otherwise
|
||||
* which are not loaded during SSR otherwise
|
||||
*/
|
||||
static withEntryComponents() {
|
||||
return {
|
||||
|
@@ -28,7 +28,7 @@ const ENTRY_COMPONENTS = [
|
||||
export class AdminWorkflowModuleModule {
|
||||
/**
|
||||
* NOTE: this method allows to resolve issue with components that using a custom decorator
|
||||
* which are not loaded during CSR otherwise
|
||||
* which are not loaded during SSR otherwise
|
||||
*/
|
||||
static withEntryComponents() {
|
||||
return {
|
||||
|
@@ -34,7 +34,7 @@ const ENTRY_COMPONENTS = [
|
||||
export class AdminModule {
|
||||
/**
|
||||
* NOTE: this method allows to resolve issue with components that using a custom decorator
|
||||
* which are not loaded during CSR otherwise
|
||||
* which are not loaded during SSR otherwise
|
||||
*/
|
||||
static withEntryComponents() {
|
||||
return {
|
||||
|
@@ -202,8 +202,8 @@ import { GroupAdministratorGuard } from './core/data/feature-authorization/featu
|
||||
{ path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent },
|
||||
]}
|
||||
],{
|
||||
onSameUrlNavigation: 'reload',
|
||||
})
|
||||
onSameUrlNavigation: 'reload',
|
||||
})
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
|
@@ -171,7 +171,8 @@ describe('App component', () => {
|
||||
TestBed.configureTestingModule(getDefaultTestBedConf());
|
||||
TestBed.overrideProvider(ThemeService, {useValue: getMockThemeService('custom')});
|
||||
document = TestBed.inject(DOCUMENT);
|
||||
headSpy = jasmine.createSpyObj('head', ['appendChild']);
|
||||
headSpy = jasmine.createSpyObj('head', ['appendChild', 'getElementsByClassName']);
|
||||
headSpy.getElementsByClassName.and.returnValue([]);
|
||||
spyOn(document, 'getElementsByTagName').and.returnValue([headSpy]);
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
comp = fixture.componentInstance;
|
||||
|
@@ -31,12 +31,12 @@ import { AuthService } from './core/auth/auth.service';
|
||||
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
||||
import { MenuService } from './shared/menu/menu.service';
|
||||
import { HostWindowService } from './shared/host-window.service';
|
||||
import { ThemeConfig } from '../config/theme.model';
|
||||
import { HeadTagConfig, ThemeConfig } from '../config/theme.model';
|
||||
import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';
|
||||
import { environment } from '../environments/environment';
|
||||
import { models } from './core/core.module';
|
||||
import { LocaleService } from './core/locale/locale.service';
|
||||
import { hasValue, isNotEmpty } from './shared/empty.util';
|
||||
import { hasNoValue, hasValue, isNotEmpty } from './shared/empty.util';
|
||||
import { KlaroService } from './shared/cookies/klaro.service';
|
||||
import { GoogleAnalyticsService } from './statistics/google-analytics.service';
|
||||
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
|
||||
@@ -115,11 +115,11 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
this.isThemeCSSLoading$.next(true);
|
||||
}
|
||||
if (hasValue(themeName)) {
|
||||
this.setThemeCss(themeName);
|
||||
this.loadGlobalThemeConfig(themeName);
|
||||
} else if (hasValue(DEFAULT_THEME_CONFIG)) {
|
||||
this.setThemeCss(DEFAULT_THEME_CONFIG.name);
|
||||
this.loadGlobalThemeConfig(DEFAULT_THEME_CONFIG.name);
|
||||
} else {
|
||||
this.setThemeCss(BASE_THEME_NAME);
|
||||
this.loadGlobalThemeConfig(BASE_THEME_NAME);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -233,6 +233,11 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
private loadGlobalThemeConfig(themeName: string): void {
|
||||
this.setThemeCss(themeName);
|
||||
this.setHeadTags(themeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the theme css file in <head>
|
||||
*
|
||||
@@ -241,9 +246,13 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
*/
|
||||
private setThemeCss(themeName: string): void {
|
||||
const head = this.document.getElementsByTagName('head')[0];
|
||||
if (hasNoValue(head)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Array.from to ensure we end up with an array, not an HTMLCollection, which would be
|
||||
// automatically updated if we add nodes later
|
||||
const currentThemeLinks = Array.from(this.document.getElementsByClassName('theme-css'));
|
||||
const currentThemeLinks = Array.from(head.getElementsByClassName('theme-css'));
|
||||
const link = this.document.createElement('link');
|
||||
link.setAttribute('rel', 'stylesheet');
|
||||
link.setAttribute('type', 'text/css');
|
||||
@@ -265,6 +274,78 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
head.appendChild(link);
|
||||
}
|
||||
|
||||
private setHeadTags(themeName: string): void {
|
||||
const head = this.document.getElementsByTagName('head')[0];
|
||||
if (hasNoValue(head)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// clear head tags
|
||||
const currentHeadTags = Array.from(head.getElementsByClassName('theme-head-tag'));
|
||||
if (hasValue(currentHeadTags)) {
|
||||
currentHeadTags.forEach((currentHeadTag: any) => currentHeadTag.remove());
|
||||
}
|
||||
|
||||
// create new head tags (not yet added to DOM)
|
||||
const headTagFragment = this.document.createDocumentFragment();
|
||||
this.createHeadTags(themeName)
|
||||
.forEach(newHeadTag => headTagFragment.appendChild(newHeadTag));
|
||||
|
||||
// add new head tags to DOM
|
||||
head.appendChild(headTagFragment);
|
||||
}
|
||||
|
||||
private createHeadTags(themeName: string): HTMLElement[] {
|
||||
const themeConfig = this.themeService.getThemeConfigFor(themeName);
|
||||
const headTagConfigs = themeConfig?.headTags;
|
||||
|
||||
if (hasNoValue(headTagConfigs)) {
|
||||
const parentThemeName = themeConfig?.extends;
|
||||
if (hasValue(parentThemeName)) {
|
||||
// inherit the head tags of the parent theme
|
||||
return this.createHeadTags(parentThemeName);
|
||||
}
|
||||
|
||||
const defaultThemeName = DEFAULT_THEME_CONFIG.name;
|
||||
if (
|
||||
hasNoValue(defaultThemeName) ||
|
||||
themeName === defaultThemeName ||
|
||||
themeName === BASE_THEME_NAME
|
||||
) {
|
||||
// last resort, use fallback favicon.ico
|
||||
return [
|
||||
this.createHeadTag({
|
||||
'tagName': 'link',
|
||||
'attributes': {
|
||||
'rel': 'icon',
|
||||
'href': 'assets/images/favicon.ico',
|
||||
'sizes': 'any',
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
// inherit the head tags of the default theme
|
||||
return this.createHeadTags(DEFAULT_THEME_CONFIG.name);
|
||||
}
|
||||
|
||||
return headTagConfigs.map(this.createHeadTag.bind(this));
|
||||
}
|
||||
|
||||
private createHeadTag(headTagConfig: HeadTagConfig): HTMLElement {
|
||||
const tag = this.document.createElement(headTagConfig.tagName);
|
||||
|
||||
if (hasValue(headTagConfig.attributes)) {
|
||||
Object.entries(headTagConfig.attributes)
|
||||
.forEach(([key, value]) => tag.setAttribute(key, value));
|
||||
}
|
||||
|
||||
// 'class' attribute should always be 'theme-head-tag' for removal
|
||||
tag.setAttribute('class', 'theme-head-tag');
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
private trackIdleModal() {
|
||||
const isIdle$ = this.authService.isUserIdle();
|
||||
const isAuthenticated$ = this.authService.isAuthenticated();
|
||||
|
@@ -10,11 +10,11 @@
|
||||
</nav>
|
||||
|
||||
<ng-template #breadcrumb let-text="text" let-url="url">
|
||||
<li class="breadcrumb-item"><a [routerLink]="url">{{text | translate}}</a></li>
|
||||
<li class="breadcrumb-item"><div class="breadcrumb-item-limiter"><a [routerLink]="url" class="text-truncate">{{text | translate}}</a></div></li>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #activeBreadcrumb let-text="text">
|
||||
<li class="breadcrumb-item active" aria-current="page">{{text | translate}}</li>
|
||||
<li class="breadcrumb-item active" aria-current="page"><div class="breadcrumb-item-limiter"><div class="text-truncate">{{text | translate}}</div></div></li>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
|
@@ -10,6 +10,19 @@
|
||||
background-color: var(--ds-breadcrumb-bg);
|
||||
}
|
||||
|
||||
li.breadcrumb-item {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.breadcrumb-item-limiter {
|
||||
display: inline-block;
|
||||
max-width: var(--ds-breadcrumb-max-length);
|
||||
> * {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
li.breadcrumb-item > a {
|
||||
color: var(--ds-breadcrumb-link-color) !important;
|
||||
}
|
||||
@@ -18,5 +31,6 @@ li.breadcrumb-item.active {
|
||||
}
|
||||
|
||||
.breadcrumb-item+ .breadcrumb-item::before {
|
||||
display: block;
|
||||
content: quote("•") !important;
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import { By } from '@angular/platform-browser';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { TranslateLoaderMock } from '../shared/testing/translate-loader.mock';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
describe('BreadcrumbsComponent', () => {
|
||||
@@ -72,7 +72,7 @@ describe('BreadcrumbsComponent', () => {
|
||||
expect(breadcrumbs.length).toBe(3);
|
||||
expectBreadcrumb(breadcrumbs[0], 'home.breadcrumbs', '/');
|
||||
expectBreadcrumb(breadcrumbs[1], 'bc 1', '/example.com');
|
||||
expectBreadcrumb(breadcrumbs[2], 'bc 2', null);
|
||||
expectBreadcrumb(breadcrumbs[2].query(By.css('.text-truncate')), 'bc 2', null);
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Breadcrumb } from './breadcrumb/breadcrumb.model';
|
||||
import { BreadcrumbsService } from './breadcrumbs.service';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Component representing the breadcrumbs of a page
|
||||
|
@@ -31,7 +31,7 @@ const ENTRY_COMPONENTS = [
|
||||
export class BrowseByModule {
|
||||
/**
|
||||
* NOTE: this method allows to resolve issue with components that using a custom decorator
|
||||
* which are not loaded during CSR otherwise
|
||||
* which are not loaded during SSR otherwise
|
||||
*/
|
||||
static withEntryComponents() {
|
||||
return {
|
||||
|
@@ -5,9 +5,10 @@
|
||||
<p [innerHTML]="'collection.edit.item-mapper.collection' | translate:{ name: (collectionName$ |async) }" id="collection-name"></p>
|
||||
<p>{{'collection.edit.item-mapper.description' | translate}}</p>
|
||||
|
||||
<ngb-tabset (tabChange)="tabChange($event)" [destroyOnHide]="true" #tabs="ngbTabset">
|
||||
<ngb-tab title="{{'collection.edit.item-mapper.tabs.browse' | translate}}" id="browseTab">
|
||||
<ng-template ngbTabContent>
|
||||
<ul ngbNav (navChange)="tabChange($event)" [destroyOnHide]="true" #tabs="ngbNav" class="nav-tabs">
|
||||
<li [ngbNavItem]="'browseTab'">
|
||||
<a ngbNavLink>{{'collection.edit.item-mapper.tabs.browse' | translate}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="mt-2">
|
||||
<ds-item-select class="mt-2"
|
||||
[key]="'browse'"
|
||||
@@ -21,9 +22,10 @@
|
||||
(cancel)="onCancel()"></ds-item-select>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
<ngb-tab title="{{'collection.edit.item-mapper.tabs.map' | translate}}" id="mapTab">
|
||||
<ng-template ngbTabContent>
|
||||
</li>
|
||||
<li [ngbNavItem]="'mapTab'">
|
||||
<a ngbNavLink>{{'collection.edit.item-mapper.tabs.map' | translate}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-lg-6">
|
||||
<ds-search-form id="search-form"
|
||||
@@ -52,8 +54,9 @@
|
||||
{{'collection.edit.item-mapper.no-search' | translate}}
|
||||
</div>
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
</ngb-tabset>
|
||||
</li>
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="tabs"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -27,7 +27,7 @@ import { ItemSelectComponent } from '../../shared/object-select/item-select/item
|
||||
import { ObjectSelectService } from '../../shared/object-select/object-select.service';
|
||||
import { ObjectSelectServiceStub } from '../../shared/testing/object-select-service.stub';
|
||||
import { VarDirective } from '../../shared/utils/var.directive';
|
||||
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { RouteService } from '../../core/services/route.service';
|
||||
import { ErrorComponent } from '../../shared/error/error.component';
|
||||
import { LoadingComponent } from '../../shared/loading/loading.component';
|
||||
|
@@ -11,18 +11,16 @@ import {
|
||||
import { filter, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { hasValue, hasValueOperator } from '../../../../shared/empty.util';
|
||||
import { ProcessStatus } from '../../../../process-page/processes/process-status.model';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import { RequestService } from '../../../../core/data/request.service';
|
||||
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||
import { Collection } from '../../../../core/shared/collection.model';
|
||||
import { CollectionDataService } from '../../../../core/data/collection-data.service';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Process } from '../../../../process-page/processes/process.model';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||
import { ContentSourceSetSerializer } from '../../../../core/shared/content-source-set-serializer';
|
||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||
|
||||
/**
|
||||
* Component that contains the controls to run, reset and test the harvest
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { FindListOptions } from '../core/data/request.models';
|
||||
import { hasValue } from '../shared/empty.util';
|
||||
import { CommunityListService, FlatNode } from './community-list-service';
|
||||
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
|
||||
import { BehaviorSubject, Observable, } from 'rxjs';
|
||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
|
||||
/**
|
||||
|
3
src/app/core/cache/builders/link.service.ts
vendored
3
src/app/core/cache/builders/link.service.ts
vendored
@@ -10,8 +10,7 @@ import {
|
||||
LinkDefinition
|
||||
} from './build-decorators';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { EMPTY } from 'rxjs';
|
||||
import { EMPTY, Observable } from 'rxjs';
|
||||
import { ResourceType } from '../../shared/resource-type';
|
||||
|
||||
/**
|
||||
|
@@ -7,7 +7,7 @@ import { PostRequest } from './request.models';
|
||||
import { Registration } from '../shared/registration.model';
|
||||
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service.stub';
|
||||
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
|
||||
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
|
||||
describe('EpersonRegistrationService', () => {
|
||||
|
@@ -14,7 +14,7 @@ import { FindListOptions } from './request.models';
|
||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { RemoteData } from './remote-data';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable } from 'rxjs';
|
||||
import { PaginatedList } from './paginated-list.model';
|
||||
import { ITEM_TYPE } from '../shared/item-relationships/item-type.resource-type';
|
||||
import { LICENSE } from '../shared/license.resource-type';
|
||||
|
@@ -10,7 +10,6 @@ import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||
import { difference } from '../../shared/object.util';
|
||||
import { isNumeric } from 'rxjs/internal-compatibility';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
|
@@ -7,7 +7,7 @@ import { HALLink } from './hal-link.model';
|
||||
import { ResourceType } from './resource-type';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { PaginatedList } from '../data/paginated-list.model';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ITEM_TYPE } from './item-relationships/item-type.resource-type';
|
||||
import { ItemType } from './item-relationships/item-type.model';
|
||||
|
||||
|
@@ -5,7 +5,7 @@ import { map, take } from 'rxjs/operators';
|
||||
import { NativeWindowRef, NativeWindowService } from '../services/window.service';
|
||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Provides utility methods to save files on the client-side.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import * as uuidv4 from 'uuid/v4';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { autoserialize, Serialize, Deserialize } from 'cerialize';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { isUndefined } from '../../shared/empty.util';
|
||||
import * as uuidv4 from 'uuid/v4';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { MetadataMap, MetadataValue, MetadataValueFilter, MetadatumViewModel } from './metadata.models';
|
||||
import { Metadata } from './metadata.utils';
|
||||
|
||||
|
@@ -26,7 +26,7 @@ import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../../s
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||
import { SearchConfig } from './search-filters/search-config.model';
|
||||
import { SearchService } from './search.service';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
import { of } from 'rxjs';
|
||||
import { PaginationService } from '../../pagination/pagination.service';
|
||||
|
||||
/**
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import * as uuidv4 from 'uuid/v4';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@Injectable()
|
||||
export class UUIDService {
|
||||
|
@@ -8,7 +8,7 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { ClaimedTaskDataService } from './claimed-task-data.service';
|
||||
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { FindListOptions } from '../data/request.models';
|
||||
import { RequestParam } from '../cache/models/request-param.model';
|
||||
import { getTestScheduler } from 'jasmine-marbles';
|
||||
|
@@ -10,7 +10,7 @@ import { CoreState } from '../core.reducers';
|
||||
import { PoolTaskDataService } from './pool-task-data.service';
|
||||
import { getTestScheduler } from 'jasmine-marbles';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { FindListOptions } from '../data/request.models';
|
||||
import { RequestParam } from '../cache/models/request-param.model';
|
||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||
|
@@ -17,7 +17,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
|
||||
import { ChangeAnalyzer } from '../data/change-analyzer';
|
||||
import { compare, Operation } from 'fast-json-patch';
|
||||
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||
import { getMockRemoteDataBuildService } from '../../shared/mocks/remote-data-build.service.mock';
|
||||
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
|
||||
|
@@ -8,11 +8,10 @@ import {
|
||||
HttpResponse,
|
||||
HttpXsrfTokenExtractor
|
||||
} from '@angular/common/http';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { tap, catchError } from 'rxjs/operators';
|
||||
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
|
||||
import { CookieService } from '../services/cookie.service';
|
||||
import { throwError } from 'rxjs';
|
||||
|
||||
// Name of XSRF header we may send in requests to backend (this is a standard name defined by Angular)
|
||||
export const XSRF_REQUEST_HEADER = 'X-XSRF-TOKEN';
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
||||
<ds-truncatable [id]="dso.id">
|
||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
||||
[routerLink]="[itemPageRoute]" class="lead item-list-title"
|
||||
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
|
||||
[innerHTML]="dsoTitle"></a>
|
||||
<span *ngIf="linkType == linkTypes.None"
|
||||
class="lead item-list-title"
|
||||
class="lead item-list-title dont-break-out"
|
||||
[innerHTML]="dsoTitle"></span>
|
||||
<span class="text-muted">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
||||
<ds-truncatable [id]="dso.id">
|
||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
||||
[routerLink]="[itemPageRoute]" class="lead item-list-title"
|
||||
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
|
||||
[innerHTML]="dsoTitle"></a>
|
||||
<span *ngIf="linkType == linkTypes.None"
|
||||
class="lead item-list-title"
|
||||
class="lead item-list-title dont-break-out"
|
||||
[innerHTML]="dsoTitle"></span>
|
||||
<span class="text-muted">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
||||
<ds-truncatable [id]="dso.id">
|
||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
||||
[routerLink]="[itemPageRoute]" class="lead item-list-title"
|
||||
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
|
||||
[innerHTML]="dsoTitle"></a>
|
||||
<span *ngIf="linkType == linkTypes.None"
|
||||
class="lead item-list-title"
|
||||
class="lead item-list-title dont-break-out"
|
||||
[innerHTML]="dsoTitle"></span>
|
||||
<span class="text-muted">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||
|
@@ -54,7 +54,7 @@ const ENTRY_COMPONENTS = [
|
||||
export class JournalEntitiesModule {
|
||||
/**
|
||||
* NOTE: this method allows to resolve issue with components that using a custom decorator
|
||||
* which are not loaded during CSR otherwise
|
||||
* which are not loaded during SSR otherwise
|
||||
*/
|
||||
static withEntryComponents() {
|
||||
return {
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<ds-truncatable [id]="dso.id">
|
||||
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
|
||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
||||
[routerLink]="[itemPageRoute]" class="lead item-list-title"
|
||||
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
|
||||
[innerHTML]="dsoTitle"></a>
|
||||
<span *ngIf="linkType == linkTypes.None"
|
||||
class="lead item-list-title"
|
||||
class="lead item-list-title dont-break-out"
|
||||
[innerHTML]="dsoTitle"></span>
|
||||
<!--<span class="text-muted">-->
|
||||
<!--<ds-truncatable-part [id]="dso.id" [minLines]="1">-->
|
||||
|
@@ -74,7 +74,7 @@ const COMPONENTS = [
|
||||
export class ResearchEntitiesModule {
|
||||
/**
|
||||
* NOTE: this method allows to resolve issue with components that using a custom decorator
|
||||
* which are not loaded during CSR otherwise
|
||||
* which are not loaded during SSR otherwise
|
||||
*/
|
||||
static withEntryComponents() {
|
||||
return {
|
||||
|
@@ -1,18 +1,24 @@
|
||||
<ng-template #bitstreamView>
|
||||
<div class="{{columnSizes.columns[0].buildClasses()}} row-element d-flex">
|
||||
<ng-content select="[slot=drag-handle]"></ng-content>
|
||||
<div class="float-left d-flex align-items-center">
|
||||
{{ bitstreamName }}
|
||||
<div class="float-left d-flex align-items-center overflow-hidden">
|
||||
<span class="text-truncate">
|
||||
{{ bitstreamName }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="{{columnSizes.columns[1].buildClasses()}} row-element d-flex align-items-center">
|
||||
<div class="w-100">
|
||||
<span class="text-truncate">
|
||||
{{ bitstream?.firstMetadataValue('dc.description') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="{{columnSizes.columns[2].buildClasses()}} row-element d-flex align-items-center">
|
||||
<div class="text-center w-100">
|
||||
{{ (format$ | async)?.shortDescription }}
|
||||
<span class="text-truncate">
|
||||
{{ (format$ | async)?.shortDescription }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="{{columnSizes.columns[3].buildClasses()}} row-element d-flex align-items-center">
|
||||
|
@@ -5,9 +5,10 @@
|
||||
<p [innerHTML]="'item.edit.item-mapper.item' | translate:{ name: (itemName$ | async) }" id="item-name"></p>
|
||||
<p>{{'item.edit.item-mapper.description' | translate}}</p>
|
||||
|
||||
<ngb-tabset (tabChange)="tabChange($event)" [destroyOnHide]="true" #tabs="ngbTabset">
|
||||
<ngb-tab title="{{'item.edit.item-mapper.tabs.browse' | translate}}" id="browseTab">
|
||||
<ng-template ngbTabContent>
|
||||
<ul ngbNav (navChange)="tabChange($event)" [destroyOnHide]="true" #tabs="ngbNav" class="nav-tabs">
|
||||
<li [ngbNavItem]="'browseTab'">
|
||||
<a ngbNavLink>{{'item.edit.item-mapper.tabs.browse' | translate}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="mt-2">
|
||||
<ds-collection-select class="mt-2"
|
||||
[key]="'browse'"
|
||||
@@ -20,9 +21,10 @@
|
||||
(cancel)="onCancel()"></ds-collection-select>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
<ngb-tab title="{{'item.edit.item-mapper.tabs.map' | translate}}" id="mapTab">
|
||||
<ng-template ngbTabContent>
|
||||
</li>
|
||||
<li [ngbNavItem]="'mapTab'">
|
||||
<a ngbNavLink>{{'item.edit.item-mapper.tabs.map' | translate}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-lg-6">
|
||||
<ds-search-form id="search-form"
|
||||
@@ -50,8 +52,9 @@
|
||||
{{'item.edit.item-mapper.no-search' | translate}}
|
||||
</div>
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
</ngb-tabset>
|
||||
</li>
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="tabs"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -7,7 +7,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
|
@@ -26,7 +26,7 @@
|
||||
<td class="w-100">
|
||||
<div class="value-field">
|
||||
<div *ngIf="!(editable | async)">
|
||||
<span>{{metadata?.value}}</span>
|
||||
<span class="dont-break-out">{{metadata?.value}}</span>
|
||||
</div>
|
||||
<div *ngIf="(editable | async)" class="field-container">
|
||||
<textarea class="form-control" type="textarea" attr.aria-labelledby="fieldValue" [(ngModel)]="metadata.value" [dsDebounce]
|
||||
|
@@ -3,7 +3,7 @@ import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
|
||||
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
||||
import { combineLatest as observableCombineLatest, from as observableFrom, Observable } from 'rxjs';
|
||||
import { combineLatest as observableCombineLatest, from as observableFrom, BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import {
|
||||
FieldUpdate,
|
||||
FieldUpdates,
|
||||
@@ -30,8 +30,6 @@ import { followLink } from '../../../../shared/utils/follow-link-config.model';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { Collection } from '../../../../core/shared/collection.model';
|
||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
|
||||
import { PaginationService } from '../../../../core/pagination/pagination.service';
|
||||
import { RelationshipTypeService } from '../../../../core/data/relationship-type.service';
|
||||
|
@@ -6,9 +6,8 @@ import {
|
||||
FieldUpdates,
|
||||
RelationshipIdentifiable,
|
||||
} from '../../../core/data/object-updates/object-updates.reducer';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { map, startWith, switchMap, take } from 'rxjs/operators';
|
||||
import { combineLatest as observableCombineLatest, of as observableOf, zip as observableZip } from 'rxjs';
|
||||
import { combineLatest as observableCombineLatest, of as observableOf, zip as observableZip, Observable } from 'rxjs';
|
||||
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||
import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<ds-metadata-field-wrapper [label]="label | translate">
|
||||
<a *ngFor="let mdValue of mdValues; let last=last;" [href]="mdValue.value">
|
||||
<a class="dont-break-out" *ngFor="let mdValue of mdValues; let last=last;" [href]="mdValue.value">
|
||||
{{ linktext || mdValue.value }}<span *ngIf="!last" [innerHTML]="separator"></span>
|
||||
</a>
|
||||
</ds-metadata-field-wrapper>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<ds-metadata-field-wrapper [label]="label | translate">
|
||||
<span *ngFor="let mdValue of mdValues; let last=last;">
|
||||
<span class="dont-break-out" *ngFor="let mdValue of mdValues; let last=last;">
|
||||
{{mdValue.value}}<span *ngIf="!last" [innerHTML]="separator"></span>
|
||||
</span>
|
||||
</ds-metadata-field-wrapper>
|
||||
|
@@ -33,7 +33,7 @@ import { NgxGalleryModule } from '@kolkov/ngx-gallery';
|
||||
import { MiradorViewerComponent } from './mirador-viewer/mirador-viewer.component';
|
||||
import { VersionPageComponent } from './version-page/version-page/version-page.component';
|
||||
import { VersionedItemComponent } from './simple/item-types/versioned-item/versioned-item.component';
|
||||
import { ThemedFileSectionComponent} from './simple/field-components/file-section/themed-file-section.component';
|
||||
import { ThemedFileSectionComponent } from './simple/field-components/file-section/themed-file-section.component';
|
||||
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
@@ -91,7 +91,7 @@ const DECLARATIONS = [
|
||||
export class ItemPageModule {
|
||||
/**
|
||||
* NOTE: this method allows to resolve issue with components that using a custom decorator
|
||||
* which are not loaded during CSR otherwise
|
||||
* which are not loaded during SSR otherwise
|
||||
*/
|
||||
static withEntryComponents() {
|
||||
return {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { NgxGalleryOptions } from '@kolkov/ngx-gallery';
|
||||
import { Bitstream } from '../../../core/shared/bitstream.model';
|
||||
import { MediaViewerItem } from '../../../core/shared/media-viewer-item.model';
|
||||
@@ -55,7 +55,7 @@ describe('MediaViewerImageComponent', () => {
|
||||
]
|
||||
);
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports:[],
|
||||
declarations: [MediaViewerImageComponent],
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
@@ -17,7 +17,7 @@ describe('MediaViewerVideoComponent', () => {
|
||||
let component: MediaViewerVideoComponent;
|
||||
let fixture: ComponentFixture<MediaViewerVideoComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot({
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { Bitstream } from '../../core/shared/bitstream.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||
@@ -60,7 +60,7 @@ describe('MediaViewerComponent', () => {
|
||||
{ bitstream: mockBitstream, format: 'image', thumbnail: null }
|
||||
);
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot({
|
||||
|
@@ -3,9 +3,8 @@ import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { BitstreamDataService } from '../../core/data/bitstream-data.service';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { of } from 'rxjs';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
import { MiradorViewerService } from './mirador-viewer.service';
|
||||
import { HostWindowService, WidthCategory } from '../../shared/host-window.service';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Injectable, isDevMode } from '@angular/core';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
|
||||
import { last, map, switchMap } from 'rxjs/operators';
|
||||
|
@@ -4,7 +4,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
|
||||
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||
|
@@ -1,17 +1,21 @@
|
||||
<ngb-tabset *ngIf="relationTypes.length > 1" [destroyOnHide]="true" #tabs="ngbTabset" [activeId]="activeTab$ | async" (tabChange)="onTabChange($event)">
|
||||
<ngb-tab *ngFor="let relationType of relationTypes" title="{{'item.page.relationships.' + relationType.label | translate}}" [id]="relationType.filter">
|
||||
<ng-template ngbTabContent>
|
||||
<div class="mt-4">
|
||||
<ds-related-entities-search [item]="item"
|
||||
[relationType]="relationType.filter"
|
||||
[configuration]="relationType.configuration"
|
||||
[searchEnabled]="searchEnabled"
|
||||
[sideBarWidth]="sideBarWidth">
|
||||
</ds-related-entities-search>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
</ngb-tabset>
|
||||
<ng-container *ngIf="relationTypes.length > 1">
|
||||
<ul ngbNav #tabs="ngbNav" [destroyOnHide]="true" [activeId]="activeTab$ | async" (navChange)="onTabChange($event)" class="nav-tabs">
|
||||
<li *ngFor="let relationType of relationTypes" [ngbNavItem]="relationType.filter">
|
||||
<a ngbNavLink>{{'item.page.relationships.' + relationType.label | translate}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="mt-4">
|
||||
<ds-related-entities-search [item]="item"
|
||||
[relationType]="relationType.filter"
|
||||
[configuration]="relationType.configuration"
|
||||
[searchEnabled]="searchEnabled"
|
||||
[sideBarWidth]="sideBarWidth">
|
||||
</ds-related-entities-search>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="tabs"></div>
|
||||
</ng-container>
|
||||
<div *ngIf="relationTypes.length === 1" class="mt-4">
|
||||
<ds-related-entities-search *ngVar="relationTypes[0] as relationType" [item]="item"
|
||||
[relationType]="relationType.filter"
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Component, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { Router } from '@angular/router';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { CommonModule } from '@angular/common';
|
||||
@@ -75,7 +75,7 @@ describe('MyDSpaceNewExternalDropdownComponent test', () => {
|
||||
};
|
||||
|
||||
describe('With only one Entity', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
@@ -126,7 +126,7 @@ describe('MyDSpaceNewExternalDropdownComponent test', () => {
|
||||
});
|
||||
|
||||
describe('With more than one Entity', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Component, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
@@ -79,7 +79,7 @@ describe('MyDSpaceNewSubmissionDropdownComponent test', () => {
|
||||
};
|
||||
|
||||
describe('With only one Entity', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
@@ -130,7 +130,7 @@ describe('MyDSpaceNewSubmissionDropdownComponent test', () => {
|
||||
});
|
||||
|
||||
describe('With more than one Entity', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<ds-search-sidebar *ngIf="!(isXsOrSm$ | async)" class="col-3 sidebar-md-sticky"
|
||||
id="search-sidebar"
|
||||
[configurationList]="(configurationList$ | async)"
|
||||
[resultCount]="(resultsRD$ | async)?.payload.totalElements"
|
||||
[resultCount]="(resultsRD$ | async)?.payload?.totalElements"
|
||||
[viewModeList]="viewModeList"
|
||||
[searchOptions]="(searchOptions$ | async)"
|
||||
[sortOptions]="(sortOptions$ | async)"
|
||||
@@ -27,7 +27,7 @@
|
||||
<ds-search-sidebar *ngIf="(isXsOrSm$ | async)" class="col-12"
|
||||
id="search-sidebar-sm"
|
||||
[configurationList]="(configurationList$ | async)"
|
||||
[resultCount]="(resultsRD$ | async)?.payload.totalElements"
|
||||
[resultCount]="(resultsRD$ | async)?.payload?.totalElements"
|
||||
(toggleSidebar)="closeSidebar()"
|
||||
[ngClass]="{'active': !(isSidebarCollapsed() | async)}"
|
||||
[searchOptions]="(searchOptions$ | async)"
|
||||
|
@@ -19,7 +19,7 @@ import { PaginatedSearchOptions } from '../shared/search/paginated-search-option
|
||||
import { SearchService } from '../core/shared/search/search.service';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { hasValue } from '../shared/empty.util';
|
||||
import { getFirstSucceededRemoteData } from '../core/shared/operators';
|
||||
import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
||||
import { MyDSpaceResponseParsingService } from '../core/data/mydspace-response-parsing.service';
|
||||
import { SearchConfigurationOption } from '../shared/search/search-switch-configuration/search-configuration-option.model';
|
||||
import { RoleType } from '../core/roles/role-types';
|
||||
@@ -30,7 +30,7 @@ import { MyDSpaceRequest } from '../core/data/request.models';
|
||||
import { SearchResult } from '../shared/search/search-result.model';
|
||||
import { Context } from '../core/shared/context.model';
|
||||
import { SortOptions } from '../core/cache/models/sort-options.model';
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
import { SearchObjects } from '../shared/search/search-objects.model';
|
||||
|
||||
export const MYDSPACE_ROUTE = '/mydspace';
|
||||
export const SEARCH_CONFIG_SERVICE: InjectionToken<SearchConfigurationService> = new InjectionToken<SearchConfigurationService>('searchConfigurationService');
|
||||
@@ -111,8 +111,7 @@ export class MyDSpacePageComponent implements OnInit {
|
||||
constructor(private service: SearchService,
|
||||
private sidebarService: SidebarService,
|
||||
private windowService: HostWindowService,
|
||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService,
|
||||
private routeService: RouteService) {
|
||||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService) {
|
||||
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
||||
this.service.setServiceOptions(MyDSpaceResponseParsingService, MyDSpaceRequest);
|
||||
}
|
||||
@@ -134,8 +133,8 @@ export class MyDSpacePageComponent implements OnInit {
|
||||
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
|
||||
this.sub = this.searchOptions$.pipe(
|
||||
tap(() => this.resultsRD$.next(null)),
|
||||
switchMap((options: PaginatedSearchOptions) => this.service.search(options).pipe(getFirstSucceededRemoteData())))
|
||||
.subscribe((results) => {
|
||||
switchMap((options: PaginatedSearchOptions) => this.service.search(options).pipe(getFirstCompletedRemoteData())))
|
||||
.subscribe((results: RemoteData<SearchObjects<DSpaceObject>>) => {
|
||||
this.resultsRD$.next(results);
|
||||
});
|
||||
|
||||
|
@@ -10,5 +10,5 @@
|
||||
</ds-viewable-collection>
|
||||
</div>
|
||||
<ds-loading *ngIf="isLoading()" message="{{'loading.mydspace-results' | translate}}"></ds-loading>
|
||||
<ds-error *ngIf="searchResults?.hasFailed && (!searchResults?.errorMessage || searchResults?.statusCode != 400)" message="{{'error.search-results' | translate}}"></ds-error>
|
||||
<ds-error *ngIf="showError()" message="{{errorMessageLabel() | translate}}"></ds-error>
|
||||
<h3 *ngIf="searchResults?.payload?.page.length == 0" class="text-center text-muted" ><span>{{'mydspace.results.no-results' | translate}}</span></h3>
|
||||
|
@@ -40,9 +40,19 @@ describe('MyDSpaceResultsComponent', () => {
|
||||
expect(fixture.debugElement.query(By.css('a'))).toBeNull();
|
||||
});
|
||||
|
||||
it('should display error message if error is != 400', () => {
|
||||
(comp as any).searchResults = { hasFailed: true, error: { statusCode: 500 } };
|
||||
it('should display error message if error is 500', () => {
|
||||
(comp as any).searchResults = { hasFailed: true, statusCode: 500 };
|
||||
fixture.detectChanges();
|
||||
expect(comp.showError()).toBeTrue();
|
||||
expect(comp.errorMessageLabel()).toBe('error.search-results');
|
||||
expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should display error message if error is 422', () => {
|
||||
(comp as any).searchResults = { hasFailed: true, statusCode: 422 };
|
||||
fixture.detectChanges();
|
||||
expect(comp.showError()).toBeTrue();
|
||||
expect(comp.errorMessageLabel()).toBe('error.invalid-search-query');
|
||||
expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull();
|
||||
});
|
||||
|
||||
|
@@ -58,4 +58,12 @@ export class MyDSpaceResultsComponent {
|
||||
isLoading() {
|
||||
return !this.searchResults || isEmpty(this.searchResults) || this.searchResults.isLoading;
|
||||
}
|
||||
|
||||
showError(): boolean {
|
||||
return this.searchResults?.hasFailed && (!this.searchResults?.errorMessage || this.searchResults?.statusCode !== 400);
|
||||
}
|
||||
|
||||
errorMessageLabel(): string {
|
||||
return (this.searchResults?.statusCode === 422) ? 'error.invalid-search-query' : 'error.search-results';
|
||||
}
|
||||
}
|
||||
|
@@ -50,7 +50,7 @@ const ENTRY_COMPONENTS = [
|
||||
export class MyDspaceSearchModule {
|
||||
/**
|
||||
* NOTE: this method allows to resolve issue with components that using a custom decorator
|
||||
* which are not loaded during CSR otherwise
|
||||
* which are not loaded during SSR otherwise
|
||||
*/
|
||||
static withEntryComponents() {
|
||||
return {
|
||||
|
@@ -58,7 +58,7 @@ const ENTRY_COMPONENTS = [
|
||||
export class NavbarModule {
|
||||
/**
|
||||
* NOTE: this method allows to resolve issue with components that using a custom decorator
|
||||
* which are not loaded during CSR otherwise
|
||||
* which are not loaded during SSR otherwise
|
||||
*/
|
||||
static withEntryComponents() {
|
||||
return {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="d-flex">
|
||||
<h2 class="flex-grow-1">{{'process.detail.title' | translate:{id: process?.processId, name: process?.scriptName} }}</h2>
|
||||
<div>
|
||||
<a class="btn btn-light" [routerLink]="'/processes/new'" [queryParams]="{id: process?.processId}">{{'process.detail.create' | translate}}</a>
|
||||
<button class="btn btn-lg btn-success " routerLink="/processes/new" [queryParams]="{id: process?.processId}"><i class="fas fa-plus pr-2"></i>{{'process.detail.create' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<ds-process-detail-field id="process-name" [title]="'process.detail.script'">
|
||||
@@ -23,11 +23,11 @@
|
||||
</div>
|
||||
|
||||
<ds-process-detail-field *ngIf="process && process.startTime" id="process-start-time" [title]="'process.detail.start-time' | translate">
|
||||
<div>{{ process.startTime }}</div>
|
||||
<div>{{ process.startTime | date:dateFormat:'UTC' }}</div>
|
||||
</ds-process-detail-field>
|
||||
|
||||
<ds-process-detail-field *ngIf="process && process.endTime" id="process-end-time" [title]="'process.detail.end-time' | translate">
|
||||
<div>{{ process.endTime }}</div>
|
||||
<div>{{ process.endTime | date:dateFormat:'UTC' }}</div>
|
||||
</ds-process-detail-field>
|
||||
|
||||
<ds-process-detail-field *ngIf="process && process.processStatus" id="process-status" [title]="'process.detail.status' | translate">
|
||||
@@ -35,7 +35,7 @@
|
||||
</ds-process-detail-field>
|
||||
|
||||
<ds-process-detail-field *ngIf="isProcessFinished(process)" id="process-output" [title]="'process.detail.output'">
|
||||
<button *ngIf="!showOutputLogs && process?._links?.output?.href != undefined" id="showOutputButton" class="btn btn-light" (click)="showProcessOutputLogs()">
|
||||
<button *ngIf="!showOutputLogs && process?._links?.output?.href != undefined" id="showOutputButton" class="btn btn-primary" (click)="showProcessOutputLogs()">
|
||||
{{ 'process.detail.logs.button' | translate }}
|
||||
</button>
|
||||
<ds-loading *ngIf="retrievingOutputLogs$ | async" class="ds-loading" message="{{ 'process.detail.logs.loading' | translate }}"></ds-loading>
|
||||
@@ -47,7 +47,7 @@
|
||||
</p>
|
||||
</ds-process-detail-field>
|
||||
|
||||
<div>
|
||||
<a class="btn btn-light mt-3" [routerLink]="'/processes'">{{'process.detail.back' | translate}}</a>
|
||||
<div style="text-align: right;">
|
||||
<a class="btn btn-outline-secondary mt-3" [routerLink]="'/processes'">{{'process.detail.back' | translate}}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -66,6 +66,11 @@ export class ProcessDetailComponent implements OnInit {
|
||||
*/
|
||||
retrievingOutputLogs$: BehaviorSubject<boolean>;
|
||||
|
||||
/**
|
||||
* Date format to use for start and end time of processes
|
||||
*/
|
||||
dateFormat = 'yyyy-MM-dd HH:mm:ss ZZZZ';
|
||||
|
||||
constructor(protected route: ActivatedRoute,
|
||||
protected router: Router,
|
||||
protected processService: ProcessDataService,
|
||||
|
@@ -26,8 +26,8 @@
|
||||
<td><a [routerLink]="['/processes/', process.processId]">{{process.processId}}</a></td>
|
||||
<td><a [routerLink]="['/processes/', process.processId]">{{process.scriptName}}</a></td>
|
||||
<td *ngVar="(getEpersonName(process.userId) | async) as ePersonName">{{ePersonName}}</td>
|
||||
<td>{{process.startTime | date:dateFormat}}</td>
|
||||
<td>{{process.endTime | date:dateFormat}}</td>
|
||||
<td>{{process.startTime | date:dateFormat:'UTC'}}</td>
|
||||
<td>{{process.endTime | date:dateFormat:'UTC'}}</td>
|
||||
<td>{{process.processStatus}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@@ -12,12 +12,9 @@ import { By } from '@angular/platform-browser';
|
||||
import { ProcessStatus } from '../processes/process-status.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||
import { FindListOptions } from '../../core/data/request.models';
|
||||
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
describe('ProcessOverviewComponent', () => {
|
||||
let component: ProcessOverviewComponent;
|
||||
@@ -30,27 +27,29 @@ describe('ProcessOverviewComponent', () => {
|
||||
let processes: Process[];
|
||||
let ePerson: EPerson;
|
||||
|
||||
const pipe = new DatePipe('en-US');
|
||||
|
||||
function init() {
|
||||
processes = [
|
||||
Object.assign(new Process(), {
|
||||
processId: 1,
|
||||
scriptName: 'script-name',
|
||||
startTime: '2020-03-19',
|
||||
endTime: '2020-03-19',
|
||||
startTime: '2020-03-19 00:30:00',
|
||||
endTime: '2020-03-19 23:30:00',
|
||||
processStatus: ProcessStatus.COMPLETED
|
||||
}),
|
||||
Object.assign(new Process(), {
|
||||
processId: 2,
|
||||
scriptName: 'script-name',
|
||||
startTime: '2020-03-20',
|
||||
endTime: '2020-03-20',
|
||||
startTime: '2020-03-20 00:30:00',
|
||||
endTime: '2020-03-20 23:30:00',
|
||||
processStatus: ProcessStatus.FAILED
|
||||
}),
|
||||
Object.assign(new Process(), {
|
||||
processId: 3,
|
||||
scriptName: 'another-script-name',
|
||||
startTime: '2020-03-21',
|
||||
endTime: '2020-03-21',
|
||||
startTime: '2020-03-21 00:30:00',
|
||||
endTime: '2020-03-21 23:30:00',
|
||||
processStatus: ProcessStatus.RUNNING
|
||||
})
|
||||
];
|
||||
@@ -135,14 +134,14 @@ describe('ProcessOverviewComponent', () => {
|
||||
it('should display the start time in the fourth column', () => {
|
||||
rowElements.forEach((rowElement, index) => {
|
||||
const el = rowElement.query(By.css('td:nth-child(4)')).nativeElement;
|
||||
expect(el.textContent).toContain(processes[index].startTime);
|
||||
expect(el.textContent).toContain(pipe.transform(processes[index].startTime, component.dateFormat, 'UTC'));
|
||||
});
|
||||
});
|
||||
|
||||
it('should display the end time in the fifth column', () => {
|
||||
rowElements.forEach((rowElement, index) => {
|
||||
const el = rowElement.query(By.css('td:nth-child(5)')).nativeElement;
|
||||
expect(el.textContent).toContain(processes[index].endTime);
|
||||
expect(el.textContent).toContain(pipe.transform(processes[index].endTime, component.dateFormat, 'UTC'));
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -23,7 +23,7 @@ export class ProcessPageResolver implements Resolve<RemoteData<Process>> {
|
||||
* or an error if something went wrong
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Process>> {
|
||||
return this.processService.findById(route.params.id, true, false, followLink('script')).pipe(
|
||||
return this.processService.findById(route.params.id, false, true, followLink('script')).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
);
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { ItemRequest } from '../../core/shared/item-request.model';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable } from 'rxjs';
|
||||
import {
|
||||
getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload,
|
||||
redirectOn4xx
|
||||
|
@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { ItemRequest } from '../../core/shared/item-request.model';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable } from 'rxjs';
|
||||
import {
|
||||
getFirstCompletedRemoteData,
|
||||
getFirstSucceededRemoteDataPayload,
|
||||
|
@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { ItemRequest } from '../../core/shared/item-request.model';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable } from 'rxjs';
|
||||
import {
|
||||
getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload,
|
||||
redirectOn4xx
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
|
||||
import { RemoteData } from '../core/data/remote-data';
|
||||
import { ItemRequest } from '../core/shared/item-request.model';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ItemRequestDataService } from '../core/data/item-request-data.service';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
||||
|
@@ -8,7 +8,7 @@ import { pushInOut } from '../shared/animations/push';
|
||||
import { HostWindowService } from '../shared/host-window.service';
|
||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||
import { hasValue, isEmpty } from '../shared/empty.util';
|
||||
import { getFirstSucceededRemoteData } from '../core/shared/operators';
|
||||
import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
||||
import { RouteService } from '../core/services/route.service';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component';
|
||||
import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
|
||||
@@ -126,12 +126,12 @@ export class SearchComponent implements OnInit {
|
||||
this.searchOptions$ = this.getSearchOptions();
|
||||
this.sub = this.searchOptions$.pipe(
|
||||
switchMap((options) => this.service.search(
|
||||
options, undefined, true, true, followLink<Item>('thumbnail', { isOptional: true })
|
||||
).pipe(getFirstSucceededRemoteData(), startWith(undefined))
|
||||
options, undefined, false, true, followLink<Item>('thumbnail', { isOptional: true })
|
||||
).pipe(getFirstCompletedRemoteData(), startWith(undefined))
|
||||
)
|
||||
).subscribe((results) => {
|
||||
this.resultsRD$.next(results);
|
||||
});
|
||||
this.resultsRD$.next(results);
|
||||
});
|
||||
|
||||
if (isEmpty(this.configuration$)) {
|
||||
this.configuration$ = this.searchConfigService.getCurrentConfiguration('default');
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { FileService } from '../../core/shared/file.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
@@ -77,7 +77,7 @@ describe('BitstreamDownloadPageComponent', () => {
|
||||
}
|
||||
|
||||
describe('init', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
init();
|
||||
initTestbed();
|
||||
}));
|
||||
@@ -93,7 +93,7 @@ describe('BitstreamDownloadPageComponent', () => {
|
||||
|
||||
describe('bitstream retrieval', () => {
|
||||
describe('when the user is authorized and not logged in', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
init();
|
||||
(authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(false));
|
||||
|
||||
@@ -109,7 +109,7 @@ describe('BitstreamDownloadPageComponent', () => {
|
||||
});
|
||||
});
|
||||
describe('when the user is authorized and logged in', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
init();
|
||||
initTestbed();
|
||||
}));
|
||||
@@ -123,7 +123,7 @@ describe('BitstreamDownloadPageComponent', () => {
|
||||
});
|
||||
});
|
||||
describe('when the user is not authorized and logged in', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
init();
|
||||
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
|
||||
initTestbed();
|
||||
@@ -138,7 +138,7 @@ describe('BitstreamDownloadPageComponent', () => {
|
||||
});
|
||||
});
|
||||
describe('when the user is not authorized and not logged in', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
init();
|
||||
(authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(false));
|
||||
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
|
||||
|
@@ -7,10 +7,9 @@ import { Bitstream } from '../../core/shared/bitstream.model';
|
||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
|
||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
|
||||
import { getBitstreamDownloadRoute, getForbiddenRoute } from '../../app-routing-paths';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { ItemRequestDataService } from '../../core/data/item-request-data.service';
|
||||
|
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
<ul class="list-unstyled">
|
||||
<li *ngFor="let object of objects?.payload?.page" class="mt-4 mb-4">
|
||||
<ds-listable-object-component-loader [object]="object"></ds-listable-object-component-loader>
|
||||
<ds-listable-object-component-loader [object]="object" [viewMode]="viewMode"></ds-listable-object-component-loader>
|
||||
</li>
|
||||
</ul>
|
||||
<div>
|
||||
|
@@ -2,7 +2,7 @@ import { BrowseByComponent } from './browse-by.component';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { SharedModule } from '../shared.module';
|
||||
import { CommonModule } from '@angular/common';
|
||||
@@ -18,17 +18,31 @@ import { PaginationComponentOptions } from '../pagination/pagination-component-o
|
||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
|
||||
import { storeModuleConfig } from '../../app.reducer';
|
||||
import { FindListOptions } from '../../core/data/request.models';
|
||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||
import { PaginationServiceStub } from '../testing/pagination-service.stub';
|
||||
import { ListableObjectComponentLoaderComponent } from '../object-collection/shared/listable-object/listable-object-component-loader.component';
|
||||
import { ViewMode } from '../../core/shared/view-mode.model';
|
||||
import { BrowseEntryListElementComponent } from '../object-list/browse-entry-list-element/browse-entry-list-element.component';
|
||||
import {
|
||||
DEFAULT_CONTEXT,
|
||||
listableObjectComponent,
|
||||
} from '../object-collection/shared/listable-object/listable-object.decorator';
|
||||
import { BrowseEntry } from '../../core/shared/browse-entry.model';
|
||||
import { ITEM } from '../../core/shared/item.resource-type';
|
||||
import { ThemeService } from '../theme-support/theme.service';
|
||||
import SpyObj = jasmine.SpyObj;
|
||||
|
||||
@listableObjectComponent(BrowseEntry, ViewMode.ListElement, DEFAULT_CONTEXT, 'custom')
|
||||
@Component({
|
||||
selector: 'ds-browse-entry-list-element',
|
||||
template: ''
|
||||
})
|
||||
class MockThemedBrowseEntryListElementComponent {}
|
||||
|
||||
describe('BrowseByComponent', () => {
|
||||
let comp: BrowseByComponent;
|
||||
let fixture: ComponentFixture<BrowseByComponent>;
|
||||
|
||||
let themeService: ThemeService;
|
||||
|
||||
const mockItems = [
|
||||
Object.assign(new Item(), {
|
||||
id: 'fakeId-1',
|
||||
@@ -59,9 +73,12 @@ describe('BrowseByComponent', () => {
|
||||
});
|
||||
const paginationService = new PaginationServiceStub(paginationConfig);
|
||||
|
||||
let themeService: SpyObj<ThemeService>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
themeService = jasmine.createSpyObj('themeService', {
|
||||
getThemeName: 'dspace',
|
||||
getThemeName$: observableOf('dspace'),
|
||||
});
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
@@ -82,6 +99,7 @@ describe('BrowseByComponent', () => {
|
||||
declarations: [],
|
||||
providers: [
|
||||
{provide: PaginationService, useValue: paginationService},
|
||||
{provide: MockThemedBrowseEntryListElementComponent},
|
||||
{ provide: ThemeService, useValue: themeService },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
@@ -170,4 +188,67 @@ describe('BrowseByComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when enableArrows is true and browseEntries are provided', () => {
|
||||
let browseEntries;
|
||||
|
||||
beforeEach(() => {
|
||||
browseEntries = [
|
||||
Object.assign(new BrowseEntry(), {
|
||||
type: ITEM,
|
||||
authority: 'authority key 1',
|
||||
value: 'browse entry 1',
|
||||
language: null,
|
||||
count: 1,
|
||||
}),
|
||||
Object.assign(new BrowseEntry(), {
|
||||
type: ITEM,
|
||||
authority: null,
|
||||
value: 'browse entry 2',
|
||||
language: null,
|
||||
count: 4,
|
||||
}),
|
||||
];
|
||||
|
||||
comp.enableArrows = true;
|
||||
comp.objects$ = createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), browseEntries));
|
||||
comp.paginationConfig = paginationConfig;
|
||||
comp.sortConfig = Object.assign(new SortOptions('dc.title', SortDirection.ASC));
|
||||
// NOTE: do NOT trigger change detection until the theme is set, such that the theme can be picked up as well
|
||||
});
|
||||
|
||||
describe('when theme is base', () => {
|
||||
beforeEach(() => {
|
||||
themeService.getThemeName.and.returnValue('base');
|
||||
themeService.getThemeName$.and.returnValue(observableOf('base'));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should use the base component to render browse entries', () => {
|
||||
const componentLoaders = fixture.debugElement.queryAll(By.directive(ListableObjectComponentLoaderComponent));
|
||||
expect(componentLoaders.length).toEqual(browseEntries.length);
|
||||
componentLoaders.forEach((componentLoader) => {
|
||||
const browseEntry = componentLoader.query(By.css('ds-browse-entry-list-element'));
|
||||
expect(browseEntry.componentInstance).toBeInstanceOf(BrowseEntryListElementComponent);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when theme is custom', () => {
|
||||
beforeEach(() => {
|
||||
themeService.getThemeName.and.returnValue('custom');
|
||||
themeService.getThemeName$.and.returnValue(observableOf('custom'));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should use the themed component to render browse entries', () => {
|
||||
const componentLoaders = fixture.debugElement.queryAll(By.directive(ListableObjectComponentLoaderComponent));
|
||||
expect(componentLoaders.length).toEqual(browseEntries.length);
|
||||
componentLoaders.forEach((componentLoader) => {
|
||||
const browseEntry = componentLoader.query(By.css('ds-browse-entry-list-element'));
|
||||
expect(browseEntry.componentInstance).toBeInstanceOf(MockThemedBrowseEntryListElementComponent);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -8,6 +8,7 @@ import { Observable } from 'rxjs';
|
||||
import { ListableObject } from '../object-collection/shared/listable-object.model';
|
||||
import { getStartsWithComponent, StartsWithType } from '../starts-with/starts-with-decorator';
|
||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||
import { ViewMode } from '../../core/shared/view-mode.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-browse-by',
|
||||
@@ -22,6 +23,12 @@ import { PaginationService } from '../../core/pagination/pagination.service';
|
||||
* Component to display a browse-by page for any ListableObject
|
||||
*/
|
||||
export class BrowseByComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* ViewMode that should be passed to {@link ListableObjectComponentLoaderComponent}.
|
||||
*/
|
||||
viewMode: ViewMode = ViewMode.ListElement;
|
||||
|
||||
/**
|
||||
* The i18n message to display as title
|
||||
*/
|
||||
|
@@ -8,7 +8,7 @@ import { ChipsItem } from './models/chips-item.model';
|
||||
import { UploaderService } from '../uploader/uploader.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Options } from 'sortablejs';
|
||||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-chips',
|
||||
|
@@ -11,8 +11,8 @@
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
(scroll)="onScroll($event)"
|
||||
infiniteScroll
|
||||
[infiniteScrollDistance]="5"
|
||||
[infiniteScrollThrottle]="300"
|
||||
[infiniteScrollDistance]="1.5"
|
||||
[infiniteScrollThrottle]="0"
|
||||
[infiniteScrollUpDistance]="1.5"
|
||||
[fromRoot]="true"
|
||||
[scrollWindow]="false"
|
||||
@@ -21,7 +21,7 @@
|
||||
<button class="dropdown-item disabled" *ngIf="searchListCollection?.length == 0 && !(isLoading | async)">
|
||||
{{'submission.sections.general.no-collection' | translate}}
|
||||
</button>
|
||||
<ng-container *ngIf="searchListCollection?.length > 0 && !(isLoading | async)">
|
||||
<ng-container *ngIf="searchListCollection?.length > 0">
|
||||
<button *ngFor="let listItem of searchListCollection"
|
||||
class="dropdown-item collection-item"
|
||||
title="{{ listItem.collection.name }}"
|
||||
|
@@ -223,20 +223,20 @@ export class CollectionDropdownComponent implements OnInit, OnDestroy {
|
||||
switchMap((collectionsRD: RemoteData<PaginatedList<Collection>>) => {
|
||||
this.searchComplete.emit();
|
||||
if (collectionsRD.hasSucceeded && collectionsRD.payload.totalElements > 0) {
|
||||
if ( (this.searchListCollection.length + findOptions.elementsPerPage) >= collectionsRD.payload.totalElements ) {
|
||||
if (this.searchListCollection.length >= collectionsRD.payload.totalElements) {
|
||||
this.hasNextPage = false;
|
||||
this.emitSelectionEvents(collectionsRD);
|
||||
return observableFrom(collectionsRD.payload.page).pipe(
|
||||
mergeMap((collection: Collection) => collection.parentCommunity.pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
map((community: Community) => ({
|
||||
communities: [{ id: community.id, name: community.name }],
|
||||
collection: { id: collection.id, uuid: collection.id, name: collection.name }
|
||||
})
|
||||
))),
|
||||
reduce((acc: any, value: any) => [...acc, value], []),
|
||||
);
|
||||
}
|
||||
this.emitSelectionEvents(collectionsRD);
|
||||
return observableFrom(collectionsRD.payload.page).pipe(
|
||||
mergeMap((collection: Collection) => collection.parentCommunity.pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
map((community: Community) => ({
|
||||
communities: [{ id: community.id, name: community.name }],
|
||||
collection: { id: collection.id, uuid: collection.id, name: collection.name }
|
||||
})
|
||||
))),
|
||||
reduce((acc: any, value: any) => [...acc, value], []),
|
||||
);
|
||||
} else {
|
||||
this.hasNextPage = false;
|
||||
return observableOf([]);
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable } from 'rxjs';
|
||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||
|
||||
@Component({
|
||||
|
@@ -1,11 +1,10 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||
import { VersionHistoryDataService } from '../../../core/data/version-history-data.service';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { map, startWith, switchMap } from 'rxjs/operators';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-dso-page-version-button',
|
||||
|
@@ -3,7 +3,7 @@
|
||||
class="form-control"
|
||||
(click)="$event.stopPropagation();"
|
||||
placeholder="{{'dso-selector.placeholder' | translate: { type: typesString } }}"
|
||||
[formControl]="input" dsAutoFocus (keyup.enter)="selectSingleResult()">
|
||||
[formControl]="input" ngbAutofocus (keyup.enter)="selectSingleResult()">
|
||||
</div>
|
||||
<div class="dropdown-divider"></div>
|
||||
<div class="scrollable-menu list-group">
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { METADATA_EXPORT_SCRIPT_NAME, ScriptDataService } from '../../../../core/data/processes/script-data.service';
|
||||
import { Collection } from '../../../../core/shared/collection.model';
|
||||
import { Community } from '../../../../core/shared/community.model';
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<div>
|
||||
<label>{{ message }}</label>
|
||||
</div>
|
||||
<ds-alert [type]="AlertTypeEnum.Error" [dismissible]="false">
|
||||
<!-- Using [innerHTML] instead of {{message}} allows to render HTML code -->
|
||||
<span [innerHTML]="message"></span>
|
||||
</ds-alert>
|
||||
|
@@ -36,7 +36,7 @@ describe('ErrorComponent (inline template)', () => {
|
||||
comp = fixture.componentInstance; // ErrorComponent test instance
|
||||
|
||||
// query for the message <label> by CSS element selector
|
||||
de = fixture.debugElement.query(By.css('label'));
|
||||
de = fixture.debugElement.query(By.css('ds-alert'));
|
||||
el = de.nativeElement;
|
||||
});
|
||||
|
||||
|
@@ -3,6 +3,7 @@ import { Component, Input } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { Subscription } from 'rxjs';
|
||||
import { AlertType } from '../alert/aletr-type';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-error',
|
||||
@@ -13,6 +14,12 @@ export class ErrorComponent {
|
||||
|
||||
@Input() message = 'Error...';
|
||||
|
||||
/**
|
||||
* The AlertType enumeration
|
||||
* @type {AlertType}
|
||||
*/
|
||||
public AlertTypeEnum = AlertType;
|
||||
|
||||
private subscription: Subscription;
|
||||
|
||||
constructor(private translate: TranslateService) {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<a [routerLink]="(bitstreamPath$| async)?.routerLink" [queryParams]="(bitstreamPath$| async)?.queryParams" [target]="isBlank ? '_blank': '_self'" [ngClass]="cssClasses">
|
||||
<a [routerLink]="(bitstreamPath$| async)?.routerLink" class="dont-break-out" [queryParams]="(bitstreamPath$| async)?.queryParams" [target]="isBlank ? '_blank': '_self'" [ngClass]="cssClasses">
|
||||
<span *ngIf="!(canDownload$ |async)"><i class="fas fa-lock"></i></span>
|
||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||
</a>
|
||||
|
@@ -7,59 +7,64 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ds-loading *ngIf="!item || !collection"></ds-loading>
|
||||
<ngb-tabset *ngIf="item && collection">
|
||||
<ngb-tab [title]="'submission.sections.describe.relationship-lookup.search-tab.tab-title.' + relationshipOptions.relationshipType | translate : {count: (totalInternal$ | async)}">
|
||||
<ng-template ngbTabContent>
|
||||
<ds-dynamic-lookup-relation-search-tab
|
||||
[selection$]="selection$"
|
||||
[listId]="listId"
|
||||
[relationship]="relationshipOptions"
|
||||
[repeatable]="repeatable"
|
||||
[context]="context"
|
||||
[query]="query"
|
||||
[relationshipType]="relationshipType"
|
||||
[isLeft]="isLeft"
|
||||
[item]="item"
|
||||
[isEditRelationship]="isEditRelationship"
|
||||
[toRemove]="toRemove"
|
||||
(selectObject)="select($event)"
|
||||
(deselectObject)="deselect($event)"
|
||||
class="d-block pt-3">
|
||||
</ds-dynamic-lookup-relation-search-tab>
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
<ngb-tab *ngFor="let source of (externalSourcesRD$ | async); let idx = index"
|
||||
[title]="'submission.sections.describe.relationship-lookup.search-tab.tab-title.' + source.id | translate : {count: (totalExternal$ | async)[idx]}">
|
||||
<ng-template ngbTabContent>
|
||||
<ds-dynamic-lookup-relation-external-source-tab
|
||||
[label]="label"
|
||||
[listId]="listId"
|
||||
[repeatable]="repeatable"
|
||||
[item]="item"
|
||||
[collection]="collection"
|
||||
[relationship]="relationshipOptions"
|
||||
[context]="context"
|
||||
[externalSource]="source"
|
||||
(importedObject)="imported($event)"
|
||||
class="d-block pt-3">
|
||||
</ds-dynamic-lookup-relation-external-source-tab>
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
<ngb-tab *ngIf="!isEditRelationship" [title]="'submission.sections.describe.relationship-lookup.selection-tab.tab-title' | translate : {count: (selection$ | async)?.length}">
|
||||
<ng-template ngbTabContent>
|
||||
<ds-dynamic-lookup-relation-selection-tab
|
||||
[selection$]="selection$"
|
||||
[listId]="listId"
|
||||
[relationshipType]="relationshipOptions.relationshipType"
|
||||
[repeatable]="repeatable"
|
||||
[context]="context"
|
||||
(selectObject)="select($event)"
|
||||
(deselectObject)="deselect($event)"
|
||||
class="d-block pt-3">
|
||||
</ds-dynamic-lookup-relation-selection-tab>
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
</ngb-tabset>
|
||||
<ng-container *ngIf="item && collection">
|
||||
<ul ngbNav #nav="ngbNav" class="nav-tabs">
|
||||
<li ngbNavItem>
|
||||
<a ngbNavLink>{{'submission.sections.describe.relationship-lookup.search-tab.tab-title.' + relationshipOptions.relationshipType | translate : { count: (totalInternal$ | async)} }}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ds-dynamic-lookup-relation-search-tab
|
||||
[selection$]="selection$"
|
||||
[listId]="listId"
|
||||
[relationship]="relationshipOptions"
|
||||
[repeatable]="repeatable"
|
||||
[context]="context"
|
||||
[query]="query"
|
||||
[relationshipType]="relationshipType"
|
||||
[isLeft]="isLeft"
|
||||
[item]="item"
|
||||
[isEditRelationship]="isEditRelationship"
|
||||
[toRemove]="toRemove"
|
||||
(selectObject)="select($event)"
|
||||
(deselectObject)="deselect($event)"
|
||||
class="d-block pt-3">
|
||||
</ds-dynamic-lookup-relation-search-tab>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li ngbNavItem *ngFor="let source of (externalSourcesRD$ | async); let idx = index">
|
||||
<a ngbNavLink>{{'submission.sections.describe.relationship-lookup.search-tab.tab-title.' + source.id | translate : { count: (totalExternal$ | async)[idx] } }}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ds-dynamic-lookup-relation-external-source-tab
|
||||
[label]="label"
|
||||
[listId]="listId"
|
||||
[repeatable]="repeatable"
|
||||
[item]="item"
|
||||
[collection]="collection"
|
||||
[relationship]="relationshipOptions"
|
||||
[context]="context"
|
||||
[externalSource]="source"
|
||||
(importedObject)="imported($event)"
|
||||
class="d-block pt-3">
|
||||
</ds-dynamic-lookup-relation-external-source-tab>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li ngbNavItem *ngIf="!isEditRelationship">
|
||||
<a ngbNavLink>{{'submission.sections.describe.relationship-lookup.selection-tab.tab-title' | translate : { count: (selection$ | async)?.length } }}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ds-dynamic-lookup-relation-selection-tab
|
||||
[selection$]="selection$"
|
||||
[listId]="listId"
|
||||
[relationshipType]="relationshipOptions.relationshipType"
|
||||
[repeatable]="repeatable"
|
||||
[context]="context"
|
||||
(selectObject)="select($event)"
|
||||
(deselectObject)="deselect($event)"
|
||||
class="d-block pt-3">
|
||||
</ds-dynamic-lookup-relation-selection-tab>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="nav"></div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<small>{{ ('submission.sections.describe.relationship-lookup.selected' | translate: {size: (selection$ | async)?.length || 0}) }}</small>
|
||||
|
@@ -6,7 +6,7 @@ import {
|
||||
} from './metadata-representation.decorator';
|
||||
import { MetadataRepresentationType } from '../../core/shared/metadata-representation/metadata-representation.model';
|
||||
import { Context } from '../../core/shared/context.model';
|
||||
import * as uuidv4 from 'uuid/v4';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { environment } from '../../../environments/environment';
|
||||
|
||||
let ogEnvironmentThemes;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { Component, Injector } from '@angular/core';
|
||||
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
|
||||
import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||
import { Router } from '@angular/router';
|
||||
@@ -10,7 +10,6 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||
import { RequestService } from '../../../../core/data/request.service';
|
||||
import { ClaimedApprovedTaskSearchResult } from '../../../object-collection/shared/claimed-approved-task-search-result.model';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
|
||||
export const WORKFLOW_TASK_OPTION_APPROVE = 'submit_approve';
|
||||
|
||||
|
@@ -9,10 +9,9 @@ import { NotificationsService } from '../../../notifications/notifications.servi
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||
import { RequestService } from '../../../../core/data/request.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
import { ClaimedDeclinedTaskSearchResult } from '../../../object-collection/shared/claimed-declined-task-search-result.model';
|
||||
|
||||
export const WORKFLOW_TASK_OPTION_REJECT = 'submit_reject';
|
||||
|
@@ -12,8 +12,7 @@ import { NotificationsService } from '../notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { RequestService } from '../../core/data/request.service';
|
||||
import { SearchService } from '../../core/shared/search/search.service';
|
||||
import { Observable} from 'rxjs/internal/Observable';
|
||||
import { of} from 'rxjs/internal/observable/of';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { ProcessTaskResponse } from '../../core/tasks/models/process-task-response';
|
||||
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
|
||||
import { getSearchResultFor } from '../search/search-result-element-decorator';
|
||||
|
@@ -20,7 +20,7 @@ import { GenericConstructor } from '../../../../core/shared/generic-constructor'
|
||||
import { ListableObjectDirective } from './listable-object.directive';
|
||||
import { CollectionElementLinkType } from '../../collection-element-link.type';
|
||||
import { hasValue, isNotEmpty } from '../../../empty.util';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { ThemeService } from '../../../theme-support/theme.service';
|
||||
|
@@ -1,3 +1,3 @@
|
||||
<div>
|
||||
<span>{{metadataRepresentation.getValue()}}</span>
|
||||
<span class="dont-break-out">{{metadataRepresentation.getValue()}}</span>
|
||||
</div>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
@@ -60,7 +60,7 @@ mockResultObject.indexableObject = Object.assign(new ClaimedTask(), { workflowit
|
||||
const linkService = getMockLinkService();
|
||||
|
||||
describe('ClaimedApprovedSearchResultListElementComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule],
|
||||
declarations: [ClaimedApprovedSearchResultListElementComponent, VarDirective],
|
||||
@@ -75,7 +75,7 @@ describe('ClaimedApprovedSearchResultListElementComponent', () => {
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
fixture = TestBed.createComponent(ClaimedApprovedSearchResultListElementComponent);
|
||||
component = fixture.componentInstance;
|
||||
}));
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
@@ -60,7 +60,7 @@ mockResultObject.indexableObject = Object.assign(new ClaimedTask(), { workflowit
|
||||
const linkService = getMockLinkService();
|
||||
|
||||
describe('ClaimedDeclinedSearchResultListElementComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule],
|
||||
declarations: [ClaimedDeclinedSearchResultListElementComponent, VarDirective],
|
||||
@@ -75,7 +75,7 @@ describe('ClaimedDeclinedSearchResultListElementComponent', () => {
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
fixture = TestBed.createComponent(ClaimedDeclinedSearchResultListElementComponent);
|
||||
component = fixture.componentInstance;
|
||||
}));
|
||||
|
@@ -2,9 +2,9 @@
|
||||
|
||||
<ds-truncatable [id]="dso.id" *ngIf="object !== undefined && object !== null">
|
||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
|
||||
[routerLink]="[itemPageRoute]" class="lead item-list-title"
|
||||
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
|
||||
[innerHTML]="dsoTitle"></a>
|
||||
<span *ngIf="linkType == linkTypes.None" class="lead item-list-title"
|
||||
<span *ngIf="linkType == linkTypes.None" class="lead item-list-title dont-break-out"
|
||||
[innerHTML]="dsoTitle"></span>
|
||||
<span class="text-muted">
|
||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||
|
@@ -3,14 +3,13 @@ import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { SearchResultListElementComponent } from '../search-result-list-element/search-result-list-element.component';
|
||||
import { Component } from '@angular/core';
|
||||
import { hasValue, isNotEmpty } from '../../empty.util';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { TruncatableService } from '../../truncatable/truncatable.service';
|
||||
import { LinkService } from '../../../core/cache/builders/link.service';
|
||||
import { find, map } from 'rxjs/operators';
|
||||
import { ChildHALResource } from '../../../core/shared/child-hal-resource.model';
|
||||
import { followLink } from '../../utils/follow-link-config.model';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { Context } from '../../../core/shared/context.model';
|
||||
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user