Skip to content

Commit bd2d8d3

Browse files
committed
Merge branch 'dspace-cris-2023_02_x' into ux-plus-2023_02_x
2 parents e230110 + 70523e7 commit bd2d8d3

18 files changed

Lines changed: 349 additions & 95 deletions

File tree

config/config.example.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ auth:
132132
# If the rest token expires in less than this amount of time, it will be refreshed automatically.
133133
# This is independent from the idle warning.
134134
timeLeftBeforeTokenRefresh: 120000 # 2 minutes
135+
# Standard login enabled
136+
disableStandardLogin: false
135137

136138
# Form settings
137139
form:

src/app/app-routing.module.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone
4646
import { ForgotPasswordCheckGuard } from './core/rest-property/forgot-password-check-guard.guard';
4747
import { SUGGESTION_MODULE_PATH } from './suggestions-page/suggestions-page-routing-paths';
4848
import { RedirectService } from './redirect/redirect.service';
49+
import { environment } from '../environments/environment';
4950
import {
5051
GenericAdministratorGuard
5152
} from './core/data/feature-authorization/feature-authorization-guard/generic-administrator-guard';
@@ -184,10 +185,21 @@ import {
184185
.then((m) => m.AdminModule),
185186
canActivate: [GenericAdministratorGuard, EndUserAgreementCurrentUserGuard]
186187
},
188+
{
189+
path: 'standard-login',
190+
loadChildren: () => import('./login-page/login-page.module').then((m) => m.LoginPageModule),
191+
data: {
192+
isBackDoor: true,
193+
},
194+
canMatch: [() => environment.auth.disableStandardLogin],
195+
},
187196
{
188197
path: 'login',
189-
loadChildren: () => import('./login-page/login-page.module')
190-
.then((m) => m.LoginPageModule)
198+
loadChildren: () => import('./login-page/login-page.module').then((m) => m.LoginPageModule),
199+
data: {
200+
isBackDoor: false,
201+
},
202+
canMatch: [() => !environment.auth.disableStandardLogin],
191203
},
192204
{
193205
path: 'external-login/:token',

src/app/core/shared/image.utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
Observable,
3+
of,
4+
} from 'rxjs';
5+
import { map } from 'rxjs/operators';
6+
7+
export const getDefaultImageUrlByEntityType = (entityType: string): Observable<string> => {
8+
const fallbackImage = 'assets/images/file-placeholder.svg';
9+
10+
if (!entityType) {
11+
return of(fallbackImage);
12+
}
13+
14+
const defaultImage = `assets/images/${entityType.toLowerCase()}-placeholder.svg`;
15+
return checkImageExists(defaultImage).pipe(map((exists) => exists ? defaultImage : fallbackImage));
16+
};
17+
18+
const checkImageExists = (url: string): Observable<boolean> => {
19+
return new Observable<boolean>((observer) => {
20+
const img = new Image();
21+
22+
img.onload = () => {
23+
observer.next(true);
24+
observer.complete();
25+
};
26+
27+
img.onerror = () => {
28+
observer.next(false);
29+
observer.complete();
30+
};
31+
32+
img.src = url;
33+
});
34+
};

src/app/core/submission/vocabularies/models/vocabulary.model.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ export class Vocabulary implements CacheableObject {
6565
@autoserialize
6666
externalSource: VocabularyExternalSourceMap;
6767

68+
69+
/**
70+
* A boolean variable that indicates whether the functionality of
71+
* multiple value generation is enabled within a generator context.
72+
*/
73+
@autoserialize
74+
multiValueOnGenerator: boolean;
75+
6876
/**
6977
* A string representing the kind of Vocabulary model
7078
*/
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<ds-thumbnail *ngIf="(initialized | async)" data-test="thumbnail"
22
[thumbnail]="thumbnail$ | async"
3-
[defaultImage]="default">
3+
[defaultImage]="default$ | async">
44
</ds-thumbnail>

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,12 @@ describe('ThumbnailComponent', () => {
152152
expect(thumbnail).toBeTruthy();
153153
}));
154154

155-
it('should show default thumbnail', () => {
156-
expect(component.default).toBe('assets/images/file-placeholder.svg');
155+
it('should show default thumbnail', (done) => {
156+
component.default$.subscribe(image => {
157+
expect(image).toBe('assets/images/file-placeholder.svg');
158+
done();
159+
});
157160
});
158-
159161
});
160162

161163
describe('When bitstreams are only original', () => {
@@ -172,10 +174,12 @@ describe('ThumbnailComponent', () => {
172174
expect(thumbnail).toBeTruthy();
173175
}));
174176

175-
it('should show default thumbnail', () => {
176-
expect(component.default).toBe('assets/images/file-placeholder.svg');
177+
it('should show default thumbnail', (done) => {
178+
component.default$.subscribe(image => {
179+
expect(image).toBe('assets/images/file-placeholder.svg');
180+
done();
181+
});
177182
});
178-
179183
});
180184

181185
describe('When bitstreams are only thumbnail', () => {
@@ -239,10 +243,12 @@ describe('ThumbnailComponent', () => {
239243
expect(thumbnail).toBeTruthy();
240244
});
241245

242-
it('should show default thumbnail', () => {
243-
expect(component.default).toBe('assets/images/file-placeholder.svg');
246+
it('should show default thumbnail', (done) => {
247+
component.default$.subscribe(image => {
248+
expect(image).toBe('assets/images/file-placeholder.svg');
249+
done();
250+
});
244251
});
245-
246252
});
247253

248254
describe('When bitstreams are only original without the right metadata information', () => {
@@ -254,8 +260,11 @@ describe('ThumbnailComponent', () => {
254260
fixture.detectChanges();
255261
});
256262

257-
it('should not show bitstream content image src but the default image', () => {
258-
expect(component.default).toBe('assets/images/file-placeholder.svg');
263+
it('should not show bitstream content image src but the default image', (done) => {
264+
component.default$.subscribe(image => {
265+
expect(image).toBe('assets/images/file-placeholder.svg');
266+
done();
267+
});
259268
});
260269

261270
});
@@ -269,10 +278,12 @@ describe('ThumbnailComponent', () => {
269278
fixture.detectChanges();
270279
});
271280

272-
it('should not show thumbnail content image src but the default image', () => {
273-
expect(component.default).toBe('assets/images/file-placeholder.svg');
281+
it('should not show thumbnail content image src but the default image', (done) => {
282+
component.default$.subscribe(image => {
283+
expect(image).toBe('assets/images/file-placeholder.svg');
284+
done();
285+
});
274286
});
275-
276287
});
277288

278289
describe('When bitstreams are only original with the right metadata information', () => {
@@ -284,8 +295,11 @@ describe('ThumbnailComponent', () => {
284295
fixture.detectChanges();
285296
});
286297

287-
it('should not show thumbnail content image src but the default image', () => {
288-
expect(component.default).toBe('assets/images/file-placeholder.svg');
298+
it('should not show thumbnail content image src but the default image', (done) => {
299+
component.default$.subscribe(image => {
300+
expect(image).toBe('assets/images/file-placeholder.svg');
301+
done();
302+
});
289303
});
290304

291305
});

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.ts

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import { Component, Inject, OnInit } from '@angular/core';
22

3-
import { BehaviorSubject, of as observableOf } from 'rxjs';
3+
import { BehaviorSubject, Observable, combineLatest, of as observableOf } from 'rxjs';
44
import { map, switchMap } from 'rxjs/operators';
55
import { TranslateService } from '@ngx-translate/core';
66

77
import { FieldRenderingType, MetadataBoxFieldRendering } from '../metadata-box.decorator';
88
import { BitstreamDataService } from '../../../../../../../core/data/bitstream-data.service';
9-
import { hasValue, isEmpty, isNotEmpty } from '../../../../../../../shared/empty.util';
9+
import { isEmpty, isNotEmpty } from '../../../../../../../shared/empty.util';
1010
import { Bitstream } from '../../../../../../../core/shared/bitstream.model';
1111
import { BitstreamRenderingModelComponent } from '../bitstream-rendering-model';
1212
import { Item } from '../../../../../../../core/shared/item.model';
1313
import { LayoutField } from '../../../../../../../core/layout/models/box.model';
1414
import { getFirstCompletedRemoteData } from '../../../../../../../core/shared/operators';
1515
import { PaginatedList } from '../../../../../../../core/data/paginated-list.model';
16+
import { getDefaultImageUrlByEntityType } from '../../../../../../../core/shared/image.utils';
1617

1718
@Component({
1819
// eslint-disable-next-line @angular-eslint/component-selector
@@ -34,7 +35,7 @@ export class ThumbnailComponent extends BitstreamRenderingModelComponent impleme
3435
/**
3536
* Default image to be shown in the thumbnail
3637
*/
37-
default: string;
38+
default$: Observable<string>;
3839

3940
/**
4041
* Item rendering initialization state
@@ -61,9 +62,14 @@ export class ThumbnailComponent extends BitstreamRenderingModelComponent impleme
6162
* Get the thumbnail information from api for this item
6263
*/
6364
ngOnInit(): void {
64-
this.setDefaultImage();
65-
this.getBitstreamsByItem().pipe(
66-
map((bitstreamList: PaginatedList<Bitstream>) => bitstreamList.page),
65+
const eType = this.item.firstMetadataValue('dspace.entity.type');
66+
this.default$ = getDefaultImageUrlByEntityType(eType);
67+
68+
combineLatest([
69+
this.default$,
70+
this.getBitstreamsByItem(),
71+
]).pipe(
72+
map(([_, bitstreamList]: [string, PaginatedList<Bitstream>]) => bitstreamList.page),
6773
switchMap((filteredBitstreams: Bitstream[]) => {
6874
if (filteredBitstreams.length > 0) {
6975
if (isEmpty(filteredBitstreams[0].thumbnail)) {
@@ -91,19 +97,4 @@ export class ThumbnailComponent extends BitstreamRenderingModelComponent impleme
9197
this.initialized.next(true);
9298
});
9399
}
94-
95-
/**
96-
* Set the default image src depending on item entity type
97-
*/
98-
setDefaultImage(): void {
99-
const eType = this.item.firstMetadataValue('dspace.entity.type');
100-
this.default = 'assets/images/file-placeholder.svg';
101-
if (hasValue(eType) && eType.toUpperCase() === 'PROJECT') {
102-
this.default = 'assets/images/project-placeholder.svg';
103-
} else if (hasValue(eType) && eType.toUpperCase() === 'ORGUNIT') {
104-
this.default = 'assets/images/orgunit-placeholder.svg';
105-
} else if (hasValue(eType) && eType.toUpperCase() === 'PERSON') {
106-
this.default = 'assets/images/person-placeholder.svg';
107-
}
108-
}
109100
}

src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-vocabulary.component.ts

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
DynamicFormLayoutService,
99
DynamicFormValidationService
1010
} from '@ng-dynamic-forms/core';
11-
import { distinctUntilChanged, filter, map, take } from 'rxjs/operators';
11+
import { distinctUntilChanged, filter, map, take, tap } from 'rxjs/operators';
1212
import { Observable, of as observableOf } from 'rxjs';
1313
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
1414

@@ -61,6 +61,8 @@ export abstract class DsDynamicVocabularyComponent extends DynamicFormControlCom
6161
public otherInfoValues: string[] = [];
6262
public otherInfoValuesUnformatted: string[] = [];
6363

64+
multiValueOnGenerator: boolean;
65+
6466
protected constructor(protected vocabularyService: VocabularyService,
6567
protected layoutService: DynamicFormLayoutService,
6668
protected validationService: DynamicFormValidationService,
@@ -174,6 +176,7 @@ export abstract class DsDynamicVocabularyComponent extends DynamicFormControlCom
174176
this.vocabulary$ = this.vocabularyService.findVocabularyById(this.model.vocabularyOptions.name).pipe(
175177
getFirstSucceededRemoteDataPayload(),
176178
distinctUntilChanged(),
179+
tap((vocabulary: Vocabulary) => this.multiValueOnGenerator = vocabulary.multiValueOnGenerator),
177180
);
178181
}
179182

@@ -256,12 +259,14 @@ export abstract class DsDynamicVocabularyComponent extends DynamicFormControlCom
256259
for (const key in otherInformation) {
257260
if (otherInformation.hasOwnProperty(key) && key.startsWith('data-')) {
258261
const fieldId = key.replace('data-', '');
259-
const newValue: FormFieldMetadataValueObject = this.getOtherInformationValue(otherInformation[key], key);
260-
if (isNotEmpty(newValue)) {
261-
const updatedModel = this.formBuilderService.updateModelValue(fieldId, newValue);
262-
if (isNotEmpty(updatedModel)) {
263-
updatedModels.push(updatedModel);
264-
}
262+
const newValues: FormFieldMetadataValueObject[] = this.getOtherInformationValue(otherInformation[key], key);
263+
if (isNotEmpty(newValues)) {
264+
newValues.forEach((newValue) => {
265+
const updatedModel = this.formBuilderService.updateModelValue(fieldId, newValue);
266+
if (isNotEmpty(updatedModel)) {
267+
updatedModels.push(updatedModel);
268+
}
269+
});
265270
}
266271
}
267272
}
@@ -276,43 +281,53 @@ export abstract class DsDynamicVocabularyComponent extends DynamicFormControlCom
276281
}
277282
}
278283

279-
getOtherInformationValue(value: string, key: string): FormFieldMetadataValueObject {
284+
getOtherInformationValue(value: string, key: string): FormFieldMetadataValueObject[] {
280285
if (isEmpty(value) || key === 'alternative-names' ) {
281286
return null;
282287
}
283288

284-
let returnValue;
289+
let returnValue = [];
290+
if (value.indexOf('|||') === -1) {
291+
returnValue.push(this.generateFormField(value));
292+
} else if (value.indexOf('|||') !== -1 && this.otherInfoValue) {
293+
const otherValues: string[] = value.split('|||');
294+
if (this.multiValueOnGenerator) {
295+
otherValues.forEach((tmpValue) => returnValue.push(this.generateFormField(tmpValue)));
296+
} else {
297+
const unformattedValue = this.otherInfoValuesUnformatted.find(otherInfoValue => otherInfoValue.includes(this.otherInfoValue || this.otherName));
298+
const authorityValue = hasValue(unformattedValue) ? unformattedValue.substring(unformattedValue.lastIndexOf('::') + 2) : null;
299+
let otherInfo = {};
300+
let alternativeValue: string;
301+
otherInfo[key] = value;
302+
if (hasValue(this.otherName)) {
303+
alternativeValue = otherValues[0].substring(0, otherValues[0].lastIndexOf('::'));
304+
}
305+
returnValue.push(new FormFieldMetadataValueObject(
306+
hasValue(alternativeValue) ? alternativeValue : this.otherInfoValue,
307+
null,
308+
null,
309+
authorityValue,
310+
null,
311+
null,
312+
null,
313+
otherInfo
314+
));
315+
}
316+
}
317+
return returnValue;
318+
}
319+
320+
private generateFormField(value: string): FormFieldMetadataValueObject {
285321
if (value.indexOf('::') === -1) {
286-
returnValue = new FormFieldMetadataValueObject(value);
287-
} else if (value.indexOf('|||') === -1) {
288-
returnValue = new FormFieldMetadataValueObject(
322+
return new FormFieldMetadataValueObject(value);
323+
} else {
324+
return new FormFieldMetadataValueObject(
289325
value.substring(0, value.lastIndexOf('::')),
290326
null,
291327
null,
292328
value.substring(value.lastIndexOf('::') + 2)
293329
);
294-
} else if (value.indexOf('|||') !== -1 && this.otherInfoValue) {
295-
const unformattedValue = this.otherInfoValuesUnformatted.find(otherInfoValue => otherInfoValue.includes(this.otherInfoValue || this.otherName));
296-
const authorityValue = hasValue(unformattedValue) ? unformattedValue.substring(unformattedValue.lastIndexOf('::') + 2) : null;
297-
let otherInfo = {};
298-
let alternativeValue;
299-
otherInfo[key] = value;
300-
if (hasValue(this.otherName)) {
301-
const otherValues = value.split('|||');
302-
alternativeValue = otherValues[0].substring(0, otherValues[0].lastIndexOf('::'));
303-
}
304-
returnValue = new FormFieldMetadataValueObject(
305-
hasValue(alternativeValue) ? alternativeValue : this.otherInfoValue,
306-
null,
307-
null,
308-
authorityValue,
309-
null,
310-
null,
311-
null,
312-
otherInfo
313-
);
314330
}
315-
return returnValue;
316331
}
317332

318333
private hasValidAuthority(formMetadataValue: FormFieldMetadataValueObject) {

0 commit comments

Comments
 (0)