From 5bf34b868cabce3d87d2bf64a704a1617b6b3e7b Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 10 Jun 2022 13:08:36 +0200 Subject: [PATCH 01/19] added missing keys workspace.search.results.head and workflow.search.results.head --- src/assets/i18n/de.json5 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index 23ab3a29e5..526da437b0 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -5358,10 +5358,14 @@ "virtual-metadata.delete-relationship.modal-head": "Wählen Sie die Items aus, deren virtuelle Metadaten Sie als echte Metadaten speichern möchten.", - + // "workspace.search.results.head": "Your submissions", + "workspace.search.results.head": "Deine Einreichungen", + // "workflowAdmin.search.results.head": "Administer Workflow", "workflowAdmin.search.results.head": "Workflow verwalten", - + + // "workflow.search.results.head": "Workflow tasks", + "workflow.search.results.head": "Workflow-Aufgaben", // "workflow-item.delete.notification.success.title": "Deleted", From afe798942bf39f39ef055bd4246d6e4be6692b7f Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 10 Jun 2022 13:15:53 +0200 Subject: [PATCH 02/19] =?UTF-8?q?harmonized=20german=20translations=20("Ih?= =?UTF-8?q?re=20Ver=C3=B6ffentlichungen")?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/i18n/de.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index 526da437b0..42e0d083e7 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -5359,7 +5359,7 @@ // "workspace.search.results.head": "Your submissions", - "workspace.search.results.head": "Deine Einreichungen", + "workspace.search.results.head": "Ihre Veröffentlichungen", // "workflowAdmin.search.results.head": "Administer Workflow", "workflowAdmin.search.results.head": "Workflow verwalten", From 68d162432b114425d3d96da1b238a986e3414d6d Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 10 Jun 2022 14:22:52 +0200 Subject: [PATCH 03/19] added two missing keys --- src/assets/i18n/de.json5 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index 42e0d083e7..effe6932fd 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -5279,6 +5279,13 @@ // "submission.workflow.tasks.pool.show-detail": "Show detail", "submission.workflow.tasks.pool.show-detail": "Details anzeigen", + // "submission.workspace.generic.view": "View", + "submission.workspace.generic.view": "Anzeige", + + // "submission.workspace.generic.view-help": "Select this option to view the item's metadata.", + "submission.workspace.generic.view-help": "Wählen Sie diese Option, um die Metadaten des Items anzuzeigen.", + + // "thumbnail.default.alt": "Thumbnail Image", "thumbnail.default.alt": "Vorschaubild", From 5d130156e8633ef8b661871bcd526703c82fd71b Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 22 Jun 2022 15:43:23 +0200 Subject: [PATCH 04/19] [CST-6174] Fix issue with bitstream access condition edit form when maxStartDate/maxEndDate are missing --- .../section-upload-file-edit.component.ts | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts index 3a43e718a0..293835c848 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts @@ -15,7 +15,9 @@ import { OR_OPERATOR } from '@ng-dynamic-forms/core'; -import { WorkspaceitemSectionUploadFileObject } from '../../../../../core/submission/models/workspaceitem-section-upload-file.model'; +import { + WorkspaceitemSectionUploadFileObject +} from '../../../../../core/submission/models/workspaceitem-section-upload-file.model'; import { FormBuilderService } from '../../../../../shared/form/builder/form-builder.service'; import { BITSTREAM_ACCESS_CONDITION_GROUP_CONFIG, @@ -43,10 +45,16 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { filter, mergeMap, take } from 'rxjs/operators'; import { dateToISOFormat } from '../../../../../shared/date.util'; import { SubmissionObject } from '../../../../../core/submission/models/submission-object.model'; -import { WorkspaceitemSectionUploadObject } from '../../../../../core/submission/models/workspaceitem-section-upload.model'; +import { + WorkspaceitemSectionUploadObject +} from '../../../../../core/submission/models/workspaceitem-section-upload.model'; import { JsonPatchOperationsBuilder } from '../../../../../core/json-patch/builder/json-patch-operations-builder'; -import { SubmissionJsonPatchOperationsService } from '../../../../../core/submission/submission-json-patch-operations.service'; -import { JsonPatchOperationPathCombiner } from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner'; +import { + SubmissionJsonPatchOperationsService +} from '../../../../../core/submission/submission-json-patch-operations.service'; +import { + JsonPatchOperationPathCombiner +} from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { SectionUploadService } from '../../section-upload.service'; import { Subscription } from 'rxjs'; @@ -251,7 +259,7 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { endDateControl?.setValue(null); if (showGroups) { - if (accessCondition.hasStartDate) { + if (accessCondition.hasStartDate && accessCondition.maxStartDate) { const startDateModel = this.formBuilderService.findById( 'startDate', (model.parent as DynamicFormArrayGroupModel).group) as DynamicDateControlModel; @@ -263,7 +271,7 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { day: min.getUTCDate() }; } - if (accessCondition.hasEndDate) { + if (accessCondition.hasEndDate && accessCondition.maxEndDate) { const endDateModel = this.formBuilderService.findById( 'endDate', (model.parent as DynamicFormArrayGroupModel).group) as DynamicDateControlModel; @@ -274,6 +282,7 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { month: max.getUTCMonth() + 1, day: max.getUTCDate() }; + } } } From 8c14822eefe119b093e4034fd6b51d35ae2899eb Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 22 Jun 2022 15:46:30 +0200 Subject: [PATCH 05/19] [CST-6174] Fix issue with item access condition which doesn't consider the maxStartDate/maxEndDate --- .../accesses/section-accesses.component.ts | 67 +++++++++++++------ 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/src/app/submission/sections/accesses/section-accesses.component.ts b/src/app/submission/sections/accesses/section-accesses.component.ts index 40863e7ef9..c59f9af2d0 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.ts +++ b/src/app/submission/sections/accesses/section-accesses.component.ts @@ -41,7 +41,9 @@ import { FORM_ACCESS_CONDITION_TYPE_LAYOUT } from './section-accesses.model'; import { hasValue, isNotEmpty, isNotNull } from '../../../shared/empty.util'; -import { WorkspaceitemSectionAccessesObject } from '../../../core/submission/models/workspaceitem-section-accesses.model'; +import { + WorkspaceitemSectionAccessesObject +} from '../../../core/submission/models/workspaceitem-section-accesses.model'; import { SubmissionAccessesConfigService } from '../../../core/config/submission-accesses-config.service'; import { getFirstSucceededRemoteData } from '../../../core/shared/operators'; import { FormComponent } from '../../../shared/form/form.component'; @@ -50,8 +52,12 @@ import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder import { SectionFormOperationsService } from '../form/section-form-operations.service'; import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; import { AccessesConditionOption } from '../../../core/config/models/config-accesses-conditions-options.model'; -import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service'; +import { + SubmissionJsonPatchOperationsService +} from '../../../core/submission/submission-json-patch-operations.service'; import { dateToISOFormat } from '../../../shared/date.util'; +import { DynamicFormControlCondition } from '@ng-dynamic-forms/core/lib/model/misc/dynamic-form-control-relation.model'; +import { DynamicDateControlValue } from '@ng-dynamic-forms/core/lib/model/dynamic-date-control.model'; /** * This component represents a section for managing item's access conditions. @@ -322,40 +328,61 @@ export class SubmissionSectionAccessesComponent extends SectionModelComponent { } accessConditionTypeModelConfig.options = accessConditionTypeOptions; - // Dynamically assign of relation in config. For startdate, endDate, groups. - const hasStart = []; - const hasEnd = []; - const hasGroups = []; + // Dynamically assign of relation in config. For startDate and endDate. + const startDateCondition: DynamicFormControlCondition[] = []; + const endDateCondition: DynamicFormControlCondition[] = []; + let maxStartDate: DynamicDateControlValue; + let maxEndDate: DynamicDateControlValue; this.availableAccessConditionOptions.forEach((condition) => { - const showStart: boolean = condition.hasStartDate === true; - const showEnd: boolean = condition.hasEndDate === true; - const showGroups: boolean = showStart || showEnd; - if (showStart) { - hasStart.push({ id: 'name', value: condition.name }); + + if (condition.hasStartDate) { + startDateCondition.push({ id: 'name', value: condition.name }); + if (condition.maxStartDate) { + const min = new Date(condition.maxStartDate); + maxStartDate = { + year: min.getUTCFullYear(), + month: min.getUTCMonth() + 1, + day: min.getUTCDate() + }; + } } - if (showEnd) { - hasEnd.push({ id: 'name', value: condition.name }); - } - if (showGroups) { - hasGroups.push({ id: 'name', value: condition.name }); + if (condition.hasEndDate) { + endDateCondition.push({ id: 'name', value: condition.name }); + if (condition.maxEndDate) { + const max = new Date(condition.maxEndDate); + maxEndDate = { + year: max.getUTCFullYear(), + month: max.getUTCMonth() + 1, + day: max.getUTCDate() + }; + } } }); - const confStart = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasStart }] }; - const confEnd = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasEnd }] }; + const confStart = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: startDateCondition }] }; + const confEnd = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: endDateCondition }] }; + const hasStartDate = startDateCondition.length > 0; + const hasEndDate = endDateCondition.length > 0; accessConditionsArrayConfig.groupFactory = () => { const type = new DynamicSelectModel(accessConditionTypeModelConfig, FORM_ACCESS_CONDITION_TYPE_LAYOUT); const startDateConfig = Object.assign({}, FORM_ACCESS_CONDITION_START_DATE_CONFIG, confStart); + if (maxStartDate) { + startDateConfig.max = maxStartDate; + } + const endDateConfig = Object.assign({}, FORM_ACCESS_CONDITION_END_DATE_CONFIG, confEnd); + if (maxEndDate) { + endDateConfig.max = maxEndDate; + } const startDate = new DynamicDatePickerModel(startDateConfig, FORM_ACCESS_CONDITION_START_DATE_LAYOUT); const endDate = new DynamicDatePickerModel(endDateConfig, FORM_ACCESS_CONDITION_END_DATE_LAYOUT); const accessConditionGroupConfig = Object.assign({}, ACCESS_CONDITION_GROUP_CONFIG); accessConditionGroupConfig.group = [type]; - if (hasStart.length > 0) { + if (hasStartDate) { accessConditionGroupConfig.group.push(startDate); } - if (hasEnd.length > 0) { + if (hasEndDate) { accessConditionGroupConfig.group.push(endDate); } return [new DynamicFormGroupModel(accessConditionGroupConfig, ACCESS_CONDITION_GROUP_LAYOUT)]; From 5e15eb07f7fb5effa65712d3cafa8dfd043a939e Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 23 Jun 2022 10:40:33 +0200 Subject: [PATCH 06/19] fixed typo in community.create.sub-head --- src/assets/i18n/de.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index effe6932fd..4e61a9727b 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -1375,7 +1375,7 @@ "community.create.notifications.success": "Bereich erfolgreich angelegt", // "community.create.sub-head": "Create a Sub-Community for Community {{ parent }}", - "community.create.sub-head": "Teilbeirech im Bereich {{ parent }} anlegen", + "community.create.sub-head": "Teilbereich im Bereich {{ parent }} anlegen", // "community.curate.header": "Curate Community: {{community}}", "community.curate.header": "Bereich: {{community}} pflegen", From 395b2762b538fc9439bc4f27b08c10e64cff6a82 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 23 Jun 2022 10:47:32 +0200 Subject: [PATCH 07/19] added submission.edit.breadcrumbs --- src/assets/i18n/de.json5 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index 4e61a9727b..f79d6f9883 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -4615,6 +4615,8 @@ "statistics.table.header.views": "Aufrufe", + // "submission.edit.breadcrumbs": "Edit Submission", + "submission.edit.breadcrumbs": "Einreichung bearbeiten", // "submission.edit.title": "Edit Submission", "submission.edit.title": "Einreichung bearbeiten", From 146bbd80423c132c4c92ea92b589df64fe0cde48 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 23 Jun 2022 10:49:26 +0200 Subject: [PATCH 08/19] translation harmonization --- src/assets/i18n/de.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index f79d6f9883..dce700c795 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -1562,7 +1562,7 @@ "community.form.errors.title.required": "Bitte geben Sie einen Namen für den Bereich ein.", // "community.form.rights": "Copyright text (HTML)", - "community.form.rights": "Copyrighterklärung (HTML)", + "community.form.rights": "Copyright Text (HTML)", // "community.form.tableofcontents": "News (HTML)", "community.form.tableofcontents": "Neuigkeiten (HTML)", From f9c6f5f5b674665eb2650129ebe35edd311df97b Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 23 Jun 2022 11:36:21 +0200 Subject: [PATCH 09/19] added translation keys submission.general.info.* --- src/assets/i18n/de.json5 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index dce700c795..30c0a48bde 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -4642,6 +4642,12 @@ // "submission.general.discard.submit": "Discard", "submission.general.discard.submit": "Verwerfen", + // "submission.general.info.saved": "Saved", + "submission.general.info.saved": "Gespeichert", + + // "submission.general.info.pending-changes": "Unsaved changes", + "submission.general.info.pending-changes": "Ungespeicherte Änderungen", + // "submission.general.save": "Save", "submission.general.save": "Speichern", From 1168616f80a594d65df2ebc69f9de46d1207f2e6 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 23 Jun 2022 11:43:57 +0200 Subject: [PATCH 10/19] fixed typo in german translation --- src/assets/i18n/de.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index 30c0a48bde..b67fdb604c 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -1755,7 +1755,7 @@ "dso-selector.create.community.sub-level": "Einen neuen Bereich anlegen in", // "dso-selector.create.community.top-level": "Create a new top-level community", - "dso-selector.create.community.top-level": "Einen neuen Bereich auf oberster Ebene anlgen", + "dso-selector.create.community.top-level": "Einen neuen Bereich auf oberster Ebene anlegen", // "dso-selector.create.item.head": "New item", "dso-selector.create.item.head": "Neues Item", From 2a20b0c989e2c09750a2457480bf202c2a05e36e Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 23 Jun 2022 12:04:33 +0200 Subject: [PATCH 11/19] [CST-6174] Add test to check maxStartDate and maxEndDate --- .../section-accesses.component.spec.ts | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/app/submission/sections/accesses/section-accesses.component.spec.ts b/src/app/submission/sections/accesses/section-accesses.component.spec.ts index 5509cab4bb..65123db533 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.spec.ts +++ b/src/app/submission/sections/accesses/section-accesses.component.spec.ts @@ -16,11 +16,15 @@ import { SectionAccessesService } from './section-accesses.service'; import { SectionFormOperationsService } from '../form/section-form-operations.service'; import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; -import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service'; +import { + SubmissionJsonPatchOperationsService +} from '../../../core/submission/submission-json-patch-operations.service'; import { getSectionAccessesService } from '../../../shared/mocks/section-accesses.service.mock'; import { getMockFormOperationsService } from '../../../shared/mocks/form-operations-service.mock'; import { getMockTranslateService } from '../../../shared/mocks/translate.service.mock'; -import { SubmissionJsonPatchOperationsServiceStub } from '../../../shared/testing/submission-json-patch-operations-service.stub'; +import { + SubmissionJsonPatchOperationsServiceStub +} from '../../../shared/testing/submission-json-patch-operations-service.stub'; import { BrowserModule } from '@angular/platform-browser'; import { of as observableOf } from 'rxjs'; @@ -42,8 +46,6 @@ describe('SubmissionSectionAccessesComponent', () => { let fixture: ComponentFixture; const sectionsServiceStub = new SectionsServiceStub(); - // const pathCombiner = new JsonPatchOperationPathCombiner('sections', sectionId, 'files', fileIndex); - const builderService: FormBuilderService = getMockFormBuilderService(); const submissionAccessesConfigService = getSubmissionAccessesConfigService(); const sectionAccessesService = getSectionAccessesService(); @@ -55,6 +57,7 @@ describe('SubmissionSectionAccessesComponent', () => { }); let formService: any; + let formbuilderService: any; const storeStub = jasmine.createSpyObj('store', ['dispatch']); @@ -86,7 +89,6 @@ describe('SubmissionSectionAccessesComponent', () => { declarations: [SubmissionSectionAccessesComponent, FormComponent], providers: [ { provide: SectionsService, useValue: sectionsServiceStub }, - { provide: FormBuilderService, useValue: builderService }, { provide: SubmissionAccessesConfigService, useValue: submissionAccessesConfigService }, { provide: SectionAccessesService, useValue: sectionAccessesService }, { provide: SectionFormOperationsService, useValue: sectionFormOperationsService }, @@ -97,6 +99,7 @@ describe('SubmissionSectionAccessesComponent', () => { { provide: SubmissionJsonPatchOperationsService, useValue: SubmissionJsonPatchOperationsServiceStub }, { provide: 'sectionDataProvider', useValue: sectionData }, { provide: 'submissionIdProvider', useValue: '1508' }, + FormBuilderService ] }) .compileComponents(); @@ -106,6 +109,7 @@ describe('SubmissionSectionAccessesComponent', () => { fixture = TestBed.createComponent(SubmissionSectionAccessesComponent); component = fixture.componentInstance; formService = TestBed.inject(FormService); + formbuilderService = TestBed.inject(FormBuilderService); formService.validateAllFormFields.and.callFake(() => null); formService.isValid.and.returnValue(observableOf(true)); formService.getFormData.and.returnValue(observableOf(mockAccessesFormData)); @@ -133,11 +137,22 @@ describe('SubmissionSectionAccessesComponent', () => { it('formModel type array should have formgroup with 1 input and 2 datepickers', () => { const formModel: any = component.formModel[1]; const formGroup = formModel.groupFactory()[0].group; + expect(formGroup[0] instanceof DynamicSelectModel).toBeTrue(); expect(formGroup[1] instanceof DynamicDatePickerModel).toBeTrue(); expect(formGroup[2] instanceof DynamicDatePickerModel).toBeTrue(); }); + it('should have set maxStartDate and maxEndDate properly', () => { + const maxStartDate = {year: 2024, month: 12, day: 20}; + const maxEndDate = {year: 2022, month: 6, day: 20}; + + const startDateModel = formbuilderService.findById('startDate', component.formModel); + expect(startDateModel.max).toEqual(maxStartDate); + const endDateModel = formbuilderService.findById('endDate', component.formModel); + expect(endDateModel.max).toEqual(maxEndDate); + }); + it('when checkbox changed it should call operationsBuilder replace function', () => { component.onChange(checkboxChangeEvent); fixture.detectChanges(); From 0aa14bcddd460478f212c7428086314aa39c35ef Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 23 Jun 2022 12:24:16 +0200 Subject: [PATCH 12/19] [CST-6174] Set maxStartDate/maxEndDate when building form models --- ...section-upload-file-edit.component.spec.ts | 38 +++++--- .../section-upload-file-edit.component.ts | 95 +++++++++---------- src/app/submission/submission.module.ts | 3 +- 3 files changed, 71 insertions(+), 65 deletions(-) diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts index aa03d37eb2..d008bf61f1 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts @@ -1,10 +1,9 @@ import { ChangeDetectorRef, Component, NO_ERRORS_SCHEMA } from '@angular/core'; -import { waitForAsync, ComponentFixture, inject, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from '@angular/core/testing'; import { BrowserModule } from '@angular/platform-browser'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { - DynamicFormArrayGroupModel, DynamicFormArrayModel, DynamicFormControlEvent, DynamicFormGroupModel, @@ -17,13 +16,13 @@ import { SubmissionService } from '../../../../submission.service'; import { SubmissionSectionUploadFileEditComponent } from './section-upload-file-edit.component'; import { POLICY_DEFAULT_WITH_LIST } from '../../section-upload.component'; import { + mockFileFormData, mockSubmissionCollectionId, mockSubmissionId, + mockSubmissionObject, mockUploadConfigResponse, mockUploadConfigResponseMetadata, mockUploadFiles, - mockFileFormData, - mockSubmissionObject, } from '../../../../../shared/mocks/submission.mock'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormComponent } from '../../../../../shared/form/form.component'; @@ -32,12 +31,20 @@ import { getMockFormService } from '../../../../../shared/mocks/form-service.moc import { createTestComponent } from '../../../../../shared/testing/utils.test'; import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { JsonPatchOperationsBuilder } from '../../../../../core/json-patch/builder/json-patch-operations-builder'; -import { SubmissionJsonPatchOperationsServiceStub } from '../../../../../shared/testing/submission-json-patch-operations-service.stub'; -import { SubmissionJsonPatchOperationsService } from '../../../../../core/submission/submission-json-patch-operations.service'; +import { + SubmissionJsonPatchOperationsServiceStub +} from '../../../../../shared/testing/submission-json-patch-operations-service.stub'; +import { + SubmissionJsonPatchOperationsService +} from '../../../../../core/submission/submission-json-patch-operations.service'; import { SectionUploadService } from '../../section-upload.service'; import { getMockSectionUploadService } from '../../../../../shared/mocks/section-upload.service.mock'; -import { FormFieldMetadataValueObject } from '../../../../../shared/form/builder/models/form-field-metadata-value.model'; -import { JsonPatchOperationPathCombiner } from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner'; +import { + FormFieldMetadataValueObject +} from '../../../../../shared/form/builder/models/form-field-metadata-value.model'; +import { + JsonPatchOperationPathCombiner +} from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { dateToISOFormat } from '../../../../../shared/date.util'; import { of } from 'rxjs'; @@ -171,6 +178,8 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { it('should init form model properly', () => { comp.fileData = fileData; comp.formId = 'testFileForm'; + const maxStartDate = {year: 2022, month: 1, day: 12}; + const maxEndDate = {year: 2019, month: 7, day: 12}; comp.ngOnInit(); @@ -179,6 +188,10 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { expect(comp.formModel[0] instanceof DynamicFormGroupModel).toBeTruthy(); expect(comp.formModel[1] instanceof DynamicFormArrayModel).toBeTruthy(); expect((comp.formModel[1] as DynamicFormArrayModel).groups.length).toBe(2); + const startDateModel = formbuilderService.findById('startDate', comp.formModel); + expect(startDateModel.max).toEqual(maxStartDate); + const endDateModel = formbuilderService.findById('endDate', comp.formModel); + expect(endDateModel.max).toEqual(maxEndDate); }); it('should call setOptions method onChange', () => { @@ -208,20 +221,19 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { const formGroup = formbuilderService.createFormGroup(comp.formModel); const control = formbuilderService.getFormControlById('name', formGroup, comp.formModel, 0); - spyOn(formbuilderService, 'findById').and.callThrough(); + spyOn(control.parent, 'markAsDirty').and.callThrough(); control.value = 'openaccess'; comp.setOptions(model, control); - expect(formbuilderService.findById).not.toHaveBeenCalledWith('endDate', (model.parent as DynamicFormArrayGroupModel).group); - expect(formbuilderService.findById).not.toHaveBeenCalledWith('startDate', (model.parent as DynamicFormArrayGroupModel).group); + expect(control.parent.markAsDirty).toHaveBeenCalled(); control.value = 'lease'; comp.setOptions(model, control); - expect(formbuilderService.findById).toHaveBeenCalledWith('endDate', (model.parent as DynamicFormArrayGroupModel).group); + expect(control.parent.markAsDirty).toHaveBeenCalled(); control.value = 'embargo'; comp.setOptions(model, control); - expect(formbuilderService.findById).toHaveBeenCalledWith('startDate', (model.parent as DynamicFormArrayGroupModel).group); + expect(control.parent.markAsDirty).toHaveBeenCalled(); }); it('should retrieve Value From Field properly', () => { diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts index 293835c848..195d291530 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts @@ -3,9 +3,7 @@ import { FormControl } from '@angular/forms'; import { DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER, - DynamicDateControlModel, DynamicDatePickerModel, - DynamicFormArrayGroupModel, DynamicFormArrayModel, DynamicFormControlEvent, DynamicFormControlModel, @@ -57,6 +55,8 @@ import { } from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { SectionUploadService } from '../../section-upload.service'; import { Subscription } from 'rxjs'; +import { DynamicFormControlCondition } from '@ng-dynamic-forms/core/lib/model/misc/dynamic-form-control-relation.model'; +import { DynamicDateControlValue } from '@ng-dynamic-forms/core/lib/model/dynamic-date-control.model'; /** * This component represents the edit form for bitstream @@ -245,8 +245,6 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { this.availableAccessConditionOptions.filter((element) => element.name === control.value) .forEach((element) => accessCondition = element ); if (isNotEmpty(accessCondition)) { - const showGroups: boolean = accessCondition.hasStartDate === true || accessCondition.hasEndDate === true; - const startDateControl: FormControl = control.parent.get('startDate') as FormControl; const endDateControl: FormControl = control.parent.get('endDate') as FormControl; @@ -257,34 +255,6 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { startDateControl?.setValue(null); control.parent.markAsDirty(); endDateControl?.setValue(null); - - if (showGroups) { - if (accessCondition.hasStartDate && accessCondition.maxStartDate) { - const startDateModel = this.formBuilderService.findById( - 'startDate', - (model.parent as DynamicFormArrayGroupModel).group) as DynamicDateControlModel; - - const min = new Date(accessCondition.maxStartDate); - startDateModel.max = { - year: min.getUTCFullYear(), - month: min.getUTCMonth() + 1, - day: min.getUTCDate() - }; - } - if (accessCondition.hasEndDate && accessCondition.maxEndDate) { - const endDateModel = this.formBuilderService.findById( - 'endDate', - (model.parent as DynamicFormArrayGroupModel).group) as DynamicDateControlModel; - - const max = new Date(accessCondition.maxEndDate); - endDateModel.max = { - year: max.getUTCFullYear(), - month: max.getUTCMonth() + 1, - day: max.getUTCDate() - }; - - } - } } } @@ -344,38 +314,63 @@ export class SubmissionSectionUploadFileEditComponent implements OnInit { } accessConditionTypeModelConfig.options = accessConditionTypeOptions; - // Dynamically assign of relation in config. For startdate, endDate, groups. - const hasStart = []; - const hasEnd = []; - const hasGroups = []; + // Dynamically assign of relation in config. For startDate and endDate. + const startDateCondition: DynamicFormControlCondition[] = []; + const endDateCondition: DynamicFormControlCondition[] = []; + let maxStartDate: DynamicDateControlValue; + let maxEndDate: DynamicDateControlValue; this.availableAccessConditionOptions.forEach((condition) => { - const showStart: boolean = condition.hasStartDate === true; - const showEnd: boolean = condition.hasEndDate === true; - const showGroups: boolean = showStart || showEnd; - if (showStart) { - hasStart.push({id: 'name', value: condition.name}); + + if (condition.hasStartDate) { + startDateCondition.push({ id: 'name', value: condition.name }); + if (condition.maxStartDate) { + const min = new Date(condition.maxStartDate); + maxStartDate = { + year: min.getUTCFullYear(), + month: min.getUTCMonth() + 1, + day: min.getUTCDate() + }; + } } - if (showEnd) { - hasEnd.push({id: 'name', value: condition.name}); - } - if (showGroups) { - hasGroups.push({id: 'name', value: condition.name}); + if (condition.hasEndDate) { + endDateCondition.push({ id: 'name', value: condition.name }); + if (condition.maxEndDate) { + const max = new Date(condition.maxEndDate); + maxEndDate = { + year: max.getUTCFullYear(), + month: max.getUTCMonth() + 1, + day: max.getUTCDate() + }; + } } }); - const confStart = {relations: [{match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasStart}]}; - const confEnd = {relations: [{match: MATCH_ENABLED, operator: OR_OPERATOR, when: hasEnd}]}; + const confStart = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: startDateCondition }] }; + const confEnd = { relations: [{ match: MATCH_ENABLED, operator: OR_OPERATOR, when: endDateCondition }] }; + const hasStartDate = startDateCondition.length > 0; + const hasEndDate = endDateCondition.length > 0; accessConditionsArrayConfig.groupFactory = () => { const type = new DynamicSelectModel(accessConditionTypeModelConfig, BITSTREAM_FORM_ACCESS_CONDITION_TYPE_LAYOUT); const startDateConfig = Object.assign({}, BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_CONFIG, confStart); + if (maxStartDate) { + startDateConfig.max = maxStartDate; + } + const endDateConfig = Object.assign({}, BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_CONFIG, confEnd); + if (maxEndDate) { + endDateConfig.max = maxEndDate; + } const startDate = new DynamicDatePickerModel(startDateConfig, BITSTREAM_FORM_ACCESS_CONDITION_START_DATE_LAYOUT); const endDate = new DynamicDatePickerModel(endDateConfig, BITSTREAM_FORM_ACCESS_CONDITION_END_DATE_LAYOUT); const accessConditionGroupConfig = Object.assign({}, BITSTREAM_ACCESS_CONDITION_GROUP_CONFIG); accessConditionGroupConfig.group = [type]; - if (hasStart.length > 0) { accessConditionGroupConfig.group.push(startDate); } - if (hasEnd.length > 0) { accessConditionGroupConfig.group.push(endDate); } + if (hasStartDate) { + accessConditionGroupConfig.group.push(startDate); + } + if (hasEndDate) { + accessConditionGroupConfig.group.push(endDate); + } return [new DynamicFormGroupModel(accessConditionGroupConfig, BITSTREAM_ACCESS_CONDITION_GROUP_LAYOUT)]; }; diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index eb6bde35ad..97ffb7ffcd 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -50,7 +50,7 @@ import { ThemedSubmissionEditComponent } from './edit/themed-submission-edit.com import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component'; import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component'; import { FormModule } from '../shared/form/form.module'; -import { NgbCollapseModule, NgbModalModule, NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgbAccordionModule, NgbCollapseModule, NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; import { SubmissionSectionAccessesComponent } from './sections/accesses/section-accesses.component'; import { SubmissionAccessesConfigService } from '../core/config/submission-accesses-config.service'; import { SectionAccessesService } from './sections/accesses/section-accesses.service'; @@ -71,7 +71,6 @@ const ENTRY_COMPONENTS = [ SubmissionSectionLicenseComponent, SubmissionSectionCcLicensesComponent, SubmissionSectionAccessesComponent, - SubmissionSectionUploadFileEditComponent, SubmissionSectionSherpaPoliciesComponent, ]; From f2b1d11fb22fa6f81df28be412abc2e5d79d2892 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 23 Jun 2022 14:15:41 +0200 Subject: [PATCH 13/19] added translation key communityList.breadcrumbs --- src/assets/i18n/de.json5 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index b67fdb604c..2c1c6c35b6 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -1356,9 +1356,12 @@ // "community.edit.logo.delete.title": "Delete logo", "community.edit.logo.delete.title": "Logo löschen", + + // "communityList.breadcrumbs": "Community List", + "communityList.breadcrumbs": "Bereichsliste", - // "communityList.tabTitle": "DSpace - Community List", - "communityList.tabTitle": "DSpace - Bereichsliste", + // "communityList.tabTitle": "Community List", + "communityList.tabTitle": "Bereichsliste", // "communityList.title": "List of Communities", "communityList.title": "Liste der Bereiche", From 78f4096ab4663c2642aeace7ee7cb3b781b99185 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 23 Jun 2022 14:25:50 +0200 Subject: [PATCH 14/19] removed prefix "DSpace Angular ::" in various translation keys; use repository.title.* instead --- src/assets/i18n/de.json5 | 76 +++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index 2c1c6c35b6..ec2e1d39bd 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -193,8 +193,8 @@ // "admin.registries.bitstream-formats.table.supportLevel.head": "Support Level", "admin.registries.bitstream-formats.table.supportLevel.head": "Unterstützungsgrad", - // "admin.registries.bitstream-formats.title": "DSpace Angular :: Bitstream Format Registry", - "admin.registries.bitstream-formats.title": "DSpace Angular :: Referenzliste der Dateiformate", + // "admin.registries.bitstream-formats.title": "Bitstream Format Registry", + "admin.registries.bitstream-formats.title": "Referenzliste der Dateiformate", @@ -234,8 +234,8 @@ // "admin.registries.metadata.schemas.table.namespace": "Namespace", "admin.registries.metadata.schemas.table.namespace": "Namensraum", - // "admin.registries.metadata.title": "DSpace Angular :: Metadata Registry", - "admin.registries.metadata.title": "DSpace Angular :: Metadatenreferenzliste", + // "admin.registries.metadata.title": "Metadata Registry", + "admin.registries.metadata.title": "Metadatenreferenzliste", @@ -311,8 +311,8 @@ // "admin.registries.schema.return": "Return", "admin.registries.schema.return": "Zurück", - // "admin.registries.schema.title": "DSpace Angular :: Metadata Schema Registry", - "admin.registries.schema.title": "DSpace Angular :: Referenzliste der Metadatenschemata", + // "admin.registries.schema.title": "Metadata Schema Registry", + "admin.registries.schema.title": "Referenzliste der Metadatenschemata", @@ -328,8 +328,8 @@ // "admin.access-control.epeople.actions.stop-impersonating": "Stop impersonating EPerson", "admin.access-control.epeople.actions.stop-impersonating": "Von Person ausloggen", - // "admin.access-control.epeople.title": "DSpace Angular :: EPeople", - "admin.access-control.epeople.title": "DSpace Angular :: Personen", + // "admin.access-control.epeople.title": "EPeople", + "admin.access-control.epeople.title": "Personen", // "admin.access-control.epeople.head": "EPeople", "admin.access-control.epeople.head": "Personen", @@ -444,14 +444,14 @@ - // "admin.access-control.groups.title": "DSpace Angular :: Groups", - "admin.access-control.groups.title": "DSpace Angular :: Gruppen", + // "admin.access-control.groups.title": "Groups", + "admin.access-control.groups.title": "Gruppen", - // "admin.access-control.groups.title.singleGroup": "DSpace Angular :: Edit Group", - "admin.access-control.groups.title.singleGroup": "DSpace Angular :: Gruppe bearbeiten", + // "admin.access-control.groups.title.singleGroup": "Edit Group", + "admin.access-control.groups.title.singleGroup": "Gruppe bearbeiten", - // "admin.access-control.groups.title.addGroup": "DSpace Angular :: New Group", - "admin.access-control.groups.title.addGroup": "DSpace Angular :: Neue Gruppe", + // "admin.access-control.groups.title.addGroup": "New Group", + "admin.access-control.groups.title.addGroup": "Neue Gruppe", // "admin.access-control.groups.head": "Groups", "admin.access-control.groups.head": "Gruppen", @@ -2144,8 +2144,8 @@ // "home.search-form.placeholder": "Search the repository ...", "home.search-form.placeholder": "Durchsuche Repositorium", - // "home.title": "DSpace Angular :: Home", - "home.title": "DSpace Angular :: Startseite", + // "home.title": "Home", + "home.title": "Startseite", // "home.top-level-communities.head": "Communities in DSpace", "home.top-level-communities.head": "Hauptbereiche in DSpace", @@ -2806,8 +2806,8 @@ // "item.search.results.head": "Item Search Results", "item.search.results.head": "Item-Suchergebnisse", - // "item.search.title": "DSpace Angular :: Item Search", - "item.search.title": "DSpace Angular :: Item-Suche", + // "item.search.title": "Item Search", + "item.search.title": "Item-Suche", @@ -3003,8 +3003,8 @@ // "journal.search.results.head": "Journal Search Results", "journal.search.results.head": "Suchergebnisse für Zeitschriften", - // "journal.search.title": "DSpace Angular :: Journal Search", - "journal.search.title": "DSpace Angular :: Zeitschriftensuche", + // "journal.search.title": "Journal Search", + "journal.search.title": "Zeitschriftensuche", @@ -3632,8 +3632,8 @@ // "person.search.results.head": "Person Search Results", "person.search.results.head": "Ergebnisse der Personensuche", - // "person.search.title": "DSpace Angular :: Person Search", - "person.search.title": "DSpace Angular :: Personensuche", + // "person.search.title": "Person Search", + "person.search.title": "Personensuche", @@ -3925,8 +3925,8 @@ // "publication.search.results.head": "Publication Search Results", "publication.search.results.head": "Suchergebnisse Publikationen", - // "publication.search.title": "DSpace Angular :: Publication Search", - "publication.search.title": "DSpace Angular :: Publikationssuche", + // "publication.search.title": "Publication Search", + "publication.search.title": "Publikationssuche", // "register-email.title": "New user registration", @@ -4084,9 +4084,27 @@ // "relationships.isContributorOf": "Contributors", "relationships.isContributorOf": "Beteiligte", - - - + + // "relationships.isContributorOf.OrgUnit": "Contributor (Organizational Unit)", + "relationships.isContributorOf.OrgUnit": "Beteiligte (Organisationseinheit)", + + // "relationships.isContributorOf.Person": "Contributor", + "relationships.isContributorOf.Person": "Beteiligte (Person)", + + // "relationships.isFundingAgencyOf.OrgUnit": "Funder", + "relationships.isFundingAgencyOf.OrgUnit": "Förderer", + + + // "repository.image.logo": "Repository logo", + "repository.image.logo": "Repository Logo", + + // "repository.title.prefix": "DSpace Angular :: ", + "repository.title.prefix": "DSpace Angular :: ", + + // "repository.title.prefixDSpace": "DSpace Angular ::", + "repository.title.prefixDSpace": "DSpace Angular ::", + + // "resource-policies.add.button": "Add", "resource-policies.add.button": "Hinzufügen", @@ -4248,8 +4266,8 @@ // "search.switch-configuration.title": "Show", "search.switch-configuration.title": "Zeige", - // "search.title": "DSpace Angular :: Search", - "search.title": "DSpace Angular :: Suche", + // "search.title": "Search", + "search.title": "Suche", // "search.breadcrumbs": "Search", "search.breadcrumbs": "Suche", From 5a7ba93e50834f73f18fb88605f6a9cac2904af6 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 23 Jun 2022 14:46:40 +0200 Subject: [PATCH 15/19] added browse.title.page --- src/assets/i18n/de.json5 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index ec2e1d39bd..88c99468ea 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -1008,9 +1008,12 @@ // "browse.startsWith.type_text": "Or enter first few letters:", "browse.startsWith.type_text": "Oder geben Sie die ersten Buchstaben ein:", - - // "browse.title": "Browsing {{ collection }} by {{ field }} {{ value }}", - "browse.title": "Auflistung von {{ collection }} nach {{ field }} {{ value }}", + + // "browse.title": "Browsing {{ collection }} by {{ field }}{{ startsWith }} {{ value }}", + "browse.title": "Auflistung {{ collection }} nach {{ field }}{{ startsWith }} {{ value }}", + + // "browse.title.page": "Browsing {{ collection }} by {{ field }} {{ value }}", + "browse.title.page": "Auflistung {{ collection }} nach {{ field }} {{ value }}", // "chips.remove": "Remove chip", From cfca5c8ca1bb18ecb4f6f1c8b1bd2d5b96ac90a8 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 23 Jun 2022 16:56:43 +0200 Subject: [PATCH 16/19] fixed admin.registries.schema.fields.table.delete --- src/assets/i18n/de.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index 88c99468ea..f117475fe4 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -252,7 +252,7 @@ "admin.registries.schema.fields.no-items": "Es gibt keine Metadatenfelder in diesem Schema.", // "admin.registries.schema.fields.table.delete": "Delete selected", - "admin.registries.schema.fields.table.delete": "Ausgewähltes löschen", + "admin.registries.schema.fields.table.delete": "Auswahl löschen", // "admin.registries.schema.fields.table.field": "Field", "admin.registries.schema.fields.table.field": "Feld", From 79c97c94a23eb3c7d06d958dc4ebb2d43a1cf81d Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 23 Jun 2022 17:05:32 +0200 Subject: [PATCH 17/19] fixed submission.workflow.generic.delete-help --- src/assets/i18n/de.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index f117475fe4..c3a874535c 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -5231,7 +5231,7 @@ "submission.workflow.generic.delete": "Löschen", // "submission.workflow.generic.delete-help": "If you would to discard this item, select \"Delete\". You will then be asked to confirm it.", - "submission.workflow.generic.delete-help": "Wenn Sie dieses Item möchten, wählen Sie bitte \"Löschen\". Sie werden danach um Bestätigung gebeten.", + "submission.workflow.generic.delete-help": "Wenn Sie dieses Item verwerfen möchten, wählen Sie bitte \"Löschen\". Sie werden danach um Bestätigung gebeten.", // "submission.workflow.generic.edit": "Edit", "submission.workflow.generic.edit": "Bearbeiten", From 377ac3ab849bef612b6a60f3830d88a1dfec1a02 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 23 Jun 2022 14:54:12 -0500 Subject: [PATCH 18/19] Add spacing before handle on comcol pages --- .../comcol/comcol-page-handle/comcol-page-handle.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.html b/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.html index b3ca75bf94..552854a0c0 100644 --- a/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.html +++ b/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.html @@ -1,4 +1,4 @@

{{ title | translate }}

- +
From b55e4327a88c993535219f6c6793a6888a015c8f Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Fri, 24 Jun 2022 16:03:54 +0200 Subject: [PATCH 19/19] fix issue where curation tasks wouldn't work for collections and communities because the handle was in the wrong format --- .../curation-form.component.spec.ts | 26 ++++++++-- .../curation-form/curation-form.component.ts | 15 ++++-- src/app/shared/handle.service.spec.ts | 47 +++++++++++++++++++ src/app/shared/handle.service.ts | 41 ++++++++++++++++ src/assets/i18n/en.json5 | 2 + 5 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 src/app/shared/handle.service.spec.ts create mode 100644 src/app/shared/handle.service.ts diff --git a/src/app/curation-form/curation-form.component.spec.ts b/src/app/curation-form/curation-form.component.spec.ts index 4ff013f77c..dc70b925e8 100644 --- a/src/app/curation-form/curation-form.component.spec.ts +++ b/src/app/curation-form/curation-form.component.spec.ts @@ -15,6 +15,7 @@ import { By } from '@angular/platform-browser'; import { ConfigurationDataService } from '../core/data/configuration-data.service'; import { ConfigurationProperty } from '../core/shared/configuration-property.model'; import { getProcessDetailRoute } from '../process-page/process-page-routing.paths'; +import { HandleService } from '../shared/handle.service'; describe('CurationFormComponent', () => { let comp: CurationFormComponent; @@ -23,6 +24,7 @@ describe('CurationFormComponent', () => { let scriptDataService: ScriptDataService; let processDataService: ProcessDataService; let configurationDataService: ConfigurationDataService; + let handleService: HandleService; let notificationsService; let router; @@ -51,6 +53,10 @@ describe('CurationFormComponent', () => { })) }); + handleService = { + normalizeHandle: (a) => a + } as any; + notificationsService = new NotificationsServiceStub(); router = new RouterStub(); @@ -58,11 +64,12 @@ describe('CurationFormComponent', () => { imports: [TranslateModule.forRoot(), FormsModule, ReactiveFormsModule], declarations: [CurationFormComponent], providers: [ - {provide: ScriptDataService, useValue: scriptDataService}, - {provide: ProcessDataService, useValue: processDataService}, - {provide: NotificationsService, useValue: notificationsService}, - {provide: Router, useValue: router}, - {provide: ConfigurationDataService, useValue: configurationDataService}, + { provide: ScriptDataService, useValue: scriptDataService }, + { provide: ProcessDataService, useValue: processDataService }, + { provide: NotificationsService, useValue: notificationsService }, + { provide: HandleService, useValue: handleService }, + { provide: Router, useValue: router}, + { provide: ConfigurationDataService, useValue: configurationDataService }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }).compileComponents(); @@ -143,4 +150,13 @@ describe('CurationFormComponent', () => { {name: '-i', value: 'all'}, ], []); }); + + it(`should show an error notification and return when an invalid dsoHandle is provided`, () => { + comp.dsoHandle = 'test-handle'; + spyOn(handleService, 'normalizeHandle').and.returnValue(null); + comp.submit(); + + expect(notificationsService.error).toHaveBeenCalled(); + expect(scriptDataService.invoke).not.toHaveBeenCalled(); + }); }); diff --git a/src/app/curation-form/curation-form.component.ts b/src/app/curation-form/curation-form.component.ts index 31501e70d7..422c955037 100644 --- a/src/app/curation-form/curation-form.component.ts +++ b/src/app/curation-form/curation-form.component.ts @@ -5,7 +5,7 @@ import { getFirstCompletedRemoteData } from '../core/shared/operators'; import { find, map } from 'rxjs/operators'; import { NotificationsService } from '../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; -import { hasValue, isEmpty, isNotEmpty } from '../shared/empty.util'; +import { hasValue, isEmpty, isNotEmpty, hasNoValue } from '../shared/empty.util'; import { RemoteData } from '../core/data/remote-data'; import { Router } from '@angular/router'; import { ProcessDataService } from '../core/data/processes/process-data.service'; @@ -14,9 +14,9 @@ import { ConfigurationDataService } from '../core/data/configuration-data.servic import { ConfigurationProperty } from '../core/shared/configuration-property.model'; import { Observable } from 'rxjs'; import { getProcessDetailRoute } from '../process-page/process-page-routing.paths'; +import { HandleService } from '../shared/handle.service'; export const CURATION_CFG = 'plugin.named.org.dspace.curate.CurationTask'; - /** * Component responsible for rendering the Curation Task form */ @@ -39,6 +39,7 @@ export class CurationFormComponent implements OnInit { private processDataService: ProcessDataService, private notificationsService: NotificationsService, private translateService: TranslateService, + private handleService: HandleService, private router: Router ) { } @@ -76,13 +77,19 @@ export class CurationFormComponent implements OnInit { const taskName = this.form.get('task').value; let handle; if (this.hasHandleValue()) { - handle = this.dsoHandle; + handle = this.handleService.normalizeHandle(this.dsoHandle); + if (isEmpty(handle)) { + this.notificationsService.error(this.translateService.get('curation.form.submit.error.head'), + this.translateService.get('curation.form.submit.error.invalid-handle')); + return; + } } else { - handle = this.form.get('handle').value; + handle = this.handleService.normalizeHandle(this.form.get('handle').value); if (isEmpty(handle)) { handle = 'all'; } } + this.scriptDataService.invoke('curate', [ { name: '-t', value: taskName }, { name: '-i', value: handle }, diff --git a/src/app/shared/handle.service.spec.ts b/src/app/shared/handle.service.spec.ts new file mode 100644 index 0000000000..b326eb0416 --- /dev/null +++ b/src/app/shared/handle.service.spec.ts @@ -0,0 +1,47 @@ +import { HandleService } from './handle.service'; + +describe('HandleService', () => { + let service: HandleService; + + beforeEach(() => { + service = new HandleService(); + }); + + describe(`normalizeHandle`, () => { + it(`should simply return an already normalized handle`, () => { + let input, output; + + input = '123456789/123456'; + output = service.normalizeHandle(input); + expect(output).toEqual(input); + + input = '12.3456.789/123456'; + output = service.normalizeHandle(input); + expect(output).toEqual(input); + }); + + it(`should normalize a handle url`, () => { + let input, output; + + input = 'https://hdl.handle.net/handle/123456789/123456'; + output = service.normalizeHandle(input); + expect(output).toEqual('123456789/123456'); + + input = 'https://rest.api/server/handle/123456789/123456'; + output = service.normalizeHandle(input); + expect(output).toEqual('123456789/123456'); + }); + + it(`should return null if the input doesn't contain a handle`, () => { + let input, output; + + input = 'https://hdl.handle.net/handle/123456789'; + output = service.normalizeHandle(input); + expect(output).toBeNull(); + + input = 'something completely different'; + output = service.normalizeHandle(input); + expect(output).toBeNull(); + }); + }); +}); diff --git a/src/app/shared/handle.service.ts b/src/app/shared/handle.service.ts new file mode 100644 index 0000000000..da0f17f7de --- /dev/null +++ b/src/app/shared/handle.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@angular/core'; +import { isNotEmpty, isEmpty } from './empty.util'; + +const PREFIX_REGEX = /handle\/([^\/]+\/[^\/]+)$/; +const NO_PREFIX_REGEX = /^([^\/]+\/[^\/]+)$/; + +@Injectable({ + providedIn: 'root' +}) +export class HandleService { + + + /** + * Turns a handle string into the default 123456789/12345 format + * + * @param handle the input handle + * + * normalizeHandle('123456789/123456') // '123456789/123456' + * normalizeHandle('12.3456.789/123456') // '12.3456.789/123456' + * normalizeHandle('https://hdl.handle.net/handle/123456789/123456') // '123456789/123456' + * normalizeHandle('https://rest.api/server/handle/123456789/123456') // '123456789/123456' + * normalizeHandle('https://rest.api/server/handle/123456789') // null + */ + normalizeHandle(handle: string): string { + let matches: string[]; + if (isNotEmpty(handle)) { + matches = handle.match(PREFIX_REGEX); + } + + if (isEmpty(matches) || matches.length < 2) { + matches = handle.match(NO_PREFIX_REGEX); + } + + if (isEmpty(matches) || matches.length < 2) { + return null; + } else { + return matches[1]; + } + } + +} diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 3d5f15b4f2..5d7be2a681 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1291,6 +1291,8 @@ "curation.form.submit.error.content": "An error occured when trying to start the curation task.", + "curation.form.submit.error.invalid-handle": "Couldn't determine the handle for this object", + "curation.form.handle.label": "Handle:", "curation.form.handle.hint": "Hint: Enter [your-handle-prefix]/0 to run a task across entire site (not all tasks may support this capability)",