Skip to content

Commit 2a0d775

Browse files
committed
Merged ux-plus-2023_02_x into ux-plus-2023_02_x-back-porting-UXP-103
2 parents ce9c16b + cac92a3 commit 2a0d775

82 files changed

Lines changed: 826 additions & 492 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
"build:stats": "ng build --stats-json",
1818
"build:ci": "ng config cli.cache.environment ci && yarn run build:ssr",
1919
"build:prod": "cross-env NODE_ENV=production yarn run build:ssr",
20-
"build:ssr": "npm run ng-high-memory -- build --configuration production && ng run dspace-angular:server:production",
21-
"ng-high-memory": "node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng",
20+
"build:ssr": "npm run ng-high-memory -- build --configuration production && npm run ng-high-memory -- run dspace-angular:server:production",
21+
"ng-high-memory": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng",
2222
"test": "npm run ng-high-memory -- test --source-map=true --watch=false --configuration test",
2323
"test:watch": "nodemon --exec \"npm run ng-high-memory -- test --source-map=true --watch=true --configuration test\"",
2424
"test:headless": "npm run ng-high-memory -- test --source-map=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage",

src/app/core/json-patch/builder/json-patch-operations-builder.ts

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -110,27 +110,25 @@ export class JsonPatchOperationsBuilder {
110110
operationValue = [];
111111
value.forEach((entry) => {
112112
if ((typeof entry === 'object')) {
113-
if (securityLevel != null) {
113+
if (isNotEmpty(securityLevel)) {
114114
operationValue.push(this.prepareObjectValue(entry, securityLevel));
115115
} else {
116116
operationValue.push(this.prepareObjectValue(entry));
117117
}
118-
119118
} else {
120119
operationValue.push(new FormFieldMetadataValueObject(entry, null, securityLevel));
121120
}
122121
});
123122
} else if (typeof value === 'object') {
124-
if (securityLevel != null) {
123+
if (isNotEmpty(securityLevel)) {
125124
operationValue = this.prepareObjectValue(value, securityLevel);
126125
} else {
127126
operationValue = this.prepareObjectValue(value);
128127
}
129-
130128
} else {
131129
// add the possibility to add security level when value is string
132130
// in this case security level is set on metadata value
133-
if (securityLevel != null) {
131+
if (isNotEmpty(securityLevel)) {
134132
operationValue = new FormFieldMetadataValueObject(value, null, securityLevel);
135133
} else {
136134
operationValue = new FormFieldMetadataValueObject(value, null);
@@ -143,21 +141,21 @@ export class JsonPatchOperationsBuilder {
143141
}
144142

145143
protected prepareObjectValue(value: any, securityLevel = null) {
146-
let operationValue = Object.create({});
144+
let operationValue = Object.create({});
147145
if (isEmpty(value) || value instanceof FormFieldMetadataValueObject) {
148-
if (securityLevel != null) {
149-
operationValue = {...value, securityLevel: securityLevel};
150-
} else {
146+
if (isNotEmpty(securityLevel)) {
147+
operationValue = { ...value, securityLevel: securityLevel };
148+
} else {
151149
operationValue = value;
152150
}
153151
} else if (value instanceof Date) {
154152
if (securityLevel != null) {
155-
operationValue = new FormFieldMetadataValueObject(dateToISOFormat(value), null, securityLevel);
153+
operationValue = new FormFieldMetadataValueObject(dateToISOFormat(value), null, securityLevel);
156154
} else {
157155
operationValue = new FormFieldMetadataValueObject(dateToISOFormat(value));
158156
}
159157
} else if (value instanceof VocabularyEntry) {
160-
operationValue = this.prepareAuthorityValue(value);
158+
operationValue = new FormFieldMetadataValueObject(value.value, null, value.securityLevel, value.authority);
161159
} else if (value instanceof FormFieldLanguageValueObject) {
162160
operationValue = new FormFieldMetadataValueObject(value.value, value.language, securityLevel);
163161
} else if (value.hasOwnProperty('authority')) {
@@ -170,11 +168,10 @@ export class JsonPatchOperationsBuilder {
170168
Object.keys(value)
171169
.forEach((key) => {
172170
if (typeof value[key] === 'object') {
173-
if (securityLevel != null) {
174-
operationValue[key] = this.prepareObjectValue(value[key], securityLevel);
171+
if (isNotEmpty(securityLevel)) {
172+
operationValue[key] = this.prepareObjectValue(value[key], securityLevel);
175173
} else {
176-
operationValue[key] = this.prepareObjectValue(value[key]);
177-
174+
operationValue[key] = this.prepareObjectValue(value[key]);
178175
}
179176
} else {
180177
operationValue[key] = value[key];
@@ -184,14 +181,4 @@ export class JsonPatchOperationsBuilder {
184181
return operationValue;
185182
}
186183

187-
protected prepareAuthorityValue(value: any): FormFieldMetadataValueObject {
188-
let operationValue: FormFieldMetadataValueObject;
189-
if (isNotEmpty(value.authority)) {
190-
operationValue = new FormFieldMetadataValueObject(value.value, value.language, value.securityLevel, value.authority);
191-
} else {
192-
operationValue = new FormFieldMetadataValueObject(value.value, value.language, value.securityLevel,);
193-
}
194-
return operationValue;
195-
}
196-
197184
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { Injectable } from '@angular/core';
2+
import { Observable, ReplaySubject, Subject } from 'rxjs';
3+
import { environment } from 'src/environments/environment';
4+
import { MathJaxConfig, MathService } from './math.service';
5+
6+
@Injectable({
7+
providedIn: 'root'
8+
})
9+
export class ClientMathService extends MathService {
10+
11+
protected isReady$: Subject<boolean>;
12+
13+
protected mathJaxOptions = {
14+
tex: {
15+
inlineMath: [['$', '$'], ['\\(', '\\)']]
16+
},
17+
svg: {
18+
fontCache: 'global'
19+
},
20+
startup: {
21+
typeset: false
22+
}
23+
};
24+
25+
protected mathJax: MathJaxConfig = {
26+
source: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js',
27+
id: 'MathJaxScript'
28+
};
29+
protected mathJaxFallback: MathJaxConfig = {
30+
source: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-chtml.min.js',
31+
id: 'MathJaxBackupScript'
32+
};
33+
34+
constructor() {
35+
super();
36+
37+
this.isReady$ = new ReplaySubject<boolean>();
38+
39+
void this.registerMathJaxAsync(this.mathJax)
40+
.then(() => this.isReady$.next(true))
41+
.catch(_ => {
42+
void this.registerMathJaxAsync(this.mathJaxFallback)
43+
.then(() => this.isReady$.next(true));
44+
});
45+
}
46+
47+
protected async registerMathJaxAsync(config: MathJaxConfig): Promise<any> {
48+
if (environment.markdown.mathjax) {
49+
return new Promise<void>((resolve, reject) => {
50+
51+
const optionsScript: HTMLScriptElement = document.createElement('script');
52+
optionsScript.type = 'text/javascript';
53+
optionsScript.text = `MathJax = ${JSON.stringify(this.mathJaxOptions)};`;
54+
document.head.appendChild(optionsScript);
55+
56+
const script: HTMLScriptElement = document.createElement('script');
57+
script.id = config.id;
58+
script.type = 'text/javascript';
59+
script.src = config.source;
60+
script.crossOrigin = 'anonymous';
61+
script.async = true;
62+
script.onload = () => resolve();
63+
script.onerror = error => reject(error);
64+
document.head.appendChild(script);
65+
});
66+
}
67+
return Promise.resolve();
68+
}
69+
70+
ready(): Observable<boolean> {
71+
return this.isReady$;
72+
}
73+
74+
render(element: HTMLElement) {
75+
if (environment.markdown.mathjax) {
76+
(window as any).MathJax.typesetPromise([element]);
77+
}
78+
}
79+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { TestBed } from '@angular/core/testing';
2+
import { Observable, of } from 'rxjs';
3+
import { MathService, MathJaxConfig } from './math.service';
4+
5+
export class MockMathService extends MathService {
6+
protected mathJaxOptions: any = {};
7+
protected mathJax: MathJaxConfig = { source: '', id: '' };
8+
protected mathJaxFallback: MathJaxConfig = { source: '', id: '' };
9+
10+
protected registerMathJaxAsync(config: MathJaxConfig): Promise<any> {
11+
return Promise.resolve();
12+
}
13+
14+
ready(): Observable<boolean> {
15+
return of(true);
16+
}
17+
18+
render(element: HTMLElement): void {
19+
return;
20+
}
21+
}
22+
23+
describe('MathService', () => {
24+
let service: MockMathService;
25+
26+
beforeEach(() => {
27+
TestBed.configureTestingModule({});
28+
service = new MockMathService();
29+
spyOn(service, 'render');
30+
});
31+
32+
it('should be created', () => {
33+
expect(service).toBeTruthy();
34+
});
35+
36+
it('should be ready', (done) => {
37+
service.ready().subscribe(isReady => {
38+
expect(isReady).toBe(true);
39+
done();
40+
});
41+
});
42+
43+
it('should render', () => {
44+
service.render(document.createElement('div'));
45+
expect(service.render).toHaveBeenCalled();
46+
});
47+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Observable } from 'rxjs';
2+
3+
export interface MathJaxConfig {
4+
source: string;
5+
id: string;
6+
}
7+
8+
export abstract class MathService {
9+
protected abstract mathJaxOptions: any;
10+
protected abstract mathJax: MathJaxConfig;
11+
protected abstract mathJaxFallback: MathJaxConfig;
12+
13+
protected abstract registerMathJaxAsync(config: MathJaxConfig): Promise<any>;
14+
abstract ready(): Observable<boolean>;
15+
abstract render(element: HTMLElement): void;
16+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Injectable } from '@angular/core';
2+
import { Observable, ReplaySubject, Subject } from 'rxjs';
3+
import { MathJaxConfig, MathService } from './math.service';
4+
5+
@Injectable({
6+
providedIn: 'root'
7+
})
8+
export class ServerMathService extends MathService {
9+
10+
protected signal: Subject<boolean>;
11+
12+
protected mathJaxOptions = {};
13+
14+
protected mathJax: MathJaxConfig = {
15+
source: '',
16+
id: ''
17+
};
18+
protected mathJaxFallback: MathJaxConfig = {
19+
source: '',
20+
id: ''
21+
};
22+
23+
constructor() {
24+
super();
25+
26+
this.signal = new ReplaySubject<boolean>();
27+
this.signal.next(true);
28+
}
29+
30+
protected async registerMathJaxAsync(config: MathJaxConfig): Promise<any> {
31+
return Promise.resolve();
32+
}
33+
34+
ready(): Observable<boolean> {
35+
return this.signal;
36+
}
37+
38+
render(element: HTMLElement) {
39+
return;
40+
}
41+
}

src/app/core/submission/vocabularies/models/vocabulary-entry-detail.model.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import { Observable } from 'rxjs';
12
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
23

34
import { HALLink } from '../../../shared/hal-link.model';
45
import { VOCABULARY_ENTRY_DETAIL } from './vocabularies.resource-type';
5-
import { typedObject } from '../../../cache/builders/build-decorators';
6+
import { link, typedObject } from '../../../cache/builders/build-decorators';
67
import { VocabularyEntry } from './vocabulary-entry.model';
8+
import { RemoteData } from '../../../data/remote-data';
9+
import { PaginatedList } from '../../../data/paginated-list.model';
710

811
/**
912
* Model class for a VocabularyEntryDetail
@@ -33,7 +36,21 @@ export class VocabularyEntryDetail extends VocabularyEntry {
3336
self: HALLink;
3437
vocabulary: HALLink;
3538
parent: HALLink;
36-
children
39+
children: HALLink;
3740
};
3841

42+
/**
43+
* The submitter for this SubmissionObject
44+
* Will be undefined unless the submitter {@link HALLink} has been resolved.
45+
*/
46+
@link(VOCABULARY_ENTRY_DETAIL)
47+
parent?: Observable<RemoteData<VocabularyEntryDetail>>;
48+
49+
/**
50+
* The submitter for this SubmissionObject
51+
* Will be undefined unless the submitter {@link HALLink} has been resolved.
52+
*/
53+
@link(VOCABULARY_ENTRY_DETAIL, true)
54+
children?: Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>>;
55+
3956
}

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ export class VocabularyEntry extends ListableObject {
5050
@autoserialize
5151
securityLevel: number;
5252

53-
54-
5553
/**
5654
* A string representing the kind of vocabulary entry
5755
*/

src/app/core/submission/vocabularies/vocabulary-entry-details.data.service.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,29 @@ export class VocabularyEntryDetailsDataService extends IdentifiableDataService<V
5454
* requested after the response becomes stale
5555
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
5656
* {@link HALLink}s should be automatically resolved
57-
* @return {Observable<RemoteData<PaginatedList<T>>>}
57+
* @return {Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>>}
5858
* Return an observable that emits object list
5959
*/
6060
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<VocabularyEntryDetail>[]): Observable<RemoteData<PaginatedList<VocabularyEntryDetail>>> {
6161
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
6262
}
6363

64+
/**
65+
* Returns an observable of {@link RemoteData} of an object, based on its ID, with a list of
66+
* {@link FollowLinkConfig}, to automatically resolve {@link HALLink}s of the object
67+
* @param id ID of object we want to retrieve
68+
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
69+
* no valid cached version. Defaults to true
70+
* @param reRequestOnStale Whether or not the request should automatically be re-
71+
* requested after the response becomes stale
72+
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
73+
* {@link HALLink}s should be automatically resolved
74+
*/
75+
findById(id: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<VocabularyEntryDetail>[]): Observable<RemoteData<VocabularyEntryDetail>> {
76+
const href$ = this.getIDHrefObs(id, ...linksToFollow);
77+
return this.findByHref(href$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
78+
}
79+
6480
/**
6581
* Make a new FindListRequest with given search method
6682
*

0 commit comments

Comments
 (0)