From c0c12ac651707d9ecf65a5207c9e14ce36656a76 Mon Sep 17 00:00:00 2001 From: milanmajchrak <90026355+milanmajchrak@users.noreply.github.com> Date: Fri, 20 Sep 2024 08:20:04 +0200 Subject: [PATCH 01/48] Removed dynamic overflow because every content must be scrollable.. (#716) --- .../file-description/file-description.component.html | 2 +- .../file-description/file-description.component.ts | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.html b/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.html index 01bb17c657b..2691c441521 100644 --- a/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.html +++ b/src/app/item-page/simple/field-components/preview-section/file-description/file-description.component.html @@ -59,7 +59,7 @@   -
+
\ No newline at end of file +
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts index 5f7e2e3e228..656c892a0c7 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts @@ -67,7 +67,7 @@ export class DsDynamicTypeBindRelationService { throw new Error(`FormControl ${model.id} cannot depend on itself`); } - const bindModel: DynamicFormControlModel = this.formBuilderService.getTypeBindModel(); + const bindModel: DynamicFormControlModel = this.formBuilderService.getTypeBindModel(rel?.id); if (model && !models.some((modelElement) => modelElement === bindModel)) { models.push(bindModel); @@ -96,7 +96,7 @@ export class DsDynamicTypeBindRelationService { // like relation group component and submission section form component). // This model (DynamicRelationGroupModel) contains eg. mandatory field, formConfiguration, relationFields, // submission scope, form/section type and other high level properties - const bindModel: any = this.formBuilderService.getTypeBindModel(); + const bindModel: any = this.formBuilderService.getTypeBindModel(condition?.id); let values: string[]; let bindModelValue = bindModel.value; diff --git a/src/app/shared/form/builder/form-builder.service.spec.ts b/src/app/shared/form/builder/form-builder.service.spec.ts index 5e045c88ed5..657f7c1d0e2 100644 --- a/src/app/shared/form/builder/form-builder.service.spec.ts +++ b/src/app/shared/form/builder/form-builder.service.spec.ts @@ -912,8 +912,9 @@ describe('FormBuilderService test suite', () => { }); it(`should request the ${typeFieldProp} property and set value "dc_type"`, () => { - const typeValue = service.getTypeField(); - expect(configSpy.findByPropertyName).toHaveBeenCalledTimes(1); + const typeValue = service.getTypeField('dc.type'); + // Two times because the first time is when the service is created and the second time is when the method is called + expect(configSpy.findByPropertyName).toHaveBeenCalledTimes(2); expect(configSpy.findByPropertyName).toHaveBeenCalledWith(typeFieldProp); expect(typeValue).toEqual('dc_type'); }); diff --git a/src/app/shared/form/builder/form-builder.service.ts b/src/app/shared/form/builder/form-builder.service.ts index 3244d3ae283..45fb6a55100 100644 --- a/src/app/shared/form/builder/form-builder.service.ts +++ b/src/app/shared/form/builder/form-builder.service.ts @@ -29,7 +29,7 @@ import { isNotEmpty, isNotNull, isNotUndefined, - isNull + isNull, isUndefined } from '../../empty.util'; import { DynamicQualdropModel } from './ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model'; import { SubmissionFormsModel } from '../../../core/config/models/config-submission-forms.model'; @@ -49,11 +49,20 @@ import { COMPLEX_GROUP_SUFFIX, DynamicComplexModel } from './ds-dynamic-form-ui/models/ds-dynamic-complex.model'; +import { FormRowModel } from '../../../core/config/models/config-submission-form.model'; + +/** + * The key for the default type bind field. {'default': 'dc_type'} + */ +export const TYPE_BIND_DEFAULT = 'default'; @Injectable() export class FormBuilderService extends DynamicFormService { - private typeBindModel: DynamicFormControlModel; + /** + * This map contains the type bind model + */ + private typeBindModel: Map; /** * This map contains the active forms model @@ -68,7 +77,7 @@ export class FormBuilderService extends DynamicFormService { /** * This is the field to use for type binding */ - private typeField: string; + private typeFields: Map; constructor( componentService: DynamicFormComponentService, @@ -79,6 +88,10 @@ export class FormBuilderService extends DynamicFormService { super(componentService, validationService); this.formModels = new Map(); this.formGroups = new Map(); + this.typeFields = new Map(); + this.typeBindModel = new Map(); + + this.typeFields.set(TYPE_BIND_DEFAULT, 'dc_type'); // If optional config service was passed, perform an initial set of type field (default dc_type) for type binds if (hasValue(this.configService)) { this.setTypeBindFieldFromConfig(); @@ -96,55 +109,76 @@ export class FormBuilderService extends DynamicFormService { return {$event, context, control: control, group: group, model: model, type}; } - getTypeBindModel() { - return this.typeBindModel; + /** + * Get the type bind model associated to the `type-bind` + * + * @param typeBingField the special `` + * @returns the default (dc_type) type bind model or the one associated to the `type-bind` field + */ + getTypeBindModel(typeBingField: string): DynamicFormControlModel { + let typeBModelKey = this.typeFields.get(typeBingField); + if (isUndefined(typeBModelKey)) { + typeBModelKey = this.typeFields.get(TYPE_BIND_DEFAULT); + } + return this.typeBindModel.get(typeBModelKey); } setTypeBindModel(model: DynamicFormControlModel) { - this.typeBindModel = model; + this.typeBindModel.set(model.id, model); } - findById(id: string, groupModel: DynamicFormControlModel[], arrayIndex = null): DynamicFormControlModel | null { + findById(id: string | string[], groupModel: DynamicFormControlModel[], arrayIndex = null): DynamicFormControlModel | null { let result = null; - const findByIdFn = (findId: string, findGroupModel: DynamicFormControlModel[], findArrayIndex): void => { + const findByIdFn = (findId: string | string [], findGroupModel: DynamicFormControlModel[], findArrayIndex): void => { for (const controlModel of findGroupModel) { - if (controlModel.id === findId) { - - if (this.isArrayGroup(controlModel) && isNotNull(findArrayIndex)) { - result = (controlModel as DynamicFormArrayModel).get(findArrayIndex); - } else { - result = controlModel; - } - break; + const findIdArray = []; + // If the id is NOT an array, push it into array because we need to iterate over the array + if (!Array.isArray(findId)) { + findIdArray.push(findId); + } else { + findIdArray.push(...findId); } - if (this.isConcatGroup(controlModel)) { - if (controlModel.id.match(new RegExp(findId + CONCAT_GROUP_SUFFIX))) { - result = (controlModel as DynamicConcatModel); + for (const findIdIt of findIdArray) { + if (controlModel.id === findIdIt) { + + if (this.isArrayGroup(controlModel) && isNotNull(findArrayIndex)) { + result = (controlModel as DynamicFormArrayModel).get(findArrayIndex); + } else { + result = controlModel; + } break; } - } - if (this.isComplexGroup(controlModel)) { - const regex = new RegExp(findId + COMPLEX_GROUP_SUFFIX); - if (controlModel.id.match(regex)) { - result = (controlModel as DynamicComplexModel); - break; + if (this.isConcatGroup(controlModel)) { + if (controlModel.id.match(new RegExp(findIdIt + CONCAT_GROUP_SUFFIX))) { + result = (controlModel as DynamicConcatModel); + break; + } } - } - if (this.isGroup(controlModel)) { - findByIdFn(findId, (controlModel as DynamicFormGroupModel).group, findArrayIndex); - } + if (this.isComplexGroup(controlModel)) { + const regex = new RegExp(findIdIt + COMPLEX_GROUP_SUFFIX); + if (controlModel.id.match(regex)) { + result = (controlModel as DynamicComplexModel); + break; + } + } + + if (this.isGroup(controlModel)) { + findByIdFn(findIdIt, (controlModel as DynamicFormGroupModel).group, findArrayIndex); + } - if (this.isArrayGroup(controlModel) - && (isNull(findArrayIndex) || (controlModel as DynamicFormArrayModel).size > (findArrayIndex))) { - const index = (isNull(findArrayIndex)) ? 0 : findArrayIndex; - findByIdFn(findId, (controlModel as DynamicFormArrayModel).get(index).group, index); + if (this.isArrayGroup(controlModel) + && (isNull(findArrayIndex) || (controlModel as DynamicFormArrayModel).size > (findArrayIndex))) { + const index = (isNull(findArrayIndex)) ? 0 : findArrayIndex; + findByIdFn(findIdIt, (controlModel as DynamicFormArrayModel).get(index).group, index); + } } + } }; @@ -297,9 +331,9 @@ export class FormBuilderService extends DynamicFormService { let rows: DynamicFormControlModel[] = []; const rawData = typeof json === 'string' ? JSON.parse(json, parseReviver) : json; if (rawData.rows && !isEmpty(rawData.rows)) { - rawData.rows.forEach((currentRow) => { + rawData.rows.forEach((currentRow: FormRowModel) => { const rowParsed = this.rowParser.parse(submissionId, currentRow, scopeUUID, sectionData, submissionScope, - readOnly, this.getTypeField()); + readOnly); if (isNotNull(rowParsed)) { if (Array.isArray(rowParsed)) { rows = rows.concat(rowParsed); @@ -311,7 +345,7 @@ export class FormBuilderService extends DynamicFormService { } if (hasNoValue(typeBindModel)) { - typeBindModel = this.findById(this.typeField, rows); + typeBindModel = this.findById(Array.from(this.typeFields.values()), rows); } if (hasValue(typeBindModel)) { @@ -517,36 +551,59 @@ export class FormBuilderService extends DynamicFormService { /** * Get the type bind field from config */ - setTypeBindFieldFromConfig(): void { + setTypeBindFieldFromConfig(metadataField: string = null): void { this.configService.findByPropertyName('submit.type-bind.field').pipe( getFirstCompletedRemoteData(), ).subscribe((remoteData: any) => { // make sure we got a success response from the backend if (!remoteData.hasSucceeded) { - this.typeField = 'dc_type'; return; } - // Read type bind value from response and set if non-empty - const typeFieldConfig = remoteData.payload.values[0]; - if (isEmpty(typeFieldConfig)) { - this.typeField = 'dc_type'; - } else { - this.typeField = typeFieldConfig.replace(/\./g, '_'); - } + + // All cfg property values + const typeFieldConfigValues = remoteData.payload.values; + let typeFieldConfigValue = ''; + // Iterate over each config property value + typeFieldConfigValues.forEach((typeFieldConfig: string) => { + // Check if the typeFieldConfig contains the '=>' delimiter + if (typeFieldConfig.includes('=>')) { + // Split the typeFieldConfig into parts based on the delimiter + const [metadataFieldConfigPart, valuePart] = typeFieldConfig.split('=>'); + // Process only custom type-bind fields + if (isNotEmpty(valuePart)) { + // Replace '.' with '_' in the valuePart + const normalizedValuePart = valuePart.replace(/\./g, '_'); + + // Set the value in the typeFields map + this.typeFields.set(metadataFieldConfigPart, normalizedValuePart); + + if (metadataFieldConfigPart === metadataField) { + typeFieldConfigValue = valuePart; + } + } + } else { + // If no delimiter is found, use the entire typeFieldConfig as the default value + typeFieldConfigValue = typeFieldConfig; + } + + // Always update the typeFields map with the default value, normalized + this.typeFields.set(TYPE_BIND_DEFAULT, typeFieldConfigValue.replace(/\./g, '_')); + }); }); } /** * Get type field. If the type isn't already set, and a ConfigurationDataService is provided, set (with subscribe) - * from back end. Otherwise, get/set a default "dc_type" value + * from back end. Otherwise, get/set a default "dc_type" value or specific value from the typeFields map. */ - getTypeField(): string { - if (hasValue(this.configService) && hasNoValue(this.typeField)) { - this.setTypeBindFieldFromConfig(); - } else if (hasNoValue(this.typeField)) { - this.typeField = 'dc_type'; + getTypeField(metadataField: string): string { + if (hasValue(this.configService) && isEmpty(this.typeFields.values())) { + this.setTypeBindFieldFromConfig(metadataField); + } else if (hasNoValue(this.typeFields.get(TYPE_BIND_DEFAULT))) { + this.typeFields.set(TYPE_BIND_DEFAULT, 'dc_type'); } - return this.typeField; + + return this.typeFields.get(metadataField) || this.typeFields.get(TYPE_BIND_DEFAULT); } } diff --git a/src/app/shared/form/builder/parsers/row-parser.spec.ts b/src/app/shared/form/builder/parsers/row-parser.spec.ts index 1f9bde8a7fb..92084f212d1 100644 --- a/src/app/shared/form/builder/parsers/row-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/row-parser.spec.ts @@ -339,7 +339,7 @@ describe('RowParser test suite', () => { it('should return a DynamicRowGroupModel object', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly, typeField); + const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly); expect(rowModel instanceof DynamicRowGroupModel).toBe(true); }); @@ -347,7 +347,7 @@ describe('RowParser test suite', () => { it('should return a row with three fields', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly, typeField); + const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly); expect((rowModel as DynamicRowGroupModel).group.length).toBe(3); }); @@ -355,7 +355,7 @@ describe('RowParser test suite', () => { it('should return a DynamicRowArrayModel object', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row2, scopeUUID, initFormValues, submissionScope, readOnly, typeField); + const rowModel = parser.parse(submissionId, row2, scopeUUID, initFormValues, submissionScope, readOnly); expect(rowModel instanceof DynamicRowArrayModel).toBe(true); }); @@ -363,7 +363,7 @@ describe('RowParser test suite', () => { it('should return a row that contains only scoped fields', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row3, scopeUUID, initFormValues, submissionScope, readOnly, typeField); + const rowModel = parser.parse(submissionId, row3, scopeUUID, initFormValues, submissionScope, readOnly); expect((rowModel as DynamicRowGroupModel).group.length).toBe(1); }); @@ -371,7 +371,7 @@ describe('RowParser test suite', () => { it('should be able to parse a dropdown combo field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row4, scopeUUID, initFormValues, submissionScope, readOnly, typeField); + const rowModel = parser.parse(submissionId, row4, scopeUUID, initFormValues, submissionScope, readOnly); expect(rowModel).toBeDefined(); }); @@ -379,7 +379,7 @@ describe('RowParser test suite', () => { it('should be able to parse a lookup-name field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row5, scopeUUID, initFormValues, submissionScope, readOnly, typeField); + const rowModel = parser.parse(submissionId, row5, scopeUUID, initFormValues, submissionScope, readOnly); expect(rowModel).toBeDefined(); }); @@ -387,7 +387,7 @@ describe('RowParser test suite', () => { it('should be able to parse a list field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row6, scopeUUID, initFormValues, submissionScope, readOnly, typeField); + const rowModel = parser.parse(submissionId, row6, scopeUUID, initFormValues, submissionScope, readOnly); expect(rowModel).toBeDefined(); }); @@ -395,7 +395,7 @@ describe('RowParser test suite', () => { it('should be able to parse a date field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row7, scopeUUID, initFormValues, submissionScope, readOnly, typeField); + const rowModel = parser.parse(submissionId, row7, scopeUUID, initFormValues, submissionScope, readOnly); expect(rowModel).toBeDefined(); }); @@ -403,7 +403,7 @@ describe('RowParser test suite', () => { it('should be able to parse a tag field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row8, scopeUUID, initFormValues, submissionScope, readOnly, typeField); + const rowModel = parser.parse(submissionId, row8, scopeUUID, initFormValues, submissionScope, readOnly); expect(rowModel).toBeDefined(); }); @@ -411,7 +411,7 @@ describe('RowParser test suite', () => { it('should be able to parse a textarea field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row9, scopeUUID, initFormValues, submissionScope, readOnly, typeField); + const rowModel = parser.parse(submissionId, row9, scopeUUID, initFormValues, submissionScope, readOnly); expect(rowModel).toBeDefined(); }); @@ -419,7 +419,7 @@ describe('RowParser test suite', () => { it('should be able to parse a group field', () => { const parser = new RowParser(undefined); - const rowModel = parser.parse(submissionId, row10, scopeUUID, initFormValues, submissionScope, readOnly, typeField); + const rowModel = parser.parse(submissionId, row10, scopeUUID, initFormValues, submissionScope, readOnly); expect(rowModel).toBeDefined(); }); diff --git a/src/app/shared/form/builder/parsers/row-parser.ts b/src/app/shared/form/builder/parsers/row-parser.ts index 3f5b4a04c61..36c6aeef3f6 100644 --- a/src/app/shared/form/builder/parsers/row-parser.ts +++ b/src/app/shared/form/builder/parsers/row-parser.ts @@ -34,8 +34,7 @@ export class RowParser { scopeUUID, initFormValues: any, submissionScope, - readOnly: boolean, - typeField: string): DynamicRowGroupModel { + readOnly: boolean): DynamicRowGroupModel { let fieldModel: any = null; let parsedResult = null; const config: DynamicFormGroupModelConfig = { @@ -52,11 +51,12 @@ export class RowParser { readOnly: readOnly, submissionScope: submissionScope, collectionUUID: scopeUUID, - typeField: typeField + typeField: '' }; // Iterate over row's fields scopedFields.forEach((fieldData: FormFieldModel) => { + parserOptions.typeField = fieldData?.selectableMetadata[0]?.metadata; const layoutFieldClass = (fieldData.style || layoutDefaultGridClass) + layoutClass; const parserProvider = ParserFactory.getProvider(fieldData.input.type as ParserType); From 1178fae43dda8ff90736522f8fb969dc78d01a49 Mon Sep 17 00:00:00 2001 From: milanmajchrak <90026355+milanmajchrak@users.noreply.github.com> Date: Wed, 25 Sep 2024 12:51:57 +0200 Subject: [PATCH 04/48] Updated error message when tests failed --- .github/workflows/deploy.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3644944c947..b1018b4ed6e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -177,7 +177,7 @@ jobs: echo $RES # if last result is not success, return -1 and fail if [[ $RES != \"success\" ]]; then - echo "playwright tests have failed! check appropriate action run" + echo "playwright tests have failed! check appropriate action run in the dspace-ui-tests repository" exit 1 fi; @@ -210,7 +210,7 @@ jobs: echo $RES # if last result is not success, return -1 and fail if [[ $RES != \"success\" ]]; then - echo "rest-tests have failed! check appropriate action run" + echo "rest-tests have failed! check appropriate action run in the dspace-rest-test repository" exit 1 fi; @@ -249,7 +249,7 @@ jobs: # if last result is not success, return -1 and fail if [[ $RES != \"success\" ]]; then - echo "playwright tests have failed! check appropriate action run" + echo "playwright tests have failed! check appropriate action run in the dspace-ui-tests repository" exit 1 fi; @@ -282,6 +282,6 @@ jobs: echo $RES # if last result is not success, return -1 and fail if [[ $RES != \"success\" ]]; then - echo "rest-tests have failed! check appropriate action run" + echo "rest-tests have failed! check appropriate action run in the dspace-rest-test repository" exit 1 fi; From 083f2b5597b4b6194ef69ac3ab053b81271f4290 Mon Sep 17 00:00:00 2001 From: Jozef Misutka <332350+vidiecan@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:45:22 +0200 Subject: [PATCH 05/48] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 053d55b040f..0be2f23c29d 100644 --- a/README.md +++ b/README.md @@ -569,3 +569,8 @@ The full license is available in the [LICENSE](LICENSE) file or online at http:/ DSpace uses third-party libraries which may be distributed under different licenses. Those licenses are listed in the [LICENSES_THIRD_PARTY](LICENSES_THIRD_PARTY) file. + +Additional tools +---------------- + +This project is tested with BrowserStack. From b2a5d6eb1bcbf5f5c3c322f5908763b7af9f23bd Mon Sep 17 00:00:00 2001 From: milanmajchrak <90026355+milanmajchrak@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:12:22 +0200 Subject: [PATCH 06/48] UFAL/Show sesznam license on approval page (#722) * Added static files from the SEZNAM license. * Refactored fetching html content from the static files. The logic was moved into the common service. * Show Seznam static license on approval page. * Updated constant name LICENSE_NAME_SEZNAM - added the _CZ to make it more clear that is Czech license --- ...arin-license-agreement-page.component.html | 4 + ...clarin-license-agreement-page.component.ts | 29 ++++++- src/app/shared/html-content.service.ts | 37 ++++++++- .../static-page/static-page.component.spec.ts | 13 +-- src/app/static-page/static-page.component.ts | 34 +------- src/static-files/cs/szn-dataset-license.html | 82 +++++++++++++++++++ src/static-files/szn-dataset-license.html | 48 +++++++++++ 7 files changed, 205 insertions(+), 42 deletions(-) create mode 100644 src/static-files/cs/szn-dataset-license.html create mode 100644 src/static-files/szn-dataset-license.html diff --git a/src/app/bitstream-page/clarin-license-agreement-page/clarin-license-agreement-page.component.html b/src/app/bitstream-page/clarin-license-agreement-page/clarin-license-agreement-page.component.html index b73952718d5..99d77f87780 100644 --- a/src/app/bitstream-page/clarin-license-agreement-page/clarin-license-agreement-page.component.html +++ b/src/app/bitstream-page/clarin-license-agreement-page/clarin-license-agreement-page.component.html @@ -14,6 +14,10 @@