diff --git a/dmp-frontend/src/app/core/core-service.module.ts b/dmp-frontend/src/app/core/core-service.module.ts index 9e8d69031..7de195b04 100644 --- a/dmp-frontend/src/app/core/core-service.module.ts +++ b/dmp-frontend/src/app/core/core-service.module.ts @@ -55,6 +55,7 @@ import { SupportiveMaterialService } from './services/supportive-material/suppor import { UserSettingsHttpService } from './services/user-settings/user-settings-http.service'; import { UserSettingsService } from './services/user-settings/user-settings.service'; import { QueryParamsService } from './services/utilities/query-params.service'; +import { FileUtils } from './services/utilities/file-utils.service'; // // // This is shared module that provides all the services. Its imported only once on the AppModule. @@ -128,7 +129,8 @@ export class CoreServiceModule { QueryParamsService, UserSettingsService, UserSettingsHttpService, - FilterService + FilterService, + FileUtils ], }; } diff --git a/dmp-frontend/src/app/core/services/dmp/dmp-blueprint.service.ts b/dmp-frontend/src/app/core/services/dmp/dmp-blueprint.service.ts index 9e2b0474d..39a0017db 100644 --- a/dmp-frontend/src/app/core/services/dmp/dmp-blueprint.service.ts +++ b/dmp-frontend/src/app/core/services/dmp/dmp-blueprint.service.ts @@ -25,11 +25,9 @@ import { Observable, throwError } from 'rxjs'; @Injectable() export class DmpBlueprintService { - private actionUrl: string; private headers = new HttpHeaders(); constructor(private http: BaseHttpV2Service, private httpClient: HttpClient, private configurationService: ConfigurationService, private filterService: FilterService) { - this.actionUrl = configurationService.server + 'dmpprofile/'; } private get apiBase(): string { return `${this.configurationService.server}dmp-blueprint`; } diff --git a/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts b/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts index 879ef1d9c..a0436f024 100644 --- a/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts +++ b/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts @@ -14,6 +14,7 @@ import { Role } from '@app/core/common/enum/role'; import { RoleOrganizationType } from '@app/core/common/enum/role-organization-type'; import { ViewStyleType } from '@app/ui/admin/dataset-profile/editor/components/field/view-style-enum'; import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status'; +import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; @Injectable() export class EnumUtils { @@ -193,4 +194,29 @@ export class EnumUtils { case DescriptionTemplateTypeStatus.Finalized: return this.language.instant('TYPES.DESCRIPTION-TEMPLATE-TYPE-STATUS.FINALIZED'); } } + + toDmpBlueprintSystemFieldTypeString(status: DmpBlueprintSystemFieldType): string { + switch (status) { + case DmpBlueprintSystemFieldType.TEXT: return this.language.instant('TYPES.DMP-BLUEPRINT-SYSTEM-FIELD-TYPE.TEXT'); + case DmpBlueprintSystemFieldType.HTML_TEXT: return this.language.instant('TYPES.DMP-BLUEPRINT-SYSTEM-FIELD-TYPE.HTML_TEXT'); + case DmpBlueprintSystemFieldType.RESEARCHERS: return this.language.instant('TYPES.DMP-BLUEPRINT-SYSTEM-FIELD-TYPE.RESEARCHERS'); + case DmpBlueprintSystemFieldType.ORGANIZATIONS: return this.language.instant('TYPES.DMP-BLUEPRINT-SYSTEM-FIELD-TYPE.ORGANIZATIONS'); + case DmpBlueprintSystemFieldType.LANGUAGE: return this.language.instant('TYPES.DMP-BLUEPRINT-SYSTEM-FIELD-TYPE.LANGUAGE'); + case DmpBlueprintSystemFieldType.CONTACT: return this.language.instant('TYPES.DMP-BLUEPRINT-SYSTEM-FIELD-TYPE.CONTACT'); + case DmpBlueprintSystemFieldType.FUNDER: return this.language.instant('TYPES.DMP-BLUEPRINT-SYSTEM-FIELD-TYPE.FUNDER'); + case DmpBlueprintSystemFieldType.GRANT: return this.language.instant('TYPES.DMP-BLUEPRINT-SYSTEM-FIELD-TYPE.GRANT'); + case DmpBlueprintSystemFieldType.PROJECT: return this.language.instant('TYPES.DMP-BLUEPRINT-SYSTEM-FIELD-TYPE.PROJECT'); + case DmpBlueprintSystemFieldType.LICENSE: return this.language.instant('TYPES.DMP-BLUEPRINT-SYSTEM-FIELD-TYPE.LICENSE'); + case DmpBlueprintSystemFieldType.ACCESS_RIGHTS: return this.language.instant('TYPES.DMP-BLUEPRINT-SYSTEM-FIELD-TYPE.ACCESS_RIGHTS'); + } + } + + toDmpBlueprintExtraFieldDataTypeString(status: DmpBlueprintExtraFieldDataType): string { + switch (status) { + case DmpBlueprintExtraFieldDataType.Date: return this.language.instant('TYPES.DMP-BLUEPRINT-EXTRA-FIELD-DATA-TYPE.DATE'); + case DmpBlueprintExtraFieldDataType.Number: return this.language.instant('TYPES.DMP-BLUEPRINT-EXTRA-FIELD-DATA-TYPE.NUMBER'); + case DmpBlueprintExtraFieldDataType.Text: return this.language.instant('TYPES.DMP-BLUEPRINT-EXTRA-FIELD-DATA-TYPE.TEXT'); + case DmpBlueprintExtraFieldDataType.ExternalAutocomplete: return this.language.instant('TYPES.DMP-BLUEPRINT-EXTRA-FIELD-DATA-TYPE.EXTERNAL-AUTOCOMPLETE'); + } + } } diff --git a/dmp-frontend/src/app/core/services/utilities/file-utils.service.ts b/dmp-frontend/src/app/core/services/utilities/file-utils.service.ts new file mode 100644 index 000000000..2fd956963 --- /dev/null +++ b/dmp-frontend/src/app/core/services/utilities/file-utils.service.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class FileUtils { + constructor() { } + + getFilenameFromContentDispositionHeader(header: string): string { + const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); + + const matches = header.match(regex); + let filename: string; + for (let i = 0; i < matches.length; i++) { + const match = matches[i]; + if (match.includes('filename="')) { + filename = match.substring(10, match.length - 1); + break; + } else if (match.includes('filename=')) { + filename = match.substring(9); + break; + } + } + return filename; + } +} diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts index e46b8d802..95ca5fdc4 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts @@ -50,6 +50,7 @@ import { DescriptionTemplateType } from '@app/core/model/description-template-ty import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status'; import { DescriptionTemplateTypeLookup } from '@app/core/query/description-template-type.lookup'; import { nameof } from 'ts-simple-nameof'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; const skipDisable: any[] = require('../../../../../assets/resources/skipDisable.json'); @@ -119,7 +120,8 @@ export class DatasetProfileEditorComponent extends CheckDeactivateBaseComponent private fb: UntypedFormBuilder, private sidenavService: SideNavService, private userService: UserService, - private descriptionTemplateTypeService: DescriptionTemplateTypeService + private descriptionTemplateTypeService: DescriptionTemplateTypeService, + private fileUtils: FileUtils ) { super(); // this.profileID = route.snapshot.params['id']; @@ -584,30 +586,12 @@ export class DatasetProfileEditorComponent extends CheckDeactivateBaseComponent .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); }); } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - getDescriptionTemplateTypes(): DescriptionTemplateType[] { let lookup: DescriptionTemplateTypeLookup = new DescriptionTemplateTypeLookup(); lookup.project = { diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.ts index 70857dadb..f9181daab 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.ts @@ -16,11 +16,12 @@ import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; import { DatasetProfileCriteriaComponent } from '@app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component'; // import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; import { BaseComponent } from '@common/base/base.component'; import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; import { TranslateService } from '@ngx-translate/core'; import * as FileSaver from 'file-saver'; -import { Observable, merge as observableMerge, of as observableOf } from 'rxjs'; +import { Observable, merge as observableMerge } from 'rxjs'; import { map, startWith, switchMap, takeUntil } from 'rxjs/operators'; import { DialogConfirmationUploadDatasetProfiles } from './criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component'; @@ -55,6 +56,7 @@ export class DatasetProfileListingComponent extends BaseComponent implements OnI private matomoService: MatomoService, private dialog: MatDialog, private datasetProfileService: DatasetProfileService, + private fileUtils: FileUtils ) { super(); } @@ -160,28 +162,11 @@ export class DatasetProfileListingComponent extends BaseComponent implements OnI .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); }); } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } deleteTemplate(id: string) { if (id) { diff --git a/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.html b/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.html index 90843cf9f..5527b1612 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.html @@ -23,7 +23,7 @@ -
+
- - Name - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - -

Sections

+
+ + {{'DMP-BLUEPRINT-EDITOR.FIELDS.NAME' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+

{{'DMP-BLUEPRINT-EDITOR.FIELDS.SECTIONS' | translate}}

-
+
- Section {{sectionIndex + 1}} - drag_indicator +
+
+ {{'DMP-BLUEPRINT-EDITOR.FIELDS.SECTION-PREFIX' | translate}} {{sectionIndex + 1}} +
+
drag_indicator
+
+ delete +
+
-
+ +
+
+ + {{'DMP-BLUEPRINT-EDITOR.FIELDS.SECTION-NAME' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'DMP-BLUEPRINT-EDITOR.FIELDS.SECTION-DESCRIPTION' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'DMP-BLUEPRINT-EDITOR.FIELDS.SYSTEM-FIELDS' | translate}} + + {{enumUtils.toDmpBlueprintSystemFieldTypeString(systemFieldType)}} + + + +
+
+ +
- - Section name - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - - - Section description - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - -
-
+
+
-
-
-
- - System fields - - {{f.label}} - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - -
-
- -
+
+ {{fieldIndex + 1}}
-
- -
- -
-
-
-
-
-
-
- {{fieldIndex + 1}} -
-
- drag_indicator -
-
-
- - - -
- - System Field - - -
-
- - Label - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - -
-
- - Placeholder - - -
-
- - Description - - -
-
- Required -
-
- delete - {{'DMP-BLUEPRINT-EDITOR.STEPS.TOOLKIT.DELETE' | translate}} -
-
- - -
- - Type - - - {{getExtraFieldTypeValue(extraFieldType)}} - - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - -
-
- - Label - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - -
-
- - Placeholder - - -
-
- - Description - - -
-
- - Required - -
-
- delete - {{'DMP-BLUEPRINT-EDITOR.STEPS.TOOLKIT.DELETE' | translate}} -
-
- -
-
-
+
+ drag_indicator +
+
+ + {{'DMP-BLUEPRINT-EDITOR.FIELDS.SYSTEM-FIELD' | translate}} + + +
+
+ + {{'DMP-BLUEPRINT-EDITOR.FIELDS.FIELD-DATA-TYPE' | translate}} + + + {{enumUtils.toDmpBlueprintExtraFieldDataTypeString(extraFieldDataType)}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'DMP-BLUEPRINT-EDITOR.FIELDS.FIELD-LABEL' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'DMP-BLUEPRINT-EDITOR.FIELDS.FIELD-PLACEHOLDER' | translate}} + + +
+
+ + {{'DMP-BLUEPRINT-EDITOR.FIELDS.FIELD-DESCRIPTION' | translate}} + + +
+
+ {{'DMP-BLUEPRINT-EDITOR.FIELDS.FIELD-REQUIRED' | translate}} +
+
+ delete + {{'DMP-BLUEPRINT-EDITOR.ACTIONS.REMOVE-SYSTEM-FIELD' | translate}} +
+
+ delete + {{'DMP-BLUEPRINT-EDITOR.ACTIONS.REMOVE-EXTRA-FIELD' | translate}}
-
-
+
-
+
- Description Templates + {{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATES' | translate}}
- -
+
+
+
+ + {{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATES' | translate}} + + + +
+
+
+
- - Label + + {{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATE-LABEL' | translate}}
- - Min Multiplicity + + {{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATE-MIN-MULTIPLICITY' | translate}}
- - Max Multiplicity + + {{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATE-MAX-MULTIPLICITY' | translate}}
@@ -242,17 +203,7 @@
-
-
- -
- delete - {{'DMP-BLUEPRINT-EDITOR.STEPS.TOOLKIT.DELETE' | translate}} -
-
-
+
@@ -262,7 +213,7 @@
- +
@@ -281,5 +232,4 @@ -
- \ No newline at end of file +
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.scss b/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.scss index fa59f8200..c2e33c533 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.scss +++ b/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.scss @@ -89,10 +89,10 @@ .action-list-icon{ font-size: 1.2em; // padding-right: 1em; - width: 14px; - margin-right: 0.5em; - margin-left: -.09em; - height: auto; + // width: 14px; + // margin-right: 0.5em; + // margin-left: -.09em; + // height: auto; color: var(--primary-color); } diff --git a/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.ts b/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.ts index 75f199026..b58df63a5 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.component.ts @@ -1,31 +1,45 @@ import { Component, OnInit } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; +import { FormArray, UntypedFormGroup } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Router } from '@angular/router'; import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; // import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; +import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { DatePipe } from '@angular/common'; +import { DmpBlueprintExtraFieldDataType } from '@app/core/common/enum/dmp-blueprint-field-type'; +import { DmpBlueprintSectionFieldCategory } from '@app/core/common/enum/dmp-blueprint-section-field-category'; +import { DmpBlueprintStatus } from '@app/core/common/enum/dmp-blueprint-status'; +import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; import { AppPermission } from '@app/core/common/enum/permission.enum'; +import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; +import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; +import { DmpBlueprint, DmpBlueprintPersist } from '@app/core/model/dmp-blueprint/dmp-blueprint'; +import { DatasetProfileCriteria } from '@app/core/query/dataset-profile/dataset-profile-criteria'; import { AuthService } from '@app/core/services/auth/auth.service'; +import { DmpService } from '@app/core/services/dmp/dmp.service'; import { LoggingService } from '@app/core/services/logging/logging-service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; import { QueryParamsService } from '@app/core/services/utilities/query-params.service'; +import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; import { BaseEditor } from '@common/base/base-editor'; import { FormService } from '@common/forms/form-service'; +import { FormValidationErrorsDialogComponent } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component'; import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service'; import { FilterService } from '@common/modules/text-filter/filter-service'; import { Guid } from '@common/types/guid'; import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { Observable } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; -import { DmpBlueprintEditorModel } from './dmp-blueprint-editor.model'; +import { DescriptionTemplatesInSectionEditorModel, DmpBlueprintDefinitionSectionEditorModel, DmpBlueprintEditorModel, FieldInSectionEditorModel } from './dmp-blueprint-editor.model'; import { DmpBlueprintEditorResolver } from './dmp-blueprint-editor.resolver'; import { DmpBlueprintEditorService } from './dmp-blueprint-editor.service'; -import { DmpBlueprint, DmpBlueprintPersist } from '@app/core/model/dmp-blueprint/dmp-blueprint'; -import { IsActive } from '@app/core/common/enum/is-active.enum'; -import { DmpBlueprintSectionFieldCategory } from '@app/core/common/enum/dmp-blueprint-section-field-category'; @Component({ @@ -40,8 +54,21 @@ export class DmpBlueprintEditorComponent extends BaseEditor = []; dmpBlueprintSectionFieldCategory = DmpBlueprintSectionFieldCategory; + dmpBlueprintSystemFieldType = DmpBlueprintSystemFieldType; + public dmpBlueprintSystemFieldTypeEnum = this.enumUtils.getEnumValues(DmpBlueprintSystemFieldType); + dmpBlueprintExtraFieldDataType = DmpBlueprintExtraFieldDataType; + public dmpBlueprintExtraFieldDataTypeEnum = this.enumUtils.getEnumValues(DmpBlueprintExtraFieldDataType); + blueprintsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = { + filterFn: this.filterDescriptionTempaltes.bind(this), + initialItems: (excludedItems: any[]) => this.filterDescriptionTempaltes('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), + displayFn: (item: DatasetProfileModel) => item.label, + titleFn: (item: DatasetProfileModel) => item.label, + subtitleFn: (item: DatasetProfileModel) => item.description, + popupItemActionIcon: 'visibility' + }; protected get canDelete(): boolean { return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteDmpBlueprint); @@ -77,12 +104,16 @@ export class DmpBlueprintEditorComponent extends BaseEditor { + section.get('ordinal').setValue(index + 1); + }); + } + + dropSections(event: CdkDragDrop) { + const sectionsFormArray = (this.formGroup.get('definition').get('sections') as FormArray); + + moveItemInArray(sectionsFormArray.controls, event.previousIndex, event.currentIndex); + sectionsFormArray.updateValueAndValidity(); + sectionsFormArray.controls.forEach((section, index) => { + section.get('ordinal').setValue(index + 1); + }); + } + + + // + // + // Fields + // + // + systemFieldDisabled(systemField: DmpBlueprintSystemFieldType) { + return (this.formGroup.get('definition').get('sections') as FormArray)?.controls.some(x => (x.get('fields') as FormArray).controls.some(y => (y as UntypedFormGroup).get('systemFieldType')?.value === systemField)); + // for (let section in (this.formGroup.get('definition').get('sections')as FormArray)?.controls) { + // if (i != sectionIndex) { + // for (let f of this.fieldsArray(i).controls) { + // if ((f.get('category').value == FieldCategory.SYSTEM || f.get('category').value == 'SYSTEM') && f.get('type').value == systemField) { + // return true; + // } + // } + // } + // i++; + // } + // return false; + } + + selectedSystemFieldDisabled(): Array { + return (this.formGroup.get('definition').get('sections') as FormArray)?.controls.flatMap(x => (x.get('fields') as FormArray).controls.map(y => (y as UntypedFormGroup).get('systemFieldType')?.value as DmpBlueprintSystemFieldType)); + } + + addSystemField(sectionIndex: number, systemFieldType: DmpBlueprintSystemFieldType): void { + const field: FieldInSectionEditorModel = new FieldInSectionEditorModel(); + field.id = Guid.create(); + field.ordinal = ((this.formGroup.get('definition').get('sections') as FormArray).at(sectionIndex).get('fields') as FormArray).length + 1; + field.category = DmpBlueprintSectionFieldCategory.SYSTEM; + field.systemFieldType = systemFieldType; + field.required = (systemFieldType == DmpBlueprintSystemFieldType.TEXT || systemFieldType == DmpBlueprintSystemFieldType.HTML_TEXT) ? true : false; + ((this.formGroup.get('definition').get('sections') as FormArray).at(sectionIndex).get('fields') as FormArray).push(field.buildForm()); //TODO: dtziotzios validation path + } + + removeSystemField(sectionIndex: number, fieldIndex: number): void { + const formArray = ((this.formGroup.get('definition').get('sections') as FormArray).at(sectionIndex).get('fields') as FormArray); + formArray.removeAt(fieldIndex); + formArray.controls.forEach((section, index) => { + section.get('ordinal').setValue(index + 1); + }); + } + + addExtraField(sectionIndex: number): void { + const field: FieldInSectionEditorModel = new FieldInSectionEditorModel(); + field.id = Guid.create(); + field.ordinal = ((this.formGroup.get('definition').get('sections') as FormArray).at(sectionIndex).get('fields') as FormArray).length + 1; + field.category = DmpBlueprintSectionFieldCategory.EXTRA; + ((this.formGroup.get('definition').get('sections') as FormArray).at(sectionIndex).get('fields') as FormArray).push(field.buildForm()); //TODO: dtziotzios validation path + } + + removeExtraField(sectionIndex: number, fieldIndex: number): void { + const formArray = ((this.formGroup.get('definition').get('sections') as FormArray).at(sectionIndex).get('fields') as FormArray); + formArray.removeAt(fieldIndex); + formArray.controls.forEach((section, index) => { + section.get('ordinal').setValue(index + 1); + }); + } + + + dropFields(event: CdkDragDrop, sectionIndex: number) { + const fieldsFormArray = ((this.formGroup.get('definition').get('sections') as FormArray).at(sectionIndex).get('fields') as FormArray); + + moveItemInArray(fieldsFormArray.controls, event.previousIndex, event.currentIndex); + fieldsFormArray.updateValueAndValidity(); + fieldsFormArray.controls.forEach((section, index) => { + section.get('ordinal').setValue(index + 1); + }); + } + + // + // + // Autocomplete configuration + // + // + + filterDescriptionTempaltes(value: string): Observable { + const request = new DataTableRequest(null, null, { fields: ['+label'] }); + const criteria = new DatasetProfileCriteria(); + criteria.like = value; + request.criteria = criteria; + return this.dmpService.searchDmpBlueprints(request); + } + + onRemoveDescritionTemplate(event, sectionIndex: number) { + const descriptionTemplateFormArray = (this.formGroup.get('definition').get('sections') as FormArray).at(sectionIndex).get('descriptionTemplates') as FormArray; + const foundIndex = descriptionTemplateFormArray.controls.findIndex(blueprint => blueprint.get('descriptionTemplateId').value === event.id); + if (foundIndex !== -1) { + descriptionTemplateFormArray.removeAt(foundIndex); + } + } + + onSelectDescritionTemplate(item, sectionIndex) { + const descriptionTemplate: DescriptionTemplatesInSectionEditorModel = new DescriptionTemplatesInSectionEditorModel(); + descriptionTemplate.id = Guid.create(); + descriptionTemplate.descriptionTemplateId = item.id; + descriptionTemplate.label = item.label; + ((this.formGroup.get('definition').get('sections') as FormArray).at(sectionIndex).get('descriptionTemplates') as FormArray).push(descriptionTemplate.buildForm()); //TODO: dtziotzios validation path + } + + // ngAfterViewInit() { + + + + // this.route.params + // .pipe(takeUntil(this._destroyed)) + // .subscribe((params: Params) => { + // this.dmpBlueprintId = params['id']; + // const cloneId = params['cloneid']; + + // if (this.dmpBlueprintId != null) { + // this.isNew = false; + // this.dmpBlueprintService.getSingleBlueprint(this.dmpBlueprintId).pipe(map(data => data as any)) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(data => { + // this.dmpBlueprintEditor = new DmpBlueprintEditor().fromModel(data); + // this.formGroup = this.dmpBlueprintEditor.buildForm(); + // this.buildSystemFields(); + // this.fillDescriptionTemplatesInMultAutocomplete(); + // if (this.dmpBlueprintEditor.status == DmpBlueprintStatus.Finalized) { + // this.formGroup.disable(); + // this.viewOnly = true + // } + // // this.breadCrumbs = observableOf([{ + // // parentComponentName: 'DmpBlueprintListingComponent', + // // label: this.language.instant('NAV-BAR.TEMPLATE'), + // // url: '/dmp-blueprints/' + this.dmpBlueprintId + // // }]); + // }); + // } else if (cloneId != null) { + // this.isClone = true; + // this.dmpBlueprintService.clone(cloneId).pipe(map(data => data as any), takeUntil(this._destroyed)) + // .subscribe( + // data => { + // this.dmpBlueprintEditor = new DmpBlueprintEditor().fromModel(data); + // this.dmpBlueprintEditor.id = null; + // this.dmpBlueprintEditor.status = DmpBlueprintStatus.Draft; + // this.formGroup = this.dmpBlueprintEditor.buildForm(); + // this.buildSystemFields(); + // this.fillDescriptionTemplatesInMultAutocomplete(); + // }, + // error => this.onCallbackError(error) + // ); + // } else { + // this.dmpBlueprintEditorModel = new DmpBlueprintEditorModel(); + // this.dmpBlueprintEditor = new DmpBlueprintEditor(); + // setTimeout(() => { + // // this.formGroup = this.dmpBlueprintModel.buildForm(); + // // this.addField(); + // this.dmpBlueprintEditor.status = DmpBlueprintStatus.Draft; + // this.formGroup = this.dmpBlueprintEditor.buildForm(); + // }); + // // this.breadCrumbs = observableOf([{ + // // parentComponentName: 'DmpBlueprintListingComponent', + // // label: this.language.instant('NAV-BAR.TEMPLATE'), + // // url: '/dmp-blueprints/' + this.dmpBlueprintId + // // }]); + // } + // }); + + // } + + // buildSystemFields() { + // const sections = this.sectionsArray().controls.length; + // for (let i = 0; i < sections; i++) { + // let systemFieldsInSection = new Array(); + // this.fieldsArray(i).controls.forEach((field) => { + // if ((field.get('category').value == FieldCategory.SYSTEM || field.get('category').value == 'SYSTEM')) { + // systemFieldsInSection.push(this.fieldList.find(f => f.type == field.get('type').value).type); + // } + // }) + // this.systemFieldListPerSection.push(systemFieldsInSection); + // } + // } + + // fillDescriptionTemplatesInMultAutocomplete() { + // const sections = this.sectionsArray().controls.length; + // for (let i = 0; i < sections; i++) { + // let descriptionTemplatesInSection = new Array(); + // this.descriptionTemplatesArray(i).controls.forEach((template) => { + // descriptionTemplatesInSection.push({ id: template.value.descriptionTemplateId, label: template.value.label, description: "" }); + // }) + // this.descriptionTemplatesPerSection.push(descriptionTemplatesInSection); + // } + // } + + // checkForProfiles(event, sectionIndex: number) { + // if (event.checked === false) { + // this.descriptionTemplatesPerSection[sectionIndex] = new Array(); + // this.descriptionTemplatesArray(sectionIndex).clear(); + // } + // } + + + + // sectionsArray(): UntypedFormArray { + // //return this.dmpBlueprintsFormGroup.get('sections') as FormArray; + // return this.formGroup.get('definition').get('sections') as UntypedFormArray; + // } + + + // fieldsArray(sectionIndex: number): UntypedFormArray { + // return this.sectionsArray().at(sectionIndex).get('fields') as UntypedFormArray; + // } + + + + // removeField(sectionIndex: number, fieldIndex: number): void { + // this.fieldsArray(sectionIndex).removeAt(fieldIndex); + // } + + // systemFieldsArray(sectionIndex: number): UntypedFormArray { + // return this.sectionsArray().at(sectionIndex).get('systemFields') as UntypedFormArray; + // } + + // initSystemField(systemField?: SystemFieldType): UntypedFormGroup { + // return this.fb.group({ + // id: this.fb.control(Guid.create().toString()), + // type: this.fb.control(systemField), + // label: this.fb.control(''), + // placeholder: this.fb.control(''), + // description: this.fb.control(''), + // required: this.fb.control(true), + // ordinal: this.fb.control('') + // }); + // } + + // addSystemField(sectionIndex: number, systemField?: SystemFieldType): void { + // this.addField(sectionIndex, FieldCategory.SYSTEM, systemField); + // } + + // transfromEnumToString(type: SystemFieldType): string { + // return this.fieldList.find(f => f.type == type).label; + // } + + // selectedFieldType(type: SystemFieldType, sectionIndex: number): void { + // let index = this.systemFieldListPerSection[sectionIndex].indexOf(type); + // if (index == -1) { + // this.systemFieldListPerSection[sectionIndex].push(type); + // this.addSystemField(sectionIndex, type); + // } + // else { + // this.systemFieldListPerSection[sectionIndex].splice(index, 1); + // this.removeSystemField(sectionIndex, type); + // } + // } + + + + // descriptionTemplatesArray(sectionIndex: number): UntypedFormArray { + // return this.sectionsArray().at(sectionIndex).get('descriptionTemplates') as UntypedFormArray; + // } + + // addDescriptionTemplate(descriptionTemplate, sectionIndex: number): void { + // this.descriptionTemplatesArray(sectionIndex).push(this.fb.group({ + // label: this.fb.control(descriptionTemplate.value) + // })); + // } + + // removeDescriptionTemplate(sectionIndex: number, templateIndex: number): void { + // this.descriptionTemplatesArray(sectionIndex).removeAt(templateIndex); + // } + + // extraFieldsArray(sectionIndex: number): UntypedFormArray { + // return this.sectionsArray().at(sectionIndex).get('extraFields') as UntypedFormArray; + // } + + + + + + + + // getExtraFieldTypes(): Number[] { + // let keys: string[] = Object.keys(ExtraFieldType); + // keys = keys.slice(0, keys.length / 2); + // const values: Number[] = keys.map(Number); + // return values; + // } + + // getExtraFieldTypeValue(extraFieldType: ExtraFieldType): string { + // switch (extraFieldType) { + // case ExtraFieldType.TEXT: return 'Text'; + // case ExtraFieldType.RICH_TEXT: return 'Rich Text'; + // case ExtraFieldType.DATE: return 'Date'; + // case ExtraFieldType.NUMBER: return 'Number'; + // } + // } + + + + // moveItemInFormArray(formArray: UntypedFormArray, fromIndex: number, toIndex: number): void { + // const dir = toIndex > fromIndex ? 1 : -1; + + // const item = formArray.at(fromIndex); + // for (let i = fromIndex; i * dir < toIndex * dir; i = i + dir) { + // const current = formArray.at(i + dir); + // formArray.setControl(i, current); + // } + // formArray.setControl(toIndex, item); + // } + + // // clearForm(): void{ + // // this.dmpBlueprintsFormGroup.reset(); + // // } + + + + // // onPreviewTemplate(event, sectionIndex: number) { + // // const dialogRef = this.dialog.open(DatasetPreviewDialogComponent, { + // // width: '590px', + // // minHeight: '200px', + // // restoreFocus: false, + // // data: { + // // template: event + // // }, + // // panelClass: 'custom-modalbox' + // // }); + // // dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + // // if (result) { + // // let blueprints = this.sectionsArray().at(sectionIndex).get('descriptionTemplates').value;//this.formGroup.get('blueprints').value; + // // const blueprint: DescriptionTemplatesInSectionEditor = new DescriptionTemplatesInSectionEditor(); + // // blueprint.id = Guid.create().toString(); + // // blueprint.descriptionTemplateId = event.id; + // // blueprint.label = event.label; + // // blueprints.push(blueprint.buildForm()); + // // this.sectionsArray().at(sectionIndex).get('descriptionTemplates').setValue(blueprints);//this.formGroup.get('blueprints').setValue(blueprints); + // // this.blueprintsAutoCompleteConfiguration = { + // // filterFn: this.filterProfiles.bind(this), + // // initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), + // // displayFn: (item) => item['label'], + // // titleFn: (item) => item['label'], + // // subtitleFn: (item) => item['description'], + // // popupItemActionIcon: 'visibility' + // // }; + // // } + // // }); + // // } + + + + checkValidity() { + this.formService.touchAllFormFields(this.formGroup); + if (!this.isFormValid()) { return false; } + let errorMessages = []; + if (!this.hasTitle()) { + errorMessages.push("Title should be set."); + } + if (!this.hasDescription()) { + errorMessages.push("Description should be set."); + } + if (!this.hasDescriptionTemplates()) { + errorMessages.push("At least one section should have description templates."); + } + if (errorMessages.length > 0) { + this.showValidationErrorsDialog(undefined, errorMessages); + return false; + } + return true; + } + + // formSubmit(): void { + // if (this.checkValidity()) + // this.onSubmit(); + // } + + // public isFormValid() { + // return this.formGroup.valid; + // } + + hasTitle(): boolean { + const dmpBlueprint: DmpBlueprintPersist = this.formGroup.value; + return dmpBlueprint.definition.sections.some(section => section.fields.some(field => (field.category === DmpBlueprintSectionFieldCategory.SYSTEM || field.category as unknown === 'SYSTEM') && field.systemFieldType === DmpBlueprintSystemFieldType.TEXT)); + } + + hasDescription(): boolean { + const dmpBlueprint: DmpBlueprintPersist = this.formGroup.value; + return dmpBlueprint.definition.sections.some(section => section.fields.some(field => (field.category === DmpBlueprintSectionFieldCategory.SYSTEM || field.category as unknown === 'SYSTEM') && field.systemFieldType === DmpBlueprintSystemFieldType.HTML_TEXT)); + } + + hasDescriptionTemplates(): boolean { + const dmpBlueprint: DmpBlueprintPersist = this.formGroup.value; + return dmpBlueprint.definition.sections.some(section => section.hasTemplates == true); + } + + private showValidationErrorsDialog(projectOnly?: boolean, errmess?: string[]) { + + const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { + disableClose: true, + autoFocus: false, + restoreFocus: false, + data: { + errorMessages: errmess, + projectOnly: projectOnly + }, + }); + + } + + // onSubmit(): void { + // this.dmpBlueprintService.createBlueprint(this.formGroup.value) + // .pipe(takeUntil(this._destroyed)) + // .subscribe( + // complete => this.onCallbackSuccess(), + // error => this.onCallbackError(error) + // ); + // } + + // onCallbackSuccess(): void { + // this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); + // this.router.navigate(['/dmp-blueprints']); + // } + + // onCallbackError(errorResponse: any) { + // this.setErrorModel(errorResponse.error); + // this.formService.validateAllFormFields(this.formGroup); + // } + + // public setErrorModel(validationErrorModel: ValidationErrorModel) { + // Object.keys(validationErrorModel).forEach(item => { + // (this.dmpBlueprintEditor.validationErrorModel)[item] = (validationErrorModel)[item]; + // }); + // } + + public cancel(): void { + this.router.navigate(['/dmp-blueprints']); + } + + // // addField() { + // // (this.formGroup.get('definition').get('fields')).push(new DmpBlueprintFieldEditorModel().buildForm()); + // // } + + // // removeField(index: number) { + // // (this.formGroup.get('definition').get('fields')).controls.splice(index, 1); + // // } + + // getDmpBlueprintFieldDataTypeValues(): Number[] { + // let keys: string[] = Object.keys(DmpBlueprintFieldDataType); + // keys = keys.slice(0, keys.length / 2); + // const values: Number[] = keys.map(Number); + // return values; + // } + + // getDmpBlueprintFieldDataTypeWithLanguage(fieldType: DmpBlueprintFieldDataType): string { + // let result = ''; + // this.language.get(this.enumUtils.toDmpBlueprintFieldDataTypeString(fieldType)) + // .pipe(takeUntil(this._destroyed)) + // .subscribe((value: string) => { + // result = value; + // }); + // return result; + // } + + // getDmpBlueprintFieldTypeValues(): Number[] { + // let keys: string[] = Object.keys(DmpBlueprintType); + // keys = keys.slice(0, keys.length / 2); + // const values: Number[] = keys.map(Number); + // return values; + // } + + // getDmpBlueprintFieldTypeWithLanguage(blueprintType: DmpBlueprintType): string { + // let result = ''; + // this.language.get(this.enumUtils.toDmpBlueprintTypeString(blueprintType)) + // .pipe(takeUntil(this._destroyed)) + // .subscribe((value: string) => { + // result = value; + // }); + // return result; + // } + + // delete() { + // this.dialog.open(ConfirmationDialogComponent, { + // data: { + // isDeleteConfirmation: true, + // confirmButton: this.language.instant('DMP-BLUEPRINT-EDITOR.CONFIRM-DELETE-DIALOG.CONFIRM-BUTTON'), + // cancelButton: this.language.instant("DMP-BLUEPRINT-EDITOR.CONFIRM-DELETE-DIALOG.CANCEL-BUTTON"), + // message: this.language.instant("DMP-BLUEPRINT-EDITOR.CONFIRM-DELETE-DIALOG.MESSAGE") + // } + // }) + // .afterClosed() + // .subscribe( + // confirmed => { + // if (confirmed) { + // if (this.formGroup.get('status').value == DmpBlueprintStatus.Draft) { + // // this.formGroup.get('status').setValue(DmpBlueprintStatus.Deleted); + // this.dmpBlueprintService.createBlueprint(this.formGroup.value) + // .pipe(takeUntil(this._destroyed)) + // .subscribe( + // complete => this.onCallbackSuccess(), + // error => this.onCallbackError(error) + // ); + // } + // else { + // // this.dmpBlueprintService.delete(this.dmpBlueprintId) + // // .pipe(takeUntil(this._destroyed)) + // // .subscribe( + // // complete => this.onCallbackSuccess(), + // // error => { + // // if (error.error.statusCode == 674) { + // // this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DMP-BLUEPRINT-DELETE'), SnackBarNotificationLevel.Error); + // // } else { + // // this.uiNotificationService.snackBarNotification(this.language.instant(error.message), SnackBarNotificationLevel.Error); + // // } + // // } + // // ); + // } + // } + // } + // ) + + // } + + finalize() { + if (this.checkValidity()) { + this.formGroup.get('status').setValue(DmpBlueprintStatus.Finalized); + this.formSubmit(); + } + } + + downloadXML(): void { + const blueprintId = this.formGroup.get('id').value; + if (blueprintId == null) return; + this.dmpBlueprintService.downloadXML(blueprintId) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { + const blob = new Blob([response.body], { type: 'application/xml' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + + FileSaver.saveAs(blob, filename); + }); + } + + // isExternalAutocomplete(formGroup: UntypedFormGroup) { + // if (formGroup.get('dataType').value == DmpBlueprintFieldDataType.ExternalAutocomplete) { + // this.addControl(formGroup); + // return true; + // } else { + // this.removeControl(formGroup); + // return false; + // } + // } + + // addControl(formGroup: UntypedFormGroup) { + // if (formGroup.get('dataType').value == 3) + // formGroup.addControl('externalAutocomplete', new DmpBlueprintExternalAutoCompleteFieldDataEditorModel().buildForm()); + // } + + // removeControl(formGroup: UntypedFormGroup) { + // if (formGroup.get('dataType').value != 3) + // formGroup.removeControl('externalAutocomplete'); + // } } - - -// extends BaseComponent implements AfterViewInit { - -// isNew = true; -// isClone = false; -// viewOnly = false; -// dmpBlueprintEditorModel = new DmpBlueprintEditorModel(); -// dmpBlueprintEditor = new DmpBlueprintEditor(); -// formGroup: UntypedFormGroup = null; -// host: string; -// dmpBlueprintId: string; -// // breadCrumbs: Observable; - -// dmpBlueprintsFormGroup: UntypedFormGroup = null; - -// blueprintsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration; - -// fieldList = [ -// { label: 'Title', type: SystemFieldType.TEXT }, -// { label: 'Description', type: SystemFieldType.HTML_TEXT }, -// { label: 'Researchers', type: SystemFieldType.RESEARCHERS }, -// { label: 'Organizations', type: SystemFieldType.ORGANIZATIONS }, -// { label: 'Language', type: SystemFieldType.LANGUAGE }, -// { label: 'Contact', type: SystemFieldType.CONTACT }, -// { label: 'Funder', type: SystemFieldType.FUNDER }, -// { label: 'Grant', type: SystemFieldType.GRANT }, -// { label: 'Project', type: SystemFieldType.PROJECT }, -// { label: 'License', type: SystemFieldType.LICENSE }, -// { label: 'Access Rights', type: SystemFieldType.ACCESS_RIGHTS } -// ]; -// systemFieldListPerSection: Array> = new Array(); -// descriptionTemplatesPerSection: Array> = new Array>(); - -// constructor( -// private dmpBlueprintService: DmpBlueprintService, -// private _service: DmpService, -// private route: ActivatedRoute, -// private router: Router, -// private language: TranslateService, -// private enumUtils: EnumUtils, -// private uiNotificationService: UiNotificationService, -// private formService: FormService, -// private fb: UntypedFormBuilder, -// private configurationService: ConfigurationService, -// private httpClient: HttpClient, -// private matomoService: MatomoService, -// private dialog: MatDialog -// ) { -// super(); -// this.host = configurationService.server; -// } - -// ngAfterViewInit() { -// this.matomoService.trackPageView('Admin: DMP Profile Edit'); - -// this.blueprintsAutoCompleteConfiguration = { -// filterFn: this.filterProfiles.bind(this), -// initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), -// displayFn: (item) => item['label'], -// titleFn: (item) => item['label'], -// subtitleFn: (item) => item['description'], -// popupItemActionIcon: 'visibility' -// }; - -// this.route.params -// .pipe(takeUntil(this._destroyed)) -// .subscribe((params: Params) => { -// this.dmpBlueprintId = params['id']; -// const cloneId = params['cloneid']; - -// if (this.dmpBlueprintId != null) { -// this.isNew = false; -// this.dmpBlueprintService.getSingleBlueprint(this.dmpBlueprintId).pipe(map(data => data as any)) -// .pipe(takeUntil(this._destroyed)) -// .subscribe(data => { -// this.dmpBlueprintEditor = new DmpBlueprintEditor().fromModel(data); -// this.formGroup = this.dmpBlueprintEditor.buildForm(); -// this.buildSystemFields(); -// this.fillDescriptionTemplatesInMultAutocomplete(); -// if (this.dmpBlueprintEditor.status == DmpBlueprintStatus.Finalized) { -// this.formGroup.disable(); -// this.viewOnly = true -// } -// // this.breadCrumbs = observableOf([{ -// // parentComponentName: 'DmpBlueprintListingComponent', -// // label: this.language.instant('NAV-BAR.TEMPLATE'), -// // url: '/dmp-blueprints/' + this.dmpBlueprintId -// // }]); -// }); -// } else if (cloneId != null) { -// this.isClone = true; -// this.dmpBlueprintService.clone(cloneId).pipe(map(data => data as any), takeUntil(this._destroyed)) -// .subscribe( -// data => { -// this.dmpBlueprintEditor = new DmpBlueprintEditor().fromModel(data); -// this.dmpBlueprintEditor.id = null; -// this.dmpBlueprintEditor.status = DmpBlueprintStatus.Draft; -// this.formGroup = this.dmpBlueprintEditor.buildForm(); -// this.buildSystemFields(); -// this.fillDescriptionTemplatesInMultAutocomplete(); -// }, -// error => this.onCallbackError(error) -// ); -// } else { -// this.dmpBlueprintEditorModel = new DmpBlueprintEditorModel(); -// this.dmpBlueprintEditor = new DmpBlueprintEditor(); -// setTimeout(() => { -// // this.formGroup = this.dmpBlueprintModel.buildForm(); -// // this.addField(); -// this.dmpBlueprintEditor.status = DmpBlueprintStatus.Draft; -// this.formGroup = this.dmpBlueprintEditor.buildForm(); -// }); -// // this.breadCrumbs = observableOf([{ -// // parentComponentName: 'DmpBlueprintListingComponent', -// // label: this.language.instant('NAV-BAR.TEMPLATE'), -// // url: '/dmp-blueprints/' + this.dmpBlueprintId -// // }]); -// } -// }); - -// } - -// buildSystemFields() { -// const sections = this.sectionsArray().controls.length; -// for (let i = 0; i < sections; i++) { -// let systemFieldsInSection = new Array(); -// this.fieldsArray(i).controls.forEach((field) => { -// if ((field.get('category').value == FieldCategory.SYSTEM || field.get('category').value == 'SYSTEM')) { -// systemFieldsInSection.push(this.fieldList.find(f => f.type == field.get('type').value).type); -// } -// }) -// this.systemFieldListPerSection.push(systemFieldsInSection); -// } -// } - -// fillDescriptionTemplatesInMultAutocomplete() { -// const sections = this.sectionsArray().controls.length; -// for (let i = 0; i < sections; i++) { -// let descriptionTemplatesInSection = new Array(); -// this.descriptionTemplatesArray(i).controls.forEach((template) => { -// descriptionTemplatesInSection.push({ id: template.value.descriptionTemplateId, label: template.value.label, description: "" }); -// }) -// this.descriptionTemplatesPerSection.push(descriptionTemplatesInSection); -// } -// } - -// checkForProfiles(event, sectionIndex: number) { -// if (event.checked === false) { -// this.descriptionTemplatesPerSection[sectionIndex] = new Array(); -// this.descriptionTemplatesArray(sectionIndex).clear(); -// } -// } - -// filterProfiles(value: string): Observable { -// const request = new DataTableRequest(null, null, { fields: ['+label'] }); -// const criteria = new DatasetProfileCriteria(); -// criteria.like = value; -// request.criteria = criteria; -// return this._service.searchDmpBlueprints(request); -// } - -// sectionsArray(): UntypedFormArray { -// //return this.dmpBlueprintsFormGroup.get('sections') as FormArray; -// return this.formGroup.get('definition').get('sections') as UntypedFormArray; -// } - -// addSection(): void { -// const section: SectionDmpBlueprintEditor = new SectionDmpBlueprintEditor(); -// section.id = Guid.create().toString(); -// section.ordinal = this.sectionsArray().length + 1; -// section.hasTemplates = false; -// this.sectionsArray().push(section.buildForm()); -// this.systemFieldListPerSection.push(new Array()); -// } - -// removeSection(sectionIndex: number): void { -// this.systemFieldListPerSection.splice(sectionIndex, 1); -// this.sectionsArray().removeAt(sectionIndex); -// this.sectionsArray().controls.forEach((section, index) => { -// section.get('ordinal').setValue(index + 1); -// }); -// } - -// fieldsArray(sectionIndex: number): UntypedFormArray { -// return this.sectionsArray().at(sectionIndex).get('fields') as UntypedFormArray; -// } - -// addField(sectionIndex: number, fieldCategory: FieldCategory, fieldType?: number): void { -// const field: FieldInSectionEditor = new FieldInSectionEditor(); -// field.id = Guid.create().toString(); -// field.ordinal = this.fieldsArray(sectionIndex).length + 1; -// field.category = fieldCategory; -// if (!isNullOrUndefined(fieldType)) { -// field.type = fieldType -// } -// field.required = (!isNullOrUndefined(fieldType) && (fieldType == 0 || fieldType == 1)) ? true : false; -// this.fieldsArray(sectionIndex).push(field.buildForm()); -// } - -// removeField(sectionIndex: number, fieldIndex: number): void { -// this.fieldsArray(sectionIndex).removeAt(fieldIndex); -// } - -// systemFieldsArray(sectionIndex: number): UntypedFormArray { -// return this.sectionsArray().at(sectionIndex).get('systemFields') as UntypedFormArray; -// } - -// initSystemField(systemField?: SystemFieldType): UntypedFormGroup { -// return this.fb.group({ -// id: this.fb.control(Guid.create().toString()), -// type: this.fb.control(systemField), -// label: this.fb.control(''), -// placeholder: this.fb.control(''), -// description: this.fb.control(''), -// required: this.fb.control(true), -// ordinal: this.fb.control('') -// }); -// } - -// addSystemField(sectionIndex: number, systemField?: SystemFieldType): void { -// this.addField(sectionIndex, FieldCategory.SYSTEM, systemField); -// } - -// transfromEnumToString(type: SystemFieldType): string { -// return this.fieldList.find(f => f.type == type).label; -// } - -// selectedFieldType(type: SystemFieldType, sectionIndex: number): void { -// let index = this.systemFieldListPerSection[sectionIndex].indexOf(type); -// if (index == -1) { -// this.systemFieldListPerSection[sectionIndex].push(type); -// this.addSystemField(sectionIndex, type); -// } -// else { -// this.systemFieldListPerSection[sectionIndex].splice(index, 1); -// this.removeSystemField(sectionIndex, type); -// } -// } - -// systemFieldDisabled(systemField: SystemFieldType, sectionIndex: number) { -// let i = 0; -// for (let s in this.sectionsArray().controls) { -// if (i != sectionIndex) { -// for (let f of this.fieldsArray(i).controls) { -// if ((f.get('category').value == FieldCategory.SYSTEM || f.get('category').value == 'SYSTEM') && f.get('type').value == systemField) { -// return true; -// } -// } -// } -// i++; -// } -// return false; -// } - -// removeSystemFieldWithIndex(sectionIndex: number, fieldIndex: number): void { -// let type: SystemFieldType = this.fieldsArray(sectionIndex).at(fieldIndex).get('type').value; -// let index = this.systemFieldListPerSection[sectionIndex].indexOf(type); -// this.systemFieldListPerSection[sectionIndex] = this.systemFieldListPerSection[sectionIndex].filter(types => types != type); -// this.fieldsArray(sectionIndex).removeAt(fieldIndex); -// } - -// removeSystemField(sectionIndex: number, systemField: SystemFieldType): void { -// let i = 0; -// for (let f of this.fieldsArray(sectionIndex).controls) { -// if ((f.get('category').value == FieldCategory.SYSTEM || f.get('category').value == 'SYSTEM') && f.get('type').value == systemField) { -// this.fieldsArray(sectionIndex).removeAt(i); -// return; -// } -// i++; -// } -// } - -// descriptionTemplatesArray(sectionIndex: number): UntypedFormArray { -// return this.sectionsArray().at(sectionIndex).get('descriptionTemplates') as UntypedFormArray; -// } - -// addDescriptionTemplate(descriptionTemplate, sectionIndex: number): void { -// this.descriptionTemplatesArray(sectionIndex).push(this.fb.group({ -// label: this.fb.control(descriptionTemplate.value) -// })); -// } - -// removeDescriptionTemplate(sectionIndex: number, templateIndex: number): void { -// this.descriptionTemplatesArray(sectionIndex).removeAt(templateIndex); -// } - -// extraFieldsArray(sectionIndex: number): UntypedFormArray { -// return this.sectionsArray().at(sectionIndex).get('extraFields') as UntypedFormArray; -// } - -// addExtraField(sectionIndex: number): void { -// this.addField(sectionIndex, FieldCategory.EXTRA); -// } - -// removeExtraField(sectionIndex: number, fieldIndex: number): void { -// this.fieldsArray(sectionIndex).removeAt(fieldIndex); -// } - -// getExtraFieldTypes(): Number[] { -// let keys: string[] = Object.keys(ExtraFieldType); -// keys = keys.slice(0, keys.length / 2); -// const values: Number[] = keys.map(Number); -// return values; -// } - -// getExtraFieldTypeValue(extraFieldType: ExtraFieldType): string { -// switch (extraFieldType) { -// case ExtraFieldType.TEXT: return 'Text'; -// case ExtraFieldType.RICH_TEXT: return 'Rich Text'; -// case ExtraFieldType.DATE: return 'Date'; -// case ExtraFieldType.NUMBER: return 'Number'; -// } -// } - -// drop(event: CdkDragDrop, sectionIndex: number) { -// moveItemInArray(this.fieldsArray(sectionIndex).controls, event.previousIndex, event.currentIndex); -// moveItemInArray(this.fieldsArray(sectionIndex).value, event.previousIndex, event.currentIndex); -// } - -// dropSections(event: CdkDragDrop) { -// moveItemInArray(this.sectionsArray().controls, event.previousIndex, event.currentIndex); -// moveItemInArray(this.sectionsArray().value, event.previousIndex, event.currentIndex); -// this.sectionsArray().controls.forEach((section, index) => { -// section.get('ordinal').setValue(index + 1); -// }); -// } - -// moveItemInFormArray(formArray: UntypedFormArray, fromIndex: number, toIndex: number): void { -// const dir = toIndex > fromIndex ? 1 : -1; - -// const item = formArray.at(fromIndex); -// for (let i = fromIndex; i * dir < toIndex * dir; i = i + dir) { -// const current = formArray.at(i + dir); -// formArray.setControl(i, current); -// } -// formArray.setControl(toIndex, item); -// } - -// // clearForm(): void{ -// // this.dmpBlueprintsFormGroup.reset(); -// // } - -// onRemoveTemplate(event, sectionIndex: number) { -// const blueprints = this.descriptionTemplatesArray(sectionIndex).controls; -// const foundIndex = blueprints.findIndex(blueprint => blueprint.get('descriptionTemplateId').value === event.id); -// foundIndex !== -1 && this.descriptionTemplatesArray(sectionIndex).removeAt(foundIndex); -// } - -// // onPreviewTemplate(event, sectionIndex: number) { -// // const dialogRef = this.dialog.open(DatasetPreviewDialogComponent, { -// // width: '590px', -// // minHeight: '200px', -// // restoreFocus: false, -// // data: { -// // template: event -// // }, -// // panelClass: 'custom-modalbox' -// // }); -// // dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { -// // if (result) { -// // let blueprints = this.sectionsArray().at(sectionIndex).get('descriptionTemplates').value;//this.formGroup.get('blueprints').value; -// // const blueprint: DescriptionTemplatesInSectionEditor = new DescriptionTemplatesInSectionEditor(); -// // blueprint.id = Guid.create().toString(); -// // blueprint.descriptionTemplateId = event.id; -// // blueprint.label = event.label; -// // blueprints.push(blueprint.buildForm()); -// // this.sectionsArray().at(sectionIndex).get('descriptionTemplates').setValue(blueprints);//this.formGroup.get('blueprints').setValue(blueprints); -// // this.blueprintsAutoCompleteConfiguration = { -// // filterFn: this.filterProfiles.bind(this), -// // initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), -// // displayFn: (item) => item['label'], -// // titleFn: (item) => item['label'], -// // subtitleFn: (item) => item['description'], -// // popupItemActionIcon: 'visibility' -// // }; -// // } -// // }); -// // } - -// onOptionSelected(item, sectionIndex) { -// const blueprint: DescriptionTemplatesInSectionEditor = new DescriptionTemplatesInSectionEditor(); -// blueprint.id = Guid.create().toString(); -// blueprint.descriptionTemplateId = item.id; -// blueprint.label = item.label; -// this.descriptionTemplatesArray(sectionIndex).push(blueprint.buildForm()); -// } - -// checkValidity() { -// this.formService.touchAllFormFields(this.formGroup); -// if (!this.isFormValid()) { return false; } -// let errorMessages = []; -// if (!this.hasTitle()) { -// errorMessages.push("Title should be set."); -// } -// if (!this.hasDescription()) { -// errorMessages.push("Description should be set."); -// } -// if (!this.hasDescriptionTemplates()) { -// errorMessages.push("At least one section should have description templates."); -// } -// if (errorMessages.length > 0) { -// this.showValidationErrorsDialog(undefined, errorMessages); -// return false; -// } -// return true; -// } - -// formSubmit(): void { -// if (this.checkValidity()) -// this.onSubmit(); -// } - -// public isFormValid() { -// return this.formGroup.valid; -// } - -// hasTitle(): boolean { -// const dmpBlueprint: DmpBlueprint = this.formGroup.value; -// return dmpBlueprint.definition.sections.some(section => section.fields.some(field => (field.category === FieldCategory.SYSTEM || field.category as unknown === 'SYSTEM') && field.type === SystemFieldType.TEXT)); -// } - -// hasDescription(): boolean { -// const dmpBlueprint: DmpBlueprint = this.formGroup.value; -// return dmpBlueprint.definition.sections.some(section => section.fields.some(field => (field.category === FieldCategory.SYSTEM || field.category as unknown === 'SYSTEM') && field.type === SystemFieldType.HTML_TEXT)); -// } - -// hasDescriptionTemplates(): boolean { -// const dmpBlueprint: DmpBlueprint = this.formGroup.value; -// return dmpBlueprint.definition.sections.some(section => section.hasTemplates == true); -// } - -// private showValidationErrorsDialog(projectOnly?: boolean, errmess?: string[]) { - -// const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { -// disableClose: true, -// autoFocus: false, -// restoreFocus: false, -// data: { -// errorMessages: errmess, -// projectOnly: projectOnly -// }, -// }); - -// } - -// onSubmit(): void { -// this.dmpBlueprintService.createBlueprint(this.formGroup.value) -// .pipe(takeUntil(this._destroyed)) -// .subscribe( -// complete => this.onCallbackSuccess(), -// error => this.onCallbackError(error) -// ); -// } - -// onCallbackSuccess(): void { -// this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); -// this.router.navigate(['/dmp-blueprints']); -// } - -// onCallbackError(errorResponse: any) { -// this.setErrorModel(errorResponse.error); -// this.formService.validateAllFormFields(this.formGroup); -// } - -// public setErrorModel(validationErrorModel: ValidationErrorModel) { -// Object.keys(validationErrorModel).forEach(item => { -// (this.dmpBlueprintEditor.validationErrorModel)[item] = (validationErrorModel)[item]; -// }); -// } - -// public cancel(): void { -// this.router.navigate(['/dmp-blueprints']); -// } - -// // addField() { -// // (this.formGroup.get('definition').get('fields')).push(new DmpBlueprintFieldEditorModel().buildForm()); -// // } - -// // removeField(index: number) { -// // (this.formGroup.get('definition').get('fields')).controls.splice(index, 1); -// // } - -// getDmpBlueprintFieldDataTypeValues(): Number[] { -// let keys: string[] = Object.keys(DmpBlueprintFieldDataType); -// keys = keys.slice(0, keys.length / 2); -// const values: Number[] = keys.map(Number); -// return values; -// } - -// getDmpBlueprintFieldDataTypeWithLanguage(fieldType: DmpBlueprintFieldDataType): string { -// let result = ''; -// this.language.get(this.enumUtils.toDmpBlueprintFieldDataTypeString(fieldType)) -// .pipe(takeUntil(this._destroyed)) -// .subscribe((value: string) => { -// result = value; -// }); -// return result; -// } - -// getDmpBlueprintFieldTypeValues(): Number[] { -// let keys: string[] = Object.keys(DmpBlueprintType); -// keys = keys.slice(0, keys.length / 2); -// const values: Number[] = keys.map(Number); -// return values; -// } - -// getDmpBlueprintFieldTypeWithLanguage(blueprintType: DmpBlueprintType): string { -// let result = ''; -// this.language.get(this.enumUtils.toDmpBlueprintTypeString(blueprintType)) -// .pipe(takeUntil(this._destroyed)) -// .subscribe((value: string) => { -// result = value; -// }); -// return result; -// } - -// delete() { -// this.dialog.open(ConfirmationDialogComponent, { -// data: { -// isDeleteConfirmation: true, -// confirmButton: this.language.instant('DMP-BLUEPRINT-EDITOR.CONFIRM-DELETE-DIALOG.CONFIRM-BUTTON'), -// cancelButton: this.language.instant("DMP-BLUEPRINT-EDITOR.CONFIRM-DELETE-DIALOG.CANCEL-BUTTON"), -// message: this.language.instant("DMP-BLUEPRINT-EDITOR.CONFIRM-DELETE-DIALOG.MESSAGE") -// } -// }) -// .afterClosed() -// .subscribe( -// confirmed => { -// if (confirmed) { -// if (this.formGroup.get('status').value == DmpBlueprintStatus.Draft) { -// // this.formGroup.get('status').setValue(DmpBlueprintStatus.Deleted); -// this.dmpBlueprintService.createBlueprint(this.formGroup.value) -// .pipe(takeUntil(this._destroyed)) -// .subscribe( -// complete => this.onCallbackSuccess(), -// error => this.onCallbackError(error) -// ); -// } -// else { -// // this.dmpBlueprintService.delete(this.dmpBlueprintId) -// // .pipe(takeUntil(this._destroyed)) -// // .subscribe( -// // complete => this.onCallbackSuccess(), -// // error => { -// // if (error.error.statusCode == 674) { -// // this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DMP-BLUEPRINT-DELETE'), SnackBarNotificationLevel.Error); -// // } else { -// // this.uiNotificationService.snackBarNotification(this.language.instant(error.message), SnackBarNotificationLevel.Error); -// // } -// // } -// // ); -// } -// } -// } -// ) - -// } - -// finalize() { -// if (this.checkValidity()) { -// this.formGroup.get('status').setValue(DmpBlueprintStatus.Finalized); -// this.onSubmit(); -// } -// } - -// downloadXML(): void { -// this.dmpBlueprintService.downloadXML(this.dmpBlueprintId) -// .pipe(takeUntil(this._destroyed)) -// .subscribe(response => { -// const blob = new Blob([response.body], { type: 'application/xml' }); -// const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); - -// FileSaver.saveAs(blob, filename); -// }); -// } - - -// getFilenameFromContentDispositionHeader(header: string): string { -// const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - -// const matches = header.match(regex); -// let filename: string; -// for (let i = 0; i < matches.length; i++) { -// const match = matches[i]; -// if (match.includes('filename="')) { -// filename = match.substring(10, match.length - 1); -// break; -// } else if (match.includes('filename=')) { -// filename = match.substring(9); -// break; -// } -// } -// return filename; -// } - -// isExternalAutocomplete(formGroup: UntypedFormGroup) { -// if (formGroup.get('dataType').value == DmpBlueprintFieldDataType.ExternalAutocomplete) { -// this.addControl(formGroup); -// return true; -// } else { -// this.removeControl(formGroup); -// return false; -// } -// } - -// addControl(formGroup: UntypedFormGroup) { -// if (formGroup.get('dataType').value == 3) -// formGroup.addControl('externalAutocomplete', new DmpBlueprintExternalAutoCompleteFieldDataEditorModel().buildForm()); -// } - -// removeControl(formGroup: UntypedFormGroup) { -// if (formGroup.get('dataType').value != 3) -// formGroup.removeControl('externalAutocomplete'); -// } -// } diff --git a/dmp-frontend/src/app/ui/admin/dmp-blueprint/listing/dmp-blueprint-listing.component.ts b/dmp-frontend/src/app/ui/admin/dmp-blueprint/listing/dmp-blueprint-listing.component.ts index d3305c9f7..4a12053d6 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-blueprint/listing/dmp-blueprint-listing.component.ts +++ b/dmp-frontend/src/app/ui/admin/dmp-blueprint/listing/dmp-blueprint-listing.component.ts @@ -182,7 +182,7 @@ export class DmpBlueprintListingComponent extends BaseListingComponent { // const blob = new Blob([response.body], { type: 'application/xml' }); - // const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + // const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); // FileSaver.saveAs(blob, filename); // }); } @@ -209,27 +209,10 @@ export class DmpBlueprintListingComponent extends BaseListingComponent { // const blob = new Blob([response.body], { type: 'application/xml' }); - // const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + // const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); // FileSaver.saveAs(blob, filename); // }); // } - // getFilenameFromContentDispositionHeader(header: string): string { - // const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - // const matches = header.match(regex); - // let filename: string; - // for (let i = 0; i < matches.length; i++) { - // const match = matches[i]; - // if (match.includes('filename="')) { - // filename = match.substring(10, match.length - 1); - // break; - // } else if (match.includes('filename=')) { - // filename = match.substring(9); - // break; - // } - // } - // return filename; - // } // deleteTemplate(id: string) { // if (id) { diff --git a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts index 82f8119b6..9e5456559 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts +++ b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts @@ -18,6 +18,7 @@ import { UserCriteria } from '../../../../core/query/user/user-criteria'; import { UserService } from '../../../../core/services/user/user.service'; import { SnackBarNotificationComponent } from '../../../../library/notification/snack-bar/snack-bar-notification.component'; // import { BreadcrumbItem } from '../../../misc/breadcrumb/definition/breadcrumb-item'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; import { UserCriteriaComponent } from './criteria/user-criteria.component'; export class UsersDataSource extends DataSource { @@ -111,7 +112,8 @@ export class UserListingComponent extends BaseComponent implements OnInit, After private languageService: TranslateService, public snackBar: MatSnackBar, private httpClient: HttpClient, - private matomoService: MatomoService + private matomoService: MatomoService, + private fileUtils: FileUtils ) { super(); } @@ -151,29 +153,11 @@ export class UserListingComponent extends BaseComponent implements OnInit, After .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/csv' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); }); } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - public setDefaultAvatar(ev: Event) { (ev.target as HTMLImageElement).src = 'assets/images/profile-placeholder.png'; } diff --git a/dmp-frontend/src/app/ui/dashboard/drafts/drafts.component.ts b/dmp-frontend/src/app/ui/dashboard/drafts/drafts.component.ts index 563ae7ecb..5c726535c 100644 --- a/dmp-frontend/src/app/ui/dashboard/drafts/drafts.component.ts +++ b/dmp-frontend/src/app/ui/dashboard/drafts/drafts.component.ts @@ -1,50 +1,48 @@ -import {Component, OnInit, Input, EventEmitter, Output, ViewChild} from '@angular/core'; -import { DatasetService } from '../../../core/services/dataset/dataset.service'; -import {DataTableMultiTypeRequest, DataTableRequest} from '../../../core/model/data-table/data-table-request'; -import { DatasetCriteria } from '../../../core/query/dataset/dataset-criteria'; -import { DatasetListingModel } from '../../../core/model/dataset/dataset-listing'; -import { AuthService } from '../../../core/services/auth/auth.service'; -import { RecentActivityType } from '../../../core/common/enum/recent-activity-type'; -import {ActivatedRoute, Router} from '@angular/router'; -import { DmpStatus } from '../../../core/common/enum/dmp-status'; -import { TranslateService } from '@ngx-translate/core'; -import {debounceTime, map, takeUntil} from 'rxjs/operators'; -import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; -import { DatasetCopyDialogueComponent } from '@app/ui/dataset/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; -import {UntypedFormControl, UntypedFormBuilder, UntypedFormGroup} from '@angular/forms'; -import { BaseComponent } from '@common/base/base.component'; -import { MatDialog } from '@angular/material/dialog'; -import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; -import { SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service'; -import * as FileSaver from 'file-saver'; -import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; -import { UiNotificationService } from '@app/core/services/notification/ui-notification-service'; -import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; -import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; import { Location } from '@angular/common'; +import { HttpClient } from '@angular/common/http'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; +import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; import { Role } from '@app/core/common/enum/role'; +import { DatasetUrlListing } from "@app/core/model/dataset/dataset-url-listing"; +import { DmpBlueprintDefinition } from '@app/core/model/dmp-blueprint/dmp-blueprint'; +import { DmpModel } from "@app/core/model/dmp/dmp"; +import { DmpListingModel } from "@app/core/model/dmp/dmp-listing"; +import { RecentActivityModel } from "@app/core/model/recent-activity/recent-activity.model"; +import { RecentDatasetModel } from "@app/core/model/recent-activity/recent-dataset-activity.model"; +import { RecentDmpModel } from "@app/core/model/recent-activity/recent-dmp-activity.model"; +import { RecentActivityCriteria } from "@app/core/query/recent-activity/recent-activity-criteria"; +import { DashboardService } from "@app/core/services/dashboard/dashboard.service"; +import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; +import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; +import { DmpService } from "@app/core/services/dmp/dmp.service"; import { LockService } from '@app/core/services/lock/lock.service'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; -import { HttpClient } from '@angular/common/http'; -import {RecentActivityModel} from "@app/core/model/recent-activity/recent-activity.model"; -import {DmpEditorModel} from "@app/ui/dmp/editor/dmp-editor.model"; -import {DmpService} from "@app/core/services/dmp/dmp.service"; -import {DashboardService} from "@app/core/services/dashboard/dashboard.service"; -import {RecentActivityCriteria} from "@app/core/query/recent-activity/recent-activity-criteria"; -import {RecentDmpModel} from "@app/core/model/recent-activity/recent-dmp-activity.model"; -import {DatasetUrlListing} from "@app/core/model/dataset/dataset-url-listing"; -import {RecentDatasetModel} from "@app/core/model/recent-activity/recent-dataset-activity.model"; -import {DmpListingModel} from "@app/core/model/dmp/dmp-listing"; -import {DmpModel} from "@app/core/model/dmp/dmp"; -import {GrantTabModel} from "@app/ui/dmp/editor/grant-tab/grant-tab-model"; -import {ProjectFormModel} from "@app/ui/dmp/editor/grant-tab/project-form-model"; -import {FunderFormModel} from "@app/ui/dmp/editor/grant-tab/funder-form-model"; -import {ExtraPropertiesFormModel} from "@app/ui/dmp/editor/general-tab/extra-properties-form.model"; -import {CloneDialogComponent} from "@app/ui/dmp/clone/clone-dialog/clone-dialog.component"; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; +import { DatasetCopyDialogueComponent } from '@app/ui/dataset/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; +import { CloneDialogComponent } from "@app/ui/dmp/clone/clone-dialog/clone-dialog.component"; +import { DmpEditorModel } from "@app/ui/dmp/editor/dmp-editor.model"; +import { ExtraPropertiesFormModel } from "@app/ui/dmp/editor/general-tab/extra-properties-form.model"; +import { FunderFormModel } from "@app/ui/dmp/editor/grant-tab/funder-form-model"; +import { GrantTabModel } from "@app/ui/dmp/editor/grant-tab/grant-tab-model"; +import { ProjectFormModel } from "@app/ui/dmp/editor/grant-tab/project-form-model"; +import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; -import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; -import { DmpBlueprintDefinition } from '@app/core/model/dmp-blueprint/dmp-blueprint'; -import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; +import { BaseComponent } from '@common/base/base.component'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { debounceTime, map, takeUntil } from 'rxjs/operators'; +import { DmpStatus } from '../../../core/common/enum/dmp-status'; +import { RecentActivityType } from '../../../core/common/enum/recent-activity-type'; +import { DataTableMultiTypeRequest } from '../../../core/model/data-table/data-table-request'; +import { DatasetListingModel } from '../../../core/model/dataset/dataset-listing'; +import { AuthService } from '../../../core/services/auth/auth.service'; @Component({ selector: 'app-drafts', @@ -67,7 +65,7 @@ export class DraftsComponent extends BaseComponent implements OnInit { offsetLess: number = 0; pageSize: number = 5; dmpFormGroup: UntypedFormGroup; - hasMoreActivity:boolean = true; + hasMoreActivity: boolean = true; public formGroup = new UntypedFormBuilder().group({ like: new UntypedFormControl(), order: new UntypedFormControl() @@ -94,7 +92,8 @@ export class DraftsComponent extends BaseComponent implements OnInit { private location: Location, private lockService: LockService, private httpClient: HttpClient, - private matomoService: MatomoService + private matomoService: MatomoService, + private fileUtils: FileUtils ) { super(); } @@ -102,18 +101,18 @@ export class DraftsComponent extends BaseComponent implements OnInit { ngOnInit() { this.matomoService.trackPageView('Drafts'); this.route.queryParams.subscribe(params => { - if(this.isActive) { + if (this.isActive) { let page = (params['page'] === undefined) ? 1 : +params['page']; this.page = (page <= 0) ? 1 : page; - this.datasetOffset = (this.page-1)*this.pageSize; - this.dmpOffset = (this.page-1)*this.pageSize; - if(this.page > 1) { - this.offsetLess = (this.page-2)*this.pageSize; + this.datasetOffset = (this.page - 1) * this.pageSize; + this.dmpOffset = (this.page - 1) * this.pageSize; + if (this.page > 1) { + this.offsetLess = (this.page - 2) * this.pageSize; } let order = params['order']; - if(order === undefined || (order != this.order.MODIFIED && order != this.order.LABEL)) { + if (order === undefined || (order != this.order.MODIFIED && order != this.order.LABEL)) { order = this.order.MODIFIED; } this.formGroup.get('order').setValue(order); @@ -129,7 +128,7 @@ export class DraftsComponent extends BaseComponent implements OnInit { this.formGroup.get('order').setValue(this.order.MODIFIED); } const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; - const allDataTableRequest: DataTableMultiTypeRequest = new DataTableMultiTypeRequest(this.dmpOffset, this.datasetOffset, 5, {fields: fields}); + const allDataTableRequest: DataTableMultiTypeRequest = new DataTableMultiTypeRequest(this.dmpOffset, this.datasetOffset, 5, { fields: fields }); allDataTableRequest.criteria = new RecentActivityCriteria(); allDataTableRequest.criteria.like = this.formGroup.get('like').value; allDataTableRequest.criteria.order = this.formGroup.get('order').value; @@ -151,11 +150,11 @@ export class DraftsComponent extends BaseComponent implements OnInit { }); this.totalCountRecentEdited.emit(this.allRecentActivities.length); if (this.allRecentActivities.length == 0 && this.page > 1) { - let queryParams = {type: "recent", page: 1, order: this.formGroup.get("order").value}; + let queryParams = { type: "recent", page: 1, order: this.formGroup.get("order").value }; if (this.formGroup.get("like").value) { queryParams['keyword'] = this.formGroup.get("like").value; } - this.router.navigate(["/home"], {queryParams: queryParams}) + this.router.navigate(["/home"], { queryParams: queryParams }) } }); this.formGroup.get('like').valueChanges @@ -172,17 +171,17 @@ export class DraftsComponent extends BaseComponent implements OnInit { } ngOnChanges() { - if(this.isActive) { + if (this.isActive) { this.updateUrl(); } } updateUrl() { - let parameters = "?type=drafts"+ - (this.page != 1 ? "&page="+this.page : "") + - (this.formGroup.get("order").value != this.order.MODIFIED ? "&order="+this.formGroup.get("order").value : "") + - (this.formGroup.get("like").value ? ("&keyword="+this.formGroup.get("like").value) : ""); - this.location.go(this.router.url.split('?')[0]+parameters); + let parameters = "?type=drafts" + + (this.page != 1 ? "&page=" + this.page : "") + + (this.formGroup.get("order").value != this.order.MODIFIED ? "&order=" + this.formGroup.get("order").value : "") + + (this.formGroup.get("like").value ? ("&keyword=" + this.formGroup.get("like").value) : ""); + this.location.go(this.router.url.split('?')[0] + parameters); } getDatasets(activity: RecentDmpModel): DatasetUrlListing[] { @@ -492,7 +491,7 @@ export class DraftsComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "xml", id); @@ -504,7 +503,7 @@ export class DraftsComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "docx", id); @@ -516,7 +515,7 @@ export class DraftsComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "pdf", id); @@ -528,7 +527,7 @@ export class DraftsComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/json' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "json", id); }, async error => { @@ -547,7 +546,7 @@ export class DraftsComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "pdf", dataset.id); @@ -559,7 +558,7 @@ export class DraftsComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "docx", dataset.id); @@ -572,31 +571,13 @@ export class DraftsComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "xml", dataset.id); }); } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - viewVersions(rowId: String, rowLabel: String, activity: DmpListingModel) { if (activity.public && !this.isUserOwner(activity)) { let url = this.router.createUrlTree(['/explore-plans/versions/', rowId, { groupLabel: rowLabel }]); @@ -702,14 +683,14 @@ export class DraftsComponent extends BaseComponent implements OnInit { this.allRecentActivities.forEach(recentActivity => { if (recentActivity.type === RecentActivityType.Dataset) { // this.datasetOffset = this.datasetOffset + 1; - this.datasetOffset = this.page*this.pageSize; + this.datasetOffset = this.page * this.pageSize; } else if (recentActivity.type === RecentActivityType.Dmp) { // this.dmpOffset = this.dmpOffset + 1; - this.dmpOffset = this.page*this.pageSize; + this.dmpOffset = this.page * this.pageSize; } }); - if(response.length< this.pageSize) { + if (response.length < this.pageSize) { this.hasMoreActivity = false; } else { this.hasMoreActivity = true; @@ -722,10 +703,10 @@ export class DraftsComponent extends BaseComponent implements OnInit { const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; // const fields: Array = ["-modified"]; let request; - if(more) { - request = new DataTableMultiTypeRequest(this.dmpOffset, this.datasetOffset, this.pageSize, {fields: fields}); + if (more) { + request = new DataTableMultiTypeRequest(this.dmpOffset, this.datasetOffset, this.pageSize, { fields: fields }); } else { - request = new DataTableMultiTypeRequest(this.offsetLess, this.offsetLess, this.pageSize, {fields: fields}); + request = new DataTableMultiTypeRequest(this.offsetLess, this.offsetLess, this.pageSize, { fields: fields }); } request.criteria = new RecentActivityCriteria(); request.criteria.like = this.formGroup.get("like").value ? this.formGroup.get("like").value : ""; @@ -754,7 +735,7 @@ export class DraftsComponent extends BaseComponent implements OnInit { this.offsetLess = (this.page - 2) * this.pageSize; } - if(result.length < this.pageSize) { + if (result.length < this.pageSize) { this.hasMoreActivity = false; } else { this.hasMoreActivity = true; diff --git a/dmp-frontend/src/app/ui/dashboard/recent-edited-activity/recent-edited-activity.component.ts b/dmp-frontend/src/app/ui/dashboard/recent-edited-activity/recent-edited-activity.component.ts index cc3cdc585..95f441e3e 100644 --- a/dmp-frontend/src/app/ui/dashboard/recent-edited-activity/recent-edited-activity.component.ts +++ b/dmp-frontend/src/app/ui/dashboard/recent-edited-activity/recent-edited-activity.component.ts @@ -1,52 +1,50 @@ -import {Component, OnInit, Output, EventEmitter, Input, ViewChild} from '@angular/core'; +import { Location } from '@angular/common'; +import { HttpClient } from '@angular/common/http'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; -import {ActivatedRoute, Router} from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; +import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; +import { DmpStatus } from '@app/core/common/enum/dmp-status'; +import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; import { RecentActivityType } from '@app/core/common/enum/recent-activity-type'; -import { DataTableRequest, DataTableMultiTypeRequest } from '@app/core/model/data-table/data-table-request'; +import { Role } from '@app/core/common/enum/role'; +import { DataTableMultiTypeRequest } from '@app/core/model/data-table/data-table-request'; +import { DatasetListingModel } from '@app/core/model/dataset/dataset-listing'; +import { DatasetUrlListing } from '@app/core/model/dataset/dataset-url-listing'; +import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; +import { DmpModel } from '@app/core/model/dmp/dmp'; import { DmpListingModel } from '@app/core/model/dmp/dmp-listing'; -import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria'; +import { RecentActivityModel } from '@app/core/model/recent-activity/recent-activity.model'; +import { RecentDatasetModel } from '@app/core/model/recent-activity/recent-dataset-activity.model'; +import { RecentDmpModel } from '@app/core/model/recent-activity/recent-dmp-activity.model'; +import { RecentActivityCriteria } from '@app/core/query/recent-activity/recent-activity-criteria'; import { AuthService } from '@app/core/services/auth/auth.service'; +import { DashboardService } from '@app/core/services/dashboard/dashboard.service'; +import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; +import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; import { DmpService } from '@app/core/services/dmp/dmp.service'; +import { LockService } from '@app/core/services/lock/lock.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; -import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; -import { BaseComponent } from '@common/base/base.component'; -import { TranslateService } from '@ngx-translate/core'; -import * as FileSaver from 'file-saver'; -import { takeUntil, map, debounceTime } from 'rxjs/operators'; -import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; -import { DmpStatus } from '@app/core/common/enum/dmp-status'; -import { DatasetService } from '@app/core/services/dataset/dataset.service'; -import { DatasetListingModel } from '@app/core/model/dataset/dataset-listing'; -import { Role } from '@app/core/common/enum/role'; -import { RecentActivityModel } from '@app/core/model/recent-activity/recent-activity.model'; -import { DashboardService } from '@app/core/services/dashboard/dashboard.service'; -import { RecentActivityCriteria } from '@app/core/query/recent-activity/recent-activity-criteria'; -import { RecentDmpModel } from '@app/core/model/recent-activity/recent-dmp-activity.model'; -import { RecentDatasetModel } from '@app/core/model/recent-activity/recent-dataset-activity.model'; -import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; -import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; -import { UntypedFormControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; import { DatasetCopyDialogueComponent } from '@app/ui/dataset/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; -import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; -import { Location } from '@angular/common'; -import { LockService } from '@app/core/services/lock/lock.service'; -import { DatasetUrlListing } from '@app/core/model/dataset/dataset-url-listing'; +import { CloneDialogComponent } from '@app/ui/dmp/clone/clone-dialog/clone-dialog.component'; import { DmpEditorModel } from '@app/ui/dmp/editor/dmp-editor.model'; +import { ExtraPropertiesFormModel } from '@app/ui/dmp/editor/general-tab/extra-properties-form.model'; +import { FunderFormModel } from '@app/ui/dmp/editor/grant-tab/funder-form-model'; import { GrantTabModel } from '@app/ui/dmp/editor/grant-tab/grant-tab-model'; import { ProjectFormModel } from '@app/ui/dmp/editor/grant-tab/project-form-model'; -import { FunderFormModel } from '@app/ui/dmp/editor/grant-tab/funder-form-model'; -import { ExtraPropertiesFormModel } from '@app/ui/dmp/editor/general-tab/extra-properties-form.model'; -import { DmpModel } from '@app/core/model/dmp/dmp'; -import { CloneDialogComponent } from '@app/ui/dmp/clone/clone-dialog/clone-dialog.component'; -import { MatomoService } from '@app/core/services/matomo/matomo-service'; -import { HttpClient } from '@angular/common/http'; +import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; -import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; -import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; -import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; -import { nameof } from 'ts-simple-nameof'; +import { BaseComponent } from '@common/base/base.component'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { debounceTime, map, takeUntil } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; @Component({ selector: 'app-recent-edited-activity', @@ -69,7 +67,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn offsetLess: number = 0; pageSize: number = 5; dmpFormGroup: UntypedFormGroup; - hasMoreActivity:boolean = true; + hasMoreActivity: boolean = true; public formGroup = new UntypedFormBuilder().group({ like: new UntypedFormControl(), order: new UntypedFormControl() @@ -96,7 +94,8 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn private location: Location, private lockService: LockService, private httpClient: HttpClient, - private matomoService: MatomoService + private matomoService: MatomoService, + private fileUtils: FileUtils ) { super(); } @@ -104,23 +103,23 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn ngOnInit() { this.matomoService.trackPageView('Recent Edited Activity'); this.route.queryParams.subscribe(params => { - if(this.isActive) { + if (this.isActive) { let page = (params['page'] === undefined) ? 1 : +params['page']; this.page = (page <= 0) ? 1 : page; - this.datasetOffset = (this.page-1)*this.pageSize; - this.dmpOffset = (this.page-1)*this.pageSize; - if(this.page > 1) { - this.offsetLess = (this.page-2)*this.pageSize; + this.datasetOffset = (this.page - 1) * this.pageSize; + this.dmpOffset = (this.page - 1) * this.pageSize; + if (this.page > 1) { + this.offsetLess = (this.page - 2) * this.pageSize; } let order = params['order']; if (this.isAuthenticated()) { - if(order === undefined || (order != this.order.MODIFIED && order != this.order.LABEL && order != this.order.STATUS)) { + if (order === undefined || (order != this.order.MODIFIED && order != this.order.LABEL && order != this.order.STATUS)) { order = this.order.MODIFIED; } } else { - if(order === undefined || (order != this.order.PUBLISHED && order != this.order.LABEL)) { + if (order === undefined || (order != this.order.PUBLISHED && order != this.order.LABEL)) { order = this.order.PUBLISHED; } } @@ -133,7 +132,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn } }); if (this.isAuthenticated()) { - if(!this.formGroup.get('order').value) { + if (!this.formGroup.get('order').value) { this.formGroup.get('order').setValue(this.order.MODIFIED); } const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; @@ -150,19 +149,19 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn this.allRecentActivities.forEach(recentActivity => { if (recentActivity.type === RecentActivityType.Dataset) { // this.datasetOffset = this.datasetOffset + 1; - this.datasetOffset = this.page*this.pageSize; + this.datasetOffset = this.page * this.pageSize; } else if (recentActivity.type === RecentActivityType.Dmp) { // this.dmpOffset = this.dmpOffset + 1; - this.dmpOffset = this.page*this.pageSize; + this.dmpOffset = this.page * this.pageSize; } }); this.totalCountRecentEdited.emit(this.allRecentActivities.length); - if(this.allRecentActivities.length == 0 && this.page > 1) { + if (this.allRecentActivities.length == 0 && this.page > 1) { let queryParams = { type: "recent", page: 1, order: this.formGroup.get("order").value }; - if(this.formGroup.get("like").value) { + if (this.formGroup.get("like").value) { queryParams['keyword'] = this.formGroup.get("like").value; } - this.router.navigate(["/home"], { queryParams: queryParams }) + this.router.navigate(["/home"], { queryParams: queryParams }) } }); this.formGroup.get('like').valueChanges @@ -177,7 +176,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn }); } else { this.publicMode = true; - if(!this.formGroup.get('order').value) { + if (!this.formGroup.get('order').value) { this.formGroup.get('order').setValue(this.order.PUBLISHED); } const allDataTableRequest = this.setPublicDataTableRequest(); @@ -189,19 +188,19 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn this.allRecentActivities.forEach(recentActivity => { if (recentActivity.type === RecentActivityType.Dataset) { // this.datasetOffset = this.datasetOffset + 1; - this.datasetOffset = this.page*this.pageSize; + this.datasetOffset = this.page * this.pageSize; } else if (recentActivity.type === RecentActivityType.Dmp) { // this.dmpOffset = this.dmpOffset + 1; - this.dmpOffset = this.page*this.pageSize; + this.dmpOffset = this.page * this.pageSize; } }); this.totalCountRecentEdited.emit(this.allRecentActivities.length); - if(this.allRecentActivities.length == 0 && this.page > 1) { + if (this.allRecentActivities.length == 0 && this.page > 1) { let queryParams = { type: "recent", page: 1, order: this.formGroup.get("order").value }; - if(this.formGroup.get("like").value) { + if (this.formGroup.get("like").value) { queryParams['keyword'] = this.formGroup.get("like").value; } - this.router.navigate(["/home"], { queryParams: queryParams }) + this.router.navigate(["/home"], { queryParams: queryParams }) } }); this.formGroup.get('like').valueChanges @@ -218,20 +217,20 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn } ngOnChanges() { - if(this.isActive) { + if (this.isActive) { this.updateUrl(); } } updateUrl() { let parameters = ""; - parameters += (this.page != 1 ? "&page="+this.page : ""); - parameters += (((this.formGroup.get("order").value != this.order.MODIFIED && !this.publicMode) || (this.formGroup.get("order").value != this.order.PUBLISHED && this.publicMode)) ? "&order="+this.formGroup.get("order").value : ""); - parameters += (this.formGroup.get("like").value ? ("&keyword="+this.formGroup.get("like").value) : ""); - if(parameters) { - parameters = "?type=recent" + parameters; - } - this.location.go(this.router.url.split('?')[0]+parameters); + parameters += (this.page != 1 ? "&page=" + this.page : ""); + parameters += (((this.formGroup.get("order").value != this.order.MODIFIED && !this.publicMode) || (this.formGroup.get("order").value != this.order.PUBLISHED && this.publicMode)) ? "&order=" + this.formGroup.get("order").value : ""); + parameters += (this.formGroup.get("like").value ? ("&keyword=" + this.formGroup.get("like").value) : ""); + if (parameters) { + parameters = "?type=recent" + parameters; + } + this.location.go(this.router.url.split('?')[0] + parameters); } getDatasets(activity: RecentDmpModel): DatasetUrlListing[] { @@ -348,10 +347,10 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.maxMultiplicity)].join('.'), ] ) - .pipe(map(data => data as DmpBlueprint), takeUntil(this._destroyed)) - .subscribe( - data => successFunction(data), - ); + .pipe(map(data => data as DmpBlueprint), takeUntil(this._destroyed)) + .subscribe( + data => successFunction(data), + ); } @@ -596,7 +595,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "xml", id); @@ -608,7 +607,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "docx", id); @@ -620,7 +619,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "pdf", id); @@ -632,7 +631,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/json' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "json", id); }, async error => { @@ -651,7 +650,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "pdf", dataset.id); @@ -663,7 +662,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "docx", dataset.id); @@ -676,31 +675,13 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "xml", dataset.id); }); } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - // newVersion(id: String, label: String) { // let url = this.router.createUrlTree(['/plans/new_version/', id, { dmpLabel: label }]); // window.open(url.toString(), '_blank'); @@ -809,14 +790,14 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn this.allRecentActivities.forEach(recentActivity => { if (recentActivity.type === RecentActivityType.Dataset) { // this.datasetOffset = this.datasetOffset + 1; - this.datasetOffset = this.page*this.pageSize; + this.datasetOffset = this.page * this.pageSize; } else if (recentActivity.type === RecentActivityType.Dmp) { // this.dmpOffset = this.dmpOffset + 1; - this.dmpOffset = this.page*this.pageSize; + this.dmpOffset = this.page * this.pageSize; } }); - if(response.length< this.pageSize) { + if (response.length < this.pageSize) { this.hasMoreActivity = false; } else { this.hasMoreActivity = true; @@ -829,10 +810,10 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; // const fields: Array = ["-modified"]; let request; - if(more) { - request = new DataTableMultiTypeRequest(this.dmpOffset, this.datasetOffset, this.pageSize, {fields: fields}); + if (more) { + request = new DataTableMultiTypeRequest(this.dmpOffset, this.datasetOffset, this.pageSize, { fields: fields }); } else { - request = new DataTableMultiTypeRequest(this.offsetLess, this.offsetLess, this.pageSize, {fields: fields}); + request = new DataTableMultiTypeRequest(this.offsetLess, this.offsetLess, this.pageSize, { fields: fields }); } request.criteria = new RecentActivityCriteria(); request.criteria.like = this.formGroup.get("like").value ? this.formGroup.get("like").value : ""; @@ -860,7 +841,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn this.offsetLess = (this.page - 2) * this.pageSize; } - if(result.length < this.pageSize) { + if (result.length < this.pageSize) { this.hasMoreActivity = false; } else { this.hasMoreActivity = true; diff --git a/dmp-frontend/src/app/ui/dashboard/recent-edited-dataset-activity/recent-edited-dataset-activity.component.ts b/dmp-frontend/src/app/ui/dashboard/recent-edited-dataset-activity/recent-edited-dataset-activity.component.ts index d509a03f4..1a12aee40 100644 --- a/dmp-frontend/src/app/ui/dashboard/recent-edited-dataset-activity/recent-edited-dataset-activity.component.ts +++ b/dmp-frontend/src/app/ui/dashboard/recent-edited-dataset-activity/recent-edited-dataset-activity.component.ts @@ -1,31 +1,32 @@ -import {Component, OnInit, Output, EventEmitter, Input, ViewChild} from '@angular/core'; -import { DatasetListingModel } from '@app/core/model/dataset/dataset-listing'; -import { DatasetService } from '@app/core/services/dataset/dataset.service'; -import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; -import { DatasetCriteria } from '@app/core/query/dataset/dataset-criteria'; -import { AuthService } from '@app/core/services/auth/auth.service'; -import { BaseComponent } from '@common/base/base.component'; -import { TranslateService } from '@ngx-translate/core'; -import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; -import { UntypedFormControl, UntypedFormBuilder } from '@angular/forms'; -import { DatasetCopyDialogueComponent } from '@app/ui/dataset/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; +import { Location } from '@angular/common'; +import { HttpClient } from '@angular/common/http'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormControl } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; -import { debounceTime, takeUntil } from 'rxjs/operators'; -import {ActivatedRoute, Router} from '@angular/router'; -import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; -import * as FileSaver from 'file-saver'; -import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; -import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; -import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { ActivatedRoute, Router } from '@angular/router'; import { DatasetStatus } from '@app/core/common/enum/dataset-status'; -import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; import { Role } from '@app/core/common/enum/role'; -import { Location } from '@angular/common'; +import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; +import { DatasetListingModel } from '@app/core/model/dataset/dataset-listing'; +import { DatasetCriteria } from '@app/core/query/dataset/dataset-criteria'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { ConfigurationService } from "@app/core/services/configuration/configuration.service"; +import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; +import { DatasetService } from '@app/core/services/dataset/dataset.service'; import { LockService } from '@app/core/services/lock/lock.service'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; -import { HttpClient } from '@angular/common/http'; -import {ConfigurationService} from "@app/core/services/configuration/configuration.service"; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; +import { DatasetCopyDialogueComponent } from '@app/ui/dataset/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; +import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; +import { BaseComponent } from '@common/base/base.component'; +import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { debounceTime, takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-recent-edited-dataset-activity', @@ -42,7 +43,7 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen totalCount: number; startIndex: number = 0; offsetLess: number = 0; - hasMoreResults:boolean = true; + hasMoreResults: boolean = true; pageSize: number = 5; public formGroup = new UntypedFormBuilder().group({ like: new UntypedFormControl(), @@ -69,7 +70,8 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen private lockService: LockService, private httpClient: HttpClient, private matomoService: MatomoService, - private configurationService: ConfigurationService + private configurationService: ConfigurationService, + private fileUtils: FileUtils ) { super(); } @@ -77,22 +79,22 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen ngOnInit() { this.matomoService.trackPageView('Recent Dataset Activity'); this.route.queryParams.subscribe(params => { - if(this.isActive) { + if (this.isActive) { let page = (params['page'] === undefined) ? 1 : +params['page']; this.page = (page <= 0) ? 1 : page; - this.startIndex = (this.page-1)*this.pageSize; - if(this.page > 1) { - this.offsetLess = (this.page-2)*this.pageSize; + this.startIndex = (this.page - 1) * this.pageSize; + if (this.page > 1) { + this.offsetLess = (this.page - 2) * this.pageSize; } let order = params['order']; if (this.isAuthenticated()) { - if(order === undefined || (order != this.order.MODIFIED && order != this.order.LABEL && order != this.order.STATUS)) { + if (order === undefined || (order != this.order.MODIFIED && order != this.order.LABEL && order != this.order.STATUS)) { order = this.order.MODIFIED; } } else { - if(order === undefined || (order != this.order.DATASETPUBLISHED && order != this.order.LABEL)) { + if (order === undefined || (order != this.order.DATASETPUBLISHED && order != this.order.LABEL)) { order = this.order.DATASETPUBLISHED; } } @@ -106,7 +108,7 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen }); if (this.isAuthenticated()) { // const fields: Array = ["-modified"]; - if(!this.formGroup.get('order').value) { + if (!this.formGroup.get('order').value) { this.formGroup.get('order').setValue(this.order.MODIFIED); } const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; @@ -120,15 +122,15 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen this.datasetActivities = response.data; this.totalCount = response.totalCount; this.totalCountDatasets.emit(this.datasetActivities.length) - if(this.totalCount > 0 && this.totalCount <= (this.page-1)*this.pageSize && this.page > 1) { + if (this.totalCount > 0 && this.totalCount <= (this.page - 1) * this.pageSize && this.page > 1) { let queryParams = { type: "datasets", page: 1, order: this.formGroup.get("order").value }; - if(this.formGroup.get("like").value) { + if (this.formGroup.get("like").value) { queryParams['keyword'] = this.formGroup.get("like").value; } - this.router.navigate(["/home"], { queryParams: queryParams }) + this.router.navigate(["/home"], { queryParams: queryParams }) } }); - this.formGroup.get('like').valueChanges + this.formGroup.get('like').valueChanges .pipe(takeUntil(this._destroyed), debounceTime(500)) .subscribe(x => this.refresh()); this.formGroup.get('order').valueChanges @@ -136,7 +138,7 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen .subscribe(x => this.refresh()); } else { this.publicMode = true; - if(!this.formGroup.get('order').value) { + if (!this.formGroup.get('order').value) { this.formGroup.get('order').setValue(this.order.DATASETPUBLISHED); } const dataTableRequest = this.setPublicDataTableRequest(); @@ -144,12 +146,12 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen this.datasetActivities = response.data; this.totalCount = response.totalCount; this.totalCountDatasets.emit(this.datasetActivities.length); - if(this.totalCount > 0 && this.totalCount <= (this.page-1)*this.pageSize && this.page > 1) { + if (this.totalCount > 0 && this.totalCount <= (this.page - 1) * this.pageSize && this.page > 1) { let queryParams = { type: "datasets", page: 1, order: this.formGroup.get("order").value }; - if(this.formGroup.get("like").value) { + if (this.formGroup.get("like").value) { queryParams['keyword'] = this.formGroup.get("like").value; } - this.router.navigate(["/home"], { queryParams: queryParams }) + this.router.navigate(["/home"], { queryParams: queryParams }) } }); this.formGroup.get('like').valueChanges @@ -162,17 +164,17 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen } ngOnChanges() { - if(this.isActive) { + if (this.isActive) { this.updateUrl(); } } updateUrl() { - let parameters = "?type=datasets"+ - (this.page != 1 ? "&page="+this.page : "") + - (((this.formGroup.get("order").value != this.order.MODIFIED && !this.publicMode) || (this.formGroup.get("order").value != this.order.DATASETPUBLISHED && this.publicMode)) ? "&order="+this.formGroup.get("order").value : "") + - (this.formGroup.get("like").value ? ("&keyword="+this.formGroup.get("like").value) : ""); - this.location.go(this.router.url.split('?')[0]+parameters); + let parameters = "?type=datasets" + + (this.page != 1 ? "&page=" + this.page : "") + + (((this.formGroup.get("order").value != this.order.MODIFIED && !this.publicMode) || (this.formGroup.get("order").value != this.order.DATASETPUBLISHED && this.publicMode)) ? "&order=" + this.formGroup.get("order").value : "") + + (this.formGroup.get("like").value ? ("&keyword=" + this.formGroup.get("like").value) : ""); + this.location.go(this.router.url.split('?')[0] + parameters); } setPublicDataTableRequest(fields?: Array, more: boolean = true): DataTableRequest { @@ -207,7 +209,7 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen this.datasetActivities = response.data; this.totalCount = response.totalCount; this.totalCountDatasets.emit(this.datasetActivities.length); - if(response.data.length< this.pageSize) { + if (response.data.length < this.pageSize) { this.hasMoreResults = false; } else { this.hasMoreResults = true; @@ -220,12 +222,12 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; let request; - this.startIndex = (this.page)*this.pageSize; - if(this.page > 1) { - this.offsetLess = (this.page-2)*this.pageSize; + this.startIndex = (this.page) * this.pageSize; + if (this.page > 1) { + this.offsetLess = (this.page - 2) * this.pageSize; } - if(this.isAuthenticated()) { - if(more) { + if (this.isAuthenticated()) { + if (more) { request = new DataTableRequest(this.startIndex, this.pageSize, { fields: fields }); } else { request = new DataTableRequest(this.offsetLess, this.pageSize, { fields: fields }); @@ -249,7 +251,7 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen // this.datasetActivities = this.datasetActivities.length > 0 ? this.mergeTwoSortedLists(this.datasetActivities, result.data, this.formGroup.get('order').value) : result.data; this.datasetActivities = result.data; - if(result.data.length < this.pageSize) { + if (result.data.length < this.pageSize) { this.hasMoreResults = false; } else { this.hasMoreResults = true; @@ -384,30 +386,12 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen }); } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - downloadPDF(dataset: DatasetListingModel): void { this.datasetWizardService.downloadPDF(dataset.id as string) .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "pdf", dataset.id); @@ -419,7 +403,7 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "docx", dataset.id); @@ -432,7 +416,7 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "xml", dataset.id); diff --git a/dmp-frontend/src/app/ui/dashboard/recent-edited-dmp-activity/recent-edited-dmp-activity.component.ts b/dmp-frontend/src/app/ui/dashboard/recent-edited-dmp-activity/recent-edited-dmp-activity.component.ts index 4d62b678f..d8be29cb0 100644 --- a/dmp-frontend/src/app/ui/dashboard/recent-edited-dmp-activity/recent-edited-dmp-activity.component.ts +++ b/dmp-frontend/src/app/ui/dashboard/recent-edited-dmp-activity/recent-edited-dmp-activity.component.ts @@ -1,43 +1,43 @@ -import {Component, OnInit, Output, EventEmitter, Input, ViewChild} from '@angular/core'; +import { Location } from '@angular/common'; +import { HttpClient } from '@angular/common/http'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; -import {ActivatedRoute, Router} from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; +import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; +import { DmpStatus } from '@app/core/common/enum/dmp-status'; +import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; import { RecentActivityType } from '@app/core/common/enum/recent-activity-type'; +import { Role } from '@app/core/common/enum/role'; import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; +import { DatasetListingModel } from '@app/core/model/dataset/dataset-listing'; +import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; +import { DmpModel } from '@app/core/model/dmp/dmp'; import { DmpListingModel } from '@app/core/model/dmp/dmp-listing'; import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria'; import { AuthService } from '@app/core/services/auth/auth.service'; +import { DatasetService } from '@app/core/services/dataset/dataset.service'; +import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; import { DmpService } from '@app/core/services/dmp/dmp.service'; +import { LockService } from '@app/core/services/lock/lock.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; -import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; -import { BaseComponent } from '@common/base/base.component'; -import { TranslateService } from '@ngx-translate/core'; -import * as FileSaver from 'file-saver'; -import { takeUntil, map, debounceTime } from 'rxjs/operators'; -import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; -import { DmpStatus } from '@app/core/common/enum/dmp-status'; -import { DatasetService } from '@app/core/services/dataset/dataset.service'; -import { DatasetListingModel } from '@app/core/model/dataset/dataset-listing'; -import { Role } from '@app/core/common/enum/role'; -import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; -import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; -import { Location } from '@angular/common'; -import { LockService } from '@app/core/services/lock/lock.service'; -import { ExploreDmpCriteriaModel } from '@app/core/query/explore-dmp/explore-dmp-criteria'; -import { DmpModel } from '@app/core/model/dmp/dmp'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; import { CloneDialogComponent } from '@app/ui/dmp/clone/clone-dialog/clone-dialog.component'; import { DmpEditorModel } from '@app/ui/dmp/editor/dmp-editor.model'; -import { GrantTabModel } from '@app/ui/dmp/editor/grant-tab/grant-tab-model'; import { ExtraPropertiesFormModel } from '@app/ui/dmp/editor/general-tab/extra-properties-form.model'; import { FunderFormModel } from '@app/ui/dmp/editor/grant-tab/funder-form-model'; +import { GrantTabModel } from '@app/ui/dmp/editor/grant-tab/grant-tab-model'; import { ProjectFormModel } from '@app/ui/dmp/editor/grant-tab/project-form-model'; -import { MatomoService } from '@app/core/services/matomo/matomo-service'; -import { HttpClient } from '@angular/common/http'; +import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; -import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; -import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; -import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; +import { BaseComponent } from '@common/base/base.component'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { debounceTime, map, takeUntil } from 'rxjs/operators'; import { nameof } from 'ts-simple-nameof'; @Component({ @@ -60,7 +60,7 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O totalCount: number; startIndex: number = 0; offsetLess: number = 0; - hasMoreResults:boolean = true; + hasMoreResults: boolean = true; pageSize: number = 5; dmpFormGroup: UntypedFormGroup; public formGroup = new UntypedFormBuilder().group({ @@ -88,7 +88,8 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O private location: Location, private lockService: LockService, private httpClient: HttpClient, - private matomoService: MatomoService + private matomoService: MatomoService, + private fileUtils: FileUtils ) { super(); } @@ -96,22 +97,22 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O ngOnInit() { this.matomoService.trackPageView('Recent DMP Activity'); this.route.queryParams.subscribe(params => { - if(this.isActive) { + if (this.isActive) { let page = (params['page'] === undefined) ? 1 : +params['page']; this.page = (page <= 0) ? 1 : page; - this.startIndex = (this.page-1)*this.pageSize; - if(this.page > 1) { - this.offsetLess = (this.page-2)*this.pageSize; + this.startIndex = (this.page - 1) * this.pageSize; + if (this.page > 1) { + this.offsetLess = (this.page - 2) * this.pageSize; } let order = params['order']; if (this.isAuthenticated()) { - if(order === undefined || (order != this.order.MODIFIED && order != this.order.LABEL && order != this.order.STATUS)) { + if (order === undefined || (order != this.order.MODIFIED && order != this.order.LABEL && order != this.order.STATUS)) { order = this.order.MODIFIED; } } else { - if(order === undefined || (order != this.order.PUBLISHED && order != this.order.LABEL)) { + if (order === undefined || (order != this.order.PUBLISHED && order != this.order.LABEL)) { order = this.order.PUBLISHED; } } @@ -125,7 +126,7 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O }); if (this.isAuthenticated()) { // const fields: Array = ["-modified"]; - if(!this.formGroup.get('order').value) { + if (!this.formGroup.get('order').value) { this.formGroup.get('order').setValue(this.order.MODIFIED); } const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; @@ -139,12 +140,12 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O this.dmpActivities = response.data; this.totalCount = response.totalCount; this.totalCountDmps.emit(this.dmpActivities.length); - if(this.totalCount > 0 && this.totalCount <= (this.page-1)*this.pageSize && this.page > 1) { + if (this.totalCount > 0 && this.totalCount <= (this.page - 1) * this.pageSize && this.page > 1) { let queryParams = { type: "dmps", page: 1, order: this.formGroup.get("order").value }; - if(this.formGroup.get("like").value) { + if (this.formGroup.get("like").value) { queryParams['keyword'] = this.formGroup.get("like").value; } - this.router.navigate(["/home"], { queryParams: queryParams }) + this.router.navigate(["/home"], { queryParams: queryParams }) } // this.totalCount < this.pageSize ? this.totalCountDmps.emit(response.totalCount) : this.totalCountDmps.emit(this.pageSize); // this.totalCountDmps.emit(this.totalCount); @@ -179,7 +180,7 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O // }); } else { this.publicMode = true; - if(!this.formGroup.get('order').value) { + if (!this.formGroup.get('order').value) { this.formGroup.get('order').setValue(this.order.PUBLISHED); } const dataTableRequest = this.setPublicDataTableRequest(); @@ -188,12 +189,12 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O this.dmpActivities = response.data; this.totalCount = response.totalCount; this.totalCountDmps.emit(this.dmpActivities.length); - if(this.totalCount > 0 && this.totalCount <= (this.page-1)*this.pageSize && this.page > 1) { + if (this.totalCount > 0 && this.totalCount <= (this.page - 1) * this.pageSize && this.page > 1) { let queryParams = { type: "dmps", page: 1, order: this.formGroup.get("order").value }; - if(this.formGroup.get("like").value) { + if (this.formGroup.get("like").value) { queryParams['keyword'] = this.formGroup.get("like").value; } - this.router.navigate(["/home"], { queryParams: queryParams }) + this.router.navigate(["/home"], { queryParams: queryParams }) } }); this.formGroup.get('like').valueChanges @@ -206,17 +207,17 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O } ngOnChanges() { - if(this.isActive) { + if (this.isActive) { this.updateUrl(); } } updateUrl() { - let parameters = "?type=dmps"+ - (this.page != 1 ? "&page="+this.page : "") + - (((this.formGroup.get("order").value != this.order.MODIFIED && !this.publicMode) || (this.formGroup.get("order").value != this.order.PUBLISHED && this.publicMode)) ? "&order="+this.formGroup.get("order").value : "") + - (this.formGroup.get("like").value ? ("&keyword="+this.formGroup.get("like").value) : ""); - this.location.go(this.router.url.split('?')[0]+parameters); + let parameters = "?type=dmps" + + (this.page != 1 ? "&page=" + this.page : "") + + (((this.formGroup.get("order").value != this.order.MODIFIED && !this.publicMode) || (this.formGroup.get("order").value != this.order.PUBLISHED && this.publicMode)) ? "&order=" + this.formGroup.get("order").value : "") + + (this.formGroup.get("like").value ? ("&keyword=" + this.formGroup.get("like").value) : ""); + this.location.go(this.router.url.split('?')[0] + parameters); } public isAuthenticated(): boolean { @@ -299,10 +300,10 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.maxMultiplicity)].join('.'), ] ) - .pipe(map(data => data as DmpBlueprint), takeUntil(this._destroyed)) - .subscribe( - data => successFunction(data), - ); + .pipe(map(data => data as DmpBlueprint), takeUntil(this._destroyed)) + .subscribe( + data => successFunction(data), + ); } private checkForGrant(blueprint: DmpBlueprintDefinition) { @@ -522,7 +523,7 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "xml", id); @@ -534,7 +535,7 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "docx", id); @@ -546,7 +547,7 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "pdf", id); @@ -558,7 +559,7 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/json' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "json", id); }, async error => { @@ -572,24 +573,6 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O this.uiNotificationService.snackBarNotification(errorObj.message, SnackBarNotificationLevel.Error); } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - // newVersion(id: String, label: String) { // let url = this.router.createUrlTree(['/plans/new_version/', id, { dmpLabel: label }]); // window.open(url.toString(), '_blank'); @@ -624,7 +607,7 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O this.dmpActivities = response.data; this.totalCount = response.totalCount; this.totalCountDmps.emit(this.dmpActivities.length); - if(response.data.length< this.pageSize) { + if (response.data.length < this.pageSize) { this.hasMoreResults = false; } else { this.hasMoreResults = true; @@ -637,15 +620,15 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; let request; - this.startIndex = (this.page)*this.pageSize; - if(this.page > 1) { - this.offsetLess = (this.page-2)*this.pageSize; + this.startIndex = (this.page) * this.pageSize; + if (this.page > 1) { + this.offsetLess = (this.page - 2) * this.pageSize; } - if(this.isAuthenticated()) { - if(more) { - request = new DataTableRequest(this.startIndex, this.pageSize, {fields: fields}) + if (this.isAuthenticated()) { + if (more) { + request = new DataTableRequest(this.startIndex, this.pageSize, { fields: fields }) } else { - request = new DataTableRequest(this.offsetLess, this.pageSize, {fields: fields}) + request = new DataTableRequest(this.offsetLess, this.pageSize, { fields: fields }) } } else { request = this.setPublicDataTableRequest(fields, more); @@ -666,7 +649,7 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O // this.dmpActivities = this.dmpActivities.length > 0 ? this.mergeTwoSortedLists(this.dmpActivities, result.data, this.formGroup.get('order').value) : result.data; this.dmpActivities = result.data; - if(result.data.length < this.pageSize) { + if (result.data.length < this.pageSize) { this.hasMoreResults = false; } else { this.hasMoreResults = true; diff --git a/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.ts b/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.ts index e9f3c2e1a..09399b259 100644 --- a/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.ts +++ b/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.ts @@ -32,6 +32,7 @@ import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; import { CheckDeactivateBaseComponent } from '@app/library/deactivate/deactivate.component'; import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; @@ -68,7 +69,7 @@ import { catchError, debounceTime, filter, map, takeUntil } from 'rxjs/operators templateUrl: 'dataset-wizard.component.html', styleUrls: ['./dataset-wizard.component.scss'] }) -export class DatasetWizardComponent extends CheckDeactivateBaseComponent implements OnInit {//IBreadCrumbComponent +export class DatasetWizardComponent extends CheckDeactivateBaseComponent implements OnInit {//IBreadCrumbComponent canDeactivate(): boolean { return !this.isDirty(); } @@ -141,7 +142,8 @@ export class DatasetWizardComponent extends CheckDeactivateBaseComponent impleme private authService: AuthService, private configurationService: ConfigurationService, private httpClient: HttpClient, - private matomoService: MatomoService + private matomoService: MatomoService, + private fileUtils: FileUtils ) { super(); } @@ -1026,7 +1028,7 @@ export class DatasetWizardComponent extends CheckDeactivateBaseComponent impleme .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "pdf", id); @@ -1038,7 +1040,7 @@ export class DatasetWizardComponent extends CheckDeactivateBaseComponent impleme .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "docx", id); @@ -1051,7 +1053,7 @@ export class DatasetWizardComponent extends CheckDeactivateBaseComponent impleme .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "xml", id); @@ -1079,24 +1081,6 @@ export class DatasetWizardComponent extends CheckDeactivateBaseComponent impleme // }); // } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - public redirectToGrant() { this.router.navigate(['grants/edit/' + this.datasetWizardModel.dmp.grant.id]); } diff --git a/dmp-frontend/src/app/ui/dataset/listing/listing-item/dataset-listing-item.component.ts b/dmp-frontend/src/app/ui/dataset/listing/listing-item/dataset-listing-item.component.ts index 7c25ac8bf..da184bdb4 100644 --- a/dmp-frontend/src/app/ui/dataset/listing/listing-item/dataset-listing-item.component.ts +++ b/dmp-frontend/src/app/ui/dataset/listing/listing-item/dataset-listing-item.component.ts @@ -1,26 +1,26 @@ -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; -import { DatasetListingModel } from '../../../../core/model/dataset/dataset-listing'; -import { Router } from '@angular/router'; -import { DatasetStatus } from '../../../../core/common/enum/dataset-status'; -import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; -import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; -import { BaseComponent } from '@common/base/base.component'; -import { takeUntil } from 'rxjs/operators'; -import * as FileSaver from 'file-saver'; -import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; -import { MatDialog } from '@angular/material/dialog'; -import { DatasetCopyDialogueComponent } from '../../dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; -import { UntypedFormControl } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; -import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; -import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service'; -import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; -import { AuthService } from '@app/core/services/auth/auth.service'; -import { LockService } from '@app/core/services/lock/lock.service'; -import { Role } from '@app/core/common/enum/role'; import { Location } from '@angular/common'; -import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { HttpClient } from '@angular/common/http'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { UntypedFormControl } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { Router } from '@angular/router'; +import { Role } from '@app/core/common/enum/role'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; +import { LockService } from '@app/core/services/lock/lock.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; +import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; +import { BaseComponent } from '@common/base/base.component'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { takeUntil } from 'rxjs/operators'; +import { DatasetStatus } from '../../../../core/common/enum/dataset-status'; +import { DatasetListingModel } from '../../../../core/model/dataset/dataset-listing'; +import { DatasetCopyDialogueComponent } from '../../dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; @Component({ selector: 'app-dataset-listing-item-component', @@ -49,7 +49,8 @@ export class DatasetListingItemComponent extends BaseComponent implements OnInit private lockService: LockService, private location: Location, private httpClient: HttpClient, - private matomoService: MatomoService + private matomoService: MatomoService, + private fileUtils: FileUtils ) { super(); } @@ -95,7 +96,7 @@ export class DatasetListingItemComponent extends BaseComponent implements OnInit .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "pdf", dataset.id); @@ -107,7 +108,7 @@ export class DatasetListingItemComponent extends BaseComponent implements OnInit .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "docx", dataset.id); @@ -120,31 +121,13 @@ export class DatasetListingItemComponent extends BaseComponent implements OnInit .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "xml", dataset.id); }); } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - openShareDialog(dmpRowId: any, dmpRowName: any) { const dialogRef = this.dialog.open(DmpInvitationDialogComponent, { // height: '250px', diff --git a/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.ts b/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.ts index db028f03d..1e4de03b2 100644 --- a/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.ts +++ b/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.ts @@ -1,38 +1,37 @@ import { Component, OnInit } from '@angular/core'; -import { BaseComponent } from '@common/base/base.component'; import { DatasetOverviewModel } from '@app/core/model/dataset/dataset-overview'; +import { BaseComponent } from '@common/base/base.component'; // import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; -import { Observable, of as observableOf } from 'rxjs'; -import { ActivatedRoute, Router, Params } from '@angular/router'; -import { DatasetService } from '@app/core/services/dataset/dataset.service'; -import { TranslateService } from '@ngx-translate/core'; -import { AuthService } from '@app/core/services/auth/auth.service'; -import { MatDialog } from '@angular/material/dialog'; -import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; -import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; -import { Oauth2DialogService } from '@app/ui/misc/oauth2-dialog/service/oauth2-dialog.service'; -import { UserService } from '@app/core/services/user/user.service'; -import { filter, takeUntil } from 'rxjs/operators'; -import { Role } from '@app/core/common/enum/role'; import { Location } from '@angular/common'; -import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; -import { DatasetStatus } from '@app/core/common/enum/dataset-status'; -import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; -import * as FileSaver from 'file-saver'; -import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; -import { DatasetWizardEditorModel } from '../dataset-wizard/dataset-wizard-editor.model'; -import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; -import { UntypedFormControl } from '@angular/forms'; -import { DatasetCopyDialogueComponent } from '../dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; -import { DmpService } from '@app/core/services/dmp/dmp.service'; -import { ResearcherModel } from '@app/core/model/researcher/researcher'; -import { LockService } from '@app/core/services/lock/lock.service'; -import { DatasetWizardModel } from '@app/core/model/dataset/dataset-wizard'; -import { DmpStatus } from '@app/core/common/enum/dmp-status'; -import { DmpOverviewModel } from '@app/core/model/dmp/dmp-overview'; -import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { HttpClient } from '@angular/common/http'; +import { UntypedFormControl } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Params, Router } from '@angular/router'; +import { DatasetStatus } from '@app/core/common/enum/dataset-status'; +import { DmpStatus } from '@app/core/common/enum/dmp-status'; +import { Role } from '@app/core/common/enum/role'; +import { DatasetWizardModel } from '@app/core/model/dataset/dataset-wizard'; +import { DmpOverviewModel } from '@app/core/model/dmp/dmp-overview'; +import { ResearcherModel } from '@app/core/model/researcher/researcher'; +import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; +import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; +import { DatasetService } from '@app/core/services/dataset/dataset.service'; +import { DmpService } from '@app/core/services/dmp/dmp.service'; +import { LockService } from '@app/core/services/lock/lock.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { UserService } from '@app/core/services/user/user.service'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; +import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; +import { Oauth2DialogService } from '@app/ui/misc/oauth2-dialog/service/oauth2-dialog.service'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { filter, takeUntil } from 'rxjs/operators'; +import { DatasetCopyDialogueComponent } from '../dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; @Component({ @@ -73,7 +72,8 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { private datasetWizardService: DatasetWizardService, private lockService: LockService, private httpClient: HttpClient, - private matomoService: MatomoService + private matomoService: MatomoService, + private fileUtils: FileUtils ) { super(); } @@ -140,11 +140,13 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { this.lockService.checkLockStatus(id).pipe(takeUntil(this._destroyed)) .subscribe(lockStatus => { this.lockStatus = lockStatus - if(lockStatus){ - this.dialog.open(PopupNotificationDialogComponent,{data:{ - title:this.language.instant('DATASET-OVERVIEW.LOCKED.TITLE'), - message:this.language.instant('DATASET-OVERVIEW.LOCKED.MESSAGE') - }, maxWidth:'30em'}); + if (lockStatus) { + this.dialog.open(PopupNotificationDialogComponent, { + data: { + title: this.language.instant('DATASET-OVERVIEW.LOCKED.TITLE'), + message: this.language.instant('DATASET-OVERVIEW.LOCKED.MESSAGE') + }, maxWidth: '30em' + }); } }); } @@ -308,9 +310,9 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { } onUpdateCallbackError(error) { - this.uiNotificationService.snackBarNotification(error.error.message ? this.tryTranslate( error.error.message) : this.language.instant('DATASET-UPLOAD.SNACK-BAR.UNSUCCESSFUL'), SnackBarNotificationLevel.Error); + this.uiNotificationService.snackBarNotification(error.error.message ? this.tryTranslate(error.error.message) : this.language.instant('DATASET-UPLOAD.SNACK-BAR.UNSUCCESSFUL'), SnackBarNotificationLevel.Error); } - tryTranslate(errorMessage: string): string{ + tryTranslate(errorMessage: string): string { return errorMessage.replace('Field value of', this.language.instant('Field value of')) .replace('must be filled', this.language.instant('must be filled')); } @@ -335,7 +337,7 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "pdf", id); @@ -347,7 +349,7 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "docx", id); @@ -359,7 +361,7 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('datasets', "xml", id); @@ -372,29 +374,11 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { // .pipe(takeUntil(this._destroyed)) // .subscribe(response => { // const blob = new Blob([response.body], { type: 'application/json' }); - // const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + // const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); // FileSaver.saveAs(blob, filename); // }) // } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - openDmpSearchDialogue() { const formControl = new UntypedFormControl(); const dialogRef = this.dialog.open(DatasetCopyDialogueComponent, { @@ -490,14 +474,14 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { }, maxWidth: '30em' }) - .afterClosed() - .pipe( - filter(x => x), - takeUntil(this._destroyed) - ) - .subscribe( _ =>{ - this.router.navigate(['datasets','edit',dataset.id, 'finalize']); - }) + .afterClosed() + .pipe( + filter(x => x), + takeUntil(this._destroyed) + ) + .subscribe(_ => { + this.router.navigate(['datasets', 'edit', dataset.id, 'finalize']); + }) @@ -547,17 +531,17 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) { this.datasetWizardService.getSingle(dataset.id) - .pipe(takeUntil(this._destroyed)) - .subscribe(data => { - this.datasetWizardModel = data; - this.datasetWizardModel.status = DatasetStatus.Draft; - this.datasetWizardService.createDataset(this.datasetWizardModel) .pipe(takeUntil(this._destroyed)) - .subscribe( - data => this.onUpdateCallbackSuccess(), - error => this.onUpdateCallbackError(error) - ); - }); + .subscribe(data => { + this.datasetWizardModel = data; + this.datasetWizardModel.status = DatasetStatus.Draft; + this.datasetWizardService.createDataset(this.datasetWizardModel) + .pipe(takeUntil(this._destroyed)) + .subscribe( + data => this.onUpdateCallbackSuccess(), + error => this.onUpdateCallbackError(error) + ); + }); } }); } diff --git a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor-blueprint.component.ts b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor-blueprint.component.ts index e9be2d83a..761e92394 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor-blueprint.component.ts +++ b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor-blueprint.component.ts @@ -2,11 +2,13 @@ import { Component, OnInit } from '@angular/core'; import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Params, Router } from '@angular/router'; -import { DmpBlueprintStatus } from '@app/core/common/enum/dmp-blueprint-status'; +import { DmpBlueprintExtraFieldDataType } from '@app/core/common/enum/dmp-blueprint-extra-field-data-type'; +import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; import { DmpStatus } from '@app/core/common/enum/dmp-status'; import { Role } from '@app/core/common/enum/role'; import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; +import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; import { DmpModel } from '@app/core/model/dmp/dmp'; import { DmpDatasetProfile } from '@app/core/model/dmp/dmp-dataset-profile/dmp-dataset-profile'; import { DmpDatasetProfileSectionsFormModel } from '@app/core/model/dmp/dmp-dataset-profile/dmp-dataset-profile-sections-form.model'; @@ -16,7 +18,6 @@ import { LockModel } from '@app/core/model/lock/lock.model'; import { UserModel } from '@app/core/model/user/user'; import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; import { DatasetProfileCriteria } from '@app/core/query/dataset-profile/dataset-profile-criteria'; -import { DmpBlueprintCriteria } from '@app/core/query/dmp/dmp-blueprint-criteria'; import { LicenseCriteria } from '@app/core/query/license/license-criteria'; import { RequestItem } from '@app/core/query/request-item'; import { AuthService } from '@app/core/services/auth/auth.service'; @@ -44,6 +45,7 @@ import { Guid } from '@common/types/guid'; import { TranslateService } from '@ngx-translate/core'; import { Observable, interval } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; import { DatasetPreviewDialogComponent } from '../dataset-preview/dataset-preview-dialog.component'; import { DmpToDatasetDialogComponent } from '../dmp-to-dataset/dmp-to-dataset-dialog.component'; import { AddOrganizationComponent } from '../editor/add-organization/add-organization.component'; @@ -54,10 +56,6 @@ import { ExtraPropertiesFormModel } from '../editor/general-tab/extra-properties import { FunderFormModel } from '../editor/grant-tab/funder-form-model'; import { GrantTabModel } from '../editor/grant-tab/grant-tab-model'; import { ProjectFormModel } from '../editor/grant-tab/project-form-model'; -import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; -import { DmpBlueprintExtraFieldDataType } from '@app/core/common/enum/dmp-blueprint-extra-field-data-type'; -import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; -import { nameof } from 'ts-simple-nameof'; interface Visible { value: boolean; diff --git a/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.component.ts b/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.component.ts index cbb4c5c1f..bde80aa87 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.component.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.component.ts @@ -5,13 +5,11 @@ import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { DmpStatus } from '@app/core/common/enum/dmp-status'; import { Role } from "@app/core/common/enum/role"; -import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; import { DmpModel } from '@app/core/model/dmp/dmp'; import { LockModel } from '@app/core/model/lock/lock.model'; import { UserModel } from '@app/core/model/user/user'; import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; import { BaseCriteria } from '@app/core/query/base-criteria'; -import { DmpBlueprintCriteria } from '@app/core/query/dmp/dmp-blueprint-criteria'; import { GrantCriteria } from '@app/core/query/grant/grant-criteria'; import { RequestItem } from '@app/core/query/request-item'; import { AuthService } from '@app/core/services/auth/auth.service'; @@ -34,6 +32,8 @@ import { ProjectFormModel } from '@app/ui/dmp/editor/grant-tab/project-form-mode import { GrantEditorModel } from '@app/ui/grant/editor/grant-editor.model'; // import { IBreadCrumbComponent } from '@app/ui/misc/breadcrumb/definition/IBreadCrumbComponent'; // import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; +import { DmpBlueprint, DmpBlueprintDefinition } from '@app/core/model/dmp-blueprint/dmp-blueprint'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; import { FormService } from '@common/forms/form-service'; import { FormValidationErrorsDialogComponent } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component'; import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; @@ -41,11 +41,10 @@ import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog import { Guid } from '@common/types/guid'; import { TranslateService } from '@ngx-translate/core'; import * as FileSaver from 'file-saver'; -import { Observable, interval, of as observableOf } from 'rxjs'; +import { interval } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; import { DmpToDatasetDialogComponent } from '../dmp-to-dataset/dmp-to-dataset-dialog.component'; import { ExtraPropertiesFormModel } from './general-tab/extra-properties-form.model'; -import { DmpBlueprint, DmpBlueprintDefinition } from '@app/core/model/dmp-blueprint/dmp-blueprint'; @Component({ selector: 'app-dmp-editor-component', @@ -111,7 +110,8 @@ export class DmpEditorComponent extends CheckDeactivateBaseComponent implements private formService: FormService, private lockService: LockService, private configurationService: ConfigurationService, - private matomoService: MatomoService + private matomoService: MatomoService, + private fileUtils: FileUtils ) { super(); } @@ -697,7 +697,7 @@ export class DmpEditorComponent extends CheckDeactivateBaseComponent implements .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "xml", id); }); @@ -708,7 +708,7 @@ export class DmpEditorComponent extends CheckDeactivateBaseComponent implements .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "docx", id); }); @@ -719,7 +719,7 @@ export class DmpEditorComponent extends CheckDeactivateBaseComponent implements .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "pdf", id); }); @@ -730,7 +730,7 @@ export class DmpEditorComponent extends CheckDeactivateBaseComponent implements .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/json' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "json", id); }, async error => { @@ -744,24 +744,6 @@ export class DmpEditorComponent extends CheckDeactivateBaseComponent implements this.uiNotificationService.snackBarNotification(errorObj.message, SnackBarNotificationLevel.Error); } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - public enableForm() { if (this.formGroup.get('status').value !== DmpStatus.Finalized) { this.editMode = true; diff --git a/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.ts b/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.ts index 7653744a1..716eeca7c 100644 --- a/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.ts +++ b/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.ts @@ -1,37 +1,38 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { DmpListingModel } from '../../../../core/model/dmp/dmp-listing'; -import { MatDialog } from '@angular/material/dialog'; -import { DmpInvitationDialogComponent } from '../../invitation/dmp-invitation-dialog.component'; -import { Router, ActivatedRoute } from '@angular/router'; -import { AuthService } from '../../../../core/services/auth/auth.service'; -import { LangChangeEvent, TranslateService } from '@ngx-translate/core'; -import { DmpStatus } from '../../../../core/common/enum/dmp-status'; -import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; -import { DmpService } from '@app/core/services/dmp/dmp.service'; -import { takeUntil, map } from 'rxjs/operators'; -import { BaseComponent } from '@common/base/base.component'; -import * as FileSaver from 'file-saver'; -import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; -import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service'; -import { Role } from '@app/core/common/enum/role'; -import { LockService } from '@app/core/services/lock/lock.service'; import { Location } from '@angular/common'; -import { DmpModel } from '@app/core/model/dmp/dmp'; -import { UntypedFormGroup } from '@angular/forms'; -import { DmpEditorModel } from '../../editor/dmp-editor.model'; -import { CloneDialogComponent } from '../../clone/clone-dialog/clone-dialog.component'; -import { ProjectFormModel } from '../../editor/grant-tab/project-form-model'; -import { FunderFormModel } from '../../editor/grant-tab/funder-form-model'; -import { ExtraPropertiesFormModel } from '../../editor/general-tab/extra-properties-form.model'; -import { GrantTabModel } from '../../editor/grant-tab/grant-tab-model'; -import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { HttpClient } from '@angular/common/http'; -import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; -import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { UntypedFormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { Router } from '@angular/router'; import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; +import { Role } from '@app/core/common/enum/role'; import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; -import { nameof } from 'ts-simple-nameof'; +import { DmpModel } from '@app/core/model/dmp/dmp'; +import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; +import { DmpService } from '@app/core/services/dmp/dmp.service'; +import { LockService } from '@app/core/services/lock/lock.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; +import { BaseComponent } from '@common/base/base.component'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { map, takeUntil } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; +import { DmpStatus } from '../../../../core/common/enum/dmp-status'; +import { DmpListingModel } from '../../../../core/model/dmp/dmp-listing'; +import { AuthService } from '../../../../core/services/auth/auth.service'; +import { CloneDialogComponent } from '../../clone/clone-dialog/clone-dialog.component'; +import { DmpEditorModel } from '../../editor/dmp-editor.model'; +import { ExtraPropertiesFormModel } from '../../editor/general-tab/extra-properties-form.model'; +import { FunderFormModel } from '../../editor/grant-tab/funder-form-model'; +import { GrantTabModel } from '../../editor/grant-tab/grant-tab-model'; +import { ProjectFormModel } from '../../editor/grant-tab/project-form-model'; +import { DmpInvitationDialogComponent } from '../../invitation/dmp-invitation-dialog.component'; @Component({ selector: 'app-dmp-listing-item-component', @@ -63,7 +64,8 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { private lockService: LockService, private location: Location, private httpClient: HttpClient, - private matomoService: MatomoService) { + private matomoService: MatomoService, + private fileUtils: FileUtils) { super(); } @@ -180,10 +182,10 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { if (!isNullOrUndefined(this.dmpFormGroup.get('profile').value)) { this.getBlueprintDefinition(Guid.parse(this.dmpFormGroup.get('profile').value), result => { - this.checkForGrant(result.definition); - this.checkForFunder(result.definition); - this.checkForProject(result.definition); - }); + this.checkForGrant(result.definition); + this.checkForFunder(result.definition); + this.checkForProject(result.definition); + }); } if (!isNewVersion) { @@ -218,10 +220,10 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.maxMultiplicity)].join('.'), ] ) - .pipe(map(data => data as DmpBlueprint), takeUntil(this._destroyed)) - .subscribe( - data => successFunction(data), - ); + .pipe(map(data => data as DmpBlueprint), takeUntil(this._destroyed)) + .subscribe( + data => successFunction(data), + ); } private checkForGrant(blueprint: DmpBlueprintDefinition) { @@ -309,7 +311,7 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "xml", id); @@ -321,7 +323,7 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "docx", id); @@ -333,7 +335,7 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "pdf", id); @@ -345,7 +347,7 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/json' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "json", id); }, async error => { @@ -359,24 +361,6 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { this.uiNotificationService.snackBarNotification(errorObj.message, SnackBarNotificationLevel.Error); } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - deleteClicked(id: string) { this.lockService.checkLockStatus(id).pipe(takeUntil(this._destroyed)) .subscribe(lockStatus => { diff --git a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts index 0a19048fd..6b93ccacd 100644 --- a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts +++ b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts @@ -1,56 +1,57 @@ -import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; -import {MatDialog} from '@angular/material/dialog'; -import {ActivatedRoute, Params, Router} from '@angular/router'; -import {DatasetStatus} from '@app/core/common/enum/dataset-status'; -import {DmpStatus} from '@app/core/common/enum/dmp-status'; -import {DatasetOverviewModel} from '@app/core/model/dataset/dataset-overview'; -import {DatasetsToBeFinalized} from '@app/core/model/dataset/datasets-toBeFinalized'; -import {DmpOverviewModel} from '@app/core/model/dmp/dmp-overview'; -import {UserInfoListingModel} from '@app/core/model/user/user-info-listing'; -import {AuthService} from '@app/core/services/auth/auth.service'; -import {DmpService} from '@app/core/services/dmp/dmp.service'; +import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Params, Router } from '@angular/router'; +import { DatasetStatus } from '@app/core/common/enum/dataset-status'; +import { DmpStatus } from '@app/core/common/enum/dmp-status'; +import { DatasetOverviewModel } from '@app/core/model/dataset/dataset-overview'; +import { DatasetsToBeFinalized } from '@app/core/model/dataset/datasets-toBeFinalized'; +import { DmpOverviewModel } from '@app/core/model/dmp/dmp-overview'; +import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { DmpService } from '@app/core/services/dmp/dmp.service'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; -import {ConfirmationDialogComponent} from '@common/modules/confirmation-dialog/confirmation-dialog.component'; import { DmpFinalizeDialogComponent, DmpFinalizeDialogInput, DmpFinalizeDialogOutput } from '@app/ui/dmp/editor/dmp-finalize-dialog/dmp-finalize-dialog.component'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; // import {BreadcrumbItem} from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; -import {BaseComponent} from '@common/base/base.component'; -import {TranslateService} from '@ngx-translate/core'; -import * as FileSaver from 'file-saver'; -import {Observable, of as observableOf} from 'rxjs'; -import {map, takeUntil} from 'rxjs/operators'; -import {Role} from "@app/core/common/enum/role"; -import {DmpInvitationDialogComponent} from '../invitation/dmp-invitation-dialog.component'; -import {ConfigurationService} from '@app/core/services/configuration/configuration.service'; -import {Location} from '@angular/common'; -import {UntypedFormGroup} from '@angular/forms'; -import {LockService} from '@app/core/services/lock/lock.service'; -import {VersionListingModel} from '@app/core/model/version/version-listing.model'; -import {CloneDialogComponent} from '../clone/clone-dialog/clone-dialog.component'; -import {DmpModel} from '@app/core/model/dmp/dmp'; -import {DmpEditorModel} from '../editor/dmp-editor.model'; -import {FunderFormModel} from '../editor/grant-tab/funder-form-model'; -import {ProjectFormModel} from '../editor/grant-tab/project-form-model'; -import {GrantTabModel} from '../editor/grant-tab/grant-tab-model'; -import {ExtraPropertiesFormModel} from '../editor/general-tab/extra-properties-form.model'; -import {StartNewDmpDialogComponent} from '../start-new-dmp-dialogue/start-new-dmp-dialog.component'; -import {MatomoService} from '@app/core/services/matomo/matomo-service'; -import {PopupNotificationDialogComponent} from '@app/library/notification/popup/popup-notification.component'; -import {DepositRepositoriesService} from '@app/core/services/deposit-repositories/deposit-repositories.service'; -import {DepositConfigurationModel} from '@app/core/model/deposit/deposit-configuration'; -import {DoiModel} from '@app/core/model/doi/doi'; -import {isNullOrUndefined} from '@app/utilities/enhancers/utils'; -import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; -import { Guid } from '@common/types/guid'; -import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; +import { Location } from '@angular/common'; +import { UntypedFormGroup } from '@angular/forms'; import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; +import { Role } from "@app/core/common/enum/role"; +import { DepositConfigurationModel } from '@app/core/model/deposit/deposit-configuration'; +import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; +import { DmpModel } from '@app/core/model/dmp/dmp'; +import { DoiModel } from '@app/core/model/doi/doi'; +import { VersionListingModel } from '@app/core/model/version/version-listing.model'; +import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; +import { DepositRepositoriesService } from '@app/core/services/deposit-repositories/deposit-repositories.service'; +import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; +import { LockService } from '@app/core/services/lock/lock.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; +import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; +import { BaseComponent } from '@common/base/base.component'; +import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { Observable } from 'rxjs'; +import { map, takeUntil } from 'rxjs/operators'; import { nameof } from 'ts-simple-nameof'; +import { CloneDialogComponent } from '../clone/clone-dialog/clone-dialog.component'; +import { DmpEditorModel } from '../editor/dmp-editor.model'; +import { ExtraPropertiesFormModel } from '../editor/general-tab/extra-properties-form.model'; +import { FunderFormModel } from '../editor/grant-tab/funder-form-model'; +import { GrantTabModel } from '../editor/grant-tab/grant-tab-model'; +import { ProjectFormModel } from '../editor/grant-tab/project-form-model'; +import { DmpInvitationDialogComponent } from '../invitation/dmp-invitation-dialog.component'; +import { StartNewDmpDialogComponent } from '../start-new-dmp-dialogue/start-new-dmp-dialog.component'; @Component({ selector: 'app-dmp-overview', @@ -95,7 +96,8 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { private configurationService: ConfigurationService, private location: Location, private lockService: LockService, - private matomoService: MatomoService + private matomoService: MatomoService, + private fileUtils: FileUtils ) { super(); } @@ -115,7 +117,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(data => { this.dmp = data; - if(!this.hasDoi()) { + if (!this.hasDoi()) { this.selectedModel = this.dmp.dois[0]; } this.checkLockStatus(this.dmp.id); @@ -142,7 +144,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(data => { this.dmp = data; - if(!this.hasDoi()) { + if (!this.hasDoi()) { this.selectedModel = this.dmp.dois[0]; } // this.checkLockStatus(this.dmp.id); @@ -162,12 +164,12 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { } }); this.depositRepositoriesService.getAvailableRepos() - .pipe(takeUntil(this._destroyed)) - .subscribe( - repos => { - this.depositRepos = repos; - }, - error => this.depositRepos = []); + .pipe(takeUntil(this._destroyed)) + .subscribe( + repos => { + this.depositRepos = repos; + }, + error => this.depositRepos = []); } onFetchingDeletedCallbackError(redirectRoot: string) { @@ -252,11 +254,11 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.maxMultiplicity)].join('.'), ] ) - .pipe(map(data => data as DmpBlueprint), takeUntil(this._destroyed)) - .subscribe( - data => successFunction(data), - error => this.onCallbackError(error) - ); + .pipe(map(data => data as DmpBlueprint), takeUntil(this._destroyed)) + .subscribe( + data => successFunction(data), + error => this.onCallbackError(error) + ); } private checkForGrant(blueprint: DmpBlueprintDefinition) { @@ -426,7 +428,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "xml", id); @@ -438,7 +440,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "docx", id); @@ -450,7 +452,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "pdf", id); @@ -462,7 +464,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(complete => { const blob = new Blob([complete.body], { type: 'application/json' }); - const filename = this.getFilenameFromContentDispositionHeader(complete.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(complete.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); this.matomoService.trackDownload('dmps', "json", id); }, async error => { @@ -476,24 +478,6 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { this.uiNotificationService.snackBarNotification(errorObj.message, SnackBarNotificationLevel.Error); } - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - roleDisplayFromList(value: UserInfoListingModel[]) { const principalId: string = this.authentication.userId()?.toString(); let role: number; @@ -603,7 +587,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { return this.depositRepos.filter(repo => !this.dmp.dois.find(doi => doi.repositoryId === repo.repositoryId)); } - moreDeposit(){ + moreDeposit() { return (this.dmp.dois.length < this.depositRepos.length); } @@ -648,17 +632,17 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe( complete => { - if(extraProperties.visible){ + if (extraProperties.visible) { //this.publish(this.dmp.id); this.dmpService.publish(this.dmp.id) - .pipe(takeUntil(this._destroyed)) - .subscribe(() => { - //this.hasPublishButton = false; - this.dmp.status = DmpStatus.Finalized; - this.onUpdateCallbackSuccess(); - }); + .pipe(takeUntil(this._destroyed)) + .subscribe(() => { + //this.hasPublishButton = false; + this.dmp.status = DmpStatus.Finalized; + this.onUpdateCallbackSuccess(); + }); } - else{ + else { this.dmp.status = DmpStatus.Finalized; this.onUpdateCallbackSuccess(); } @@ -679,22 +663,22 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { return this.dmpBlueprintService.getSingle(Guid.parse(blueprintId)) .pipe(map(result => { return result.definition.sections.some(section => { - if(!section.hasTemplates) + if (!section.hasTemplates) return false; return section.descriptionTemplates.some(template => { - if(!(template.minMultiplicity > 0)) + if (!(template.minMultiplicity > 0)) return false; let count = 0; dmpModel.datasets.filter(dataset => dataset.dmpSectionIndex === (section.ordinal - 1)).forEach(dataset => { - if(Guid.parse(dataset.profile.id) === template.descriptionTemplateId){ + if (Guid.parse(dataset.profile.id) === template.descriptionTemplateId) { count++; } }) - if(count < template.minMultiplicity){ + if (count < template.minMultiplicity) { this.dialog.open(PopupNotificationDialogComponent, { data: { - title: this.language.instant('DMP-OVERVIEW.MIN-DESCRIPTIONS-DIALOG.TITLE', {'minMultiplicity': template.minMultiplicity}), - message: this.language.instant('DMP-OVERVIEW.MIN-DESCRIPTIONS-DIALOG.MESSAGE', ) + title: this.language.instant('DMP-OVERVIEW.MIN-DESCRIPTIONS-DIALOG.TITLE', { 'minMultiplicity': template.minMultiplicity }), + message: this.language.instant('DMP-OVERVIEW.MIN-DESCRIPTIONS-DIALOG.MESSAGE',) }, maxWidth: '30em' }); return true; @@ -766,7 +750,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { this.router.navigate(['/datasets', 'new', this.dmp.id]); } - selectDoi(doiModel: DoiModel){ + selectDoi(doiModel: DoiModel) { this.selectedModel = doiModel; const foundIdx = this.dmp.dois.findIndex(el => el.id == doiModel.id); this.dmp.dois.splice(foundIdx, 1); @@ -775,17 +759,17 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { createDoiLink(doiModel: DoiModel): string { const repository = this.depositRepos.find(r => r.repositoryId == doiModel.repositoryId); - if(typeof repository !== "undefined"){ - if(doiModel.repositoryId == "Zenodo"){ + if (typeof repository !== "undefined") { + if (doiModel.repositoryId == "Zenodo") { const doiarr = doiModel.doi.split('.'); const id = doiarr[doiarr.length - 1]; return repository.repositoryRecordUrl + id; } - else{ + else { return repository.repositoryRecordUrl + doiModel.doi; } } - else{ + else { return ""; } } @@ -888,11 +872,13 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { this.lockService.checkLockStatus(id).pipe(takeUntil(this._destroyed)) .subscribe(lockStatus => { this.lockStatus = lockStatus - if(lockStatus){ - this.dialog.open(PopupNotificationDialogComponent,{data:{ - title:this.language.instant('DMP-OVERVIEW.LOCKED-DIALOG.TITLE'), - message:this.language.instant('DMP-OVERVIEW.LOCKED-DIALOG.MESSAGE') - }, maxWidth:'30em'}); + if (lockStatus) { + this.dialog.open(PopupNotificationDialogComponent, { + data: { + title: this.language.instant('DMP-OVERVIEW.LOCKED-DIALOG.TITLE'), + message: this.language.instant('DMP-OVERVIEW.LOCKED-DIALOG.MESSAGE') + }, maxWidth: '30em' + }); } }); } diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts index 222299821..c11d835c6 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts @@ -50,6 +50,7 @@ import {HttpErrorResponse} from "@angular/common/http"; import * as FileSaver from "file-saver"; import { FetcherExternalReference } from '@app/core/model/external-reference/external-reference'; import { ExternalReferencesType } from '@app/core/common/enum/external-references-type'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; @Component({ selector: 'app-form-field', @@ -129,7 +130,8 @@ export class FormFieldComponent extends BaseComponent implements OnInit { private fileService: FileService, private cdr: ChangeDetectorRef, private uiNotificationService: UiNotificationService, - public dialog: MatDialog + public dialog: MatDialog, + private fileUtils: FileUtils ) { super(); @@ -704,31 +706,12 @@ export class FormFieldComponent extends BaseComponent implements OnInit { .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], {type: this.form.value.value.type}); - const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); }); } - // create a fileUtils/ fileHelper file and add this function (and any other possibly generic) there - getFilenameFromContentDispositionHeader(header: string): string { - const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); - - const matches = header.match(regex); - let filename: string; - for (let i = 0; i < matches.length; i++) { - const match = matches[i]; - if (match.includes('filename="')) { - filename = match.substring(10, match.length - 1); - break; - } else if (match.includes('filename=')) { - filename = match.substring(9); - break; - } - } - return filename; - } - // isImageFile(fileType: string) { // if(!fileType) { // return false; diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 81990f863..42d059b9e 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -200,13 +200,13 @@ }, "COMMONS": { "LISTING-COMPONENT": { - "SEARCH-FILTER-BTN":"Filter Results", + "SEARCH-FILTER-BTN": "Filter Results", "CLEAR-ALL-FILTERS": "clear all filters", - "FILTERS-SAVE-AS":"Save as", - "CANCEL":"Cancel", - "APPLY-FILTERS":"Apply Filters", - "TITLE":"Filters", - "ADD-NEW":"Add new", + "FILTERS-SAVE-AS": "Save as", + "CANCEL": "Cancel", + "APPLY-FILTERS": "Apply Filters", + "TITLE": "Filters", + "ADD-NEW": "Add new", "SETTINGS": { "TITLE": "Settings", "APPLY": "Apply", @@ -221,15 +221,15 @@ }, "SETTINGS-PICKER": { "DRAFT-FILTERS": "Draft profile", - "RENAME":"Rename", - "NAME":"Name", - "USER":"User", - "SHARE":"Share", - "CANCEL":"Cancel" + "RENAME": "Rename", + "NAME": "Name", + "USER": "User", + "SHARE": "Share", + "CANCEL": "Cancel" }, "BREADCRUMBS": { - "OVERVIEW":"Overview", - "HOME":"Board", + "OVERVIEW": "Overview", + "HOME": "Board", "DESCRIPTION-TEMPLATE-TYPES": "Description Types", "NEW-DESCRIPTION-TEMPLATE-TYPE": "New", "EDIT-DESCRIPTION-TEMPLATE-TYPE": "Edit", @@ -362,8 +362,8 @@ "UNTITLED": "Untitled", "QUESTION": "Question", "TEMPLATE-OUTLINE": "Template outline", - "ERRORS":{ - "USER-NOT-FOUND":"User not found." + "ERRORS": { + "USER-NOT-FOUND": "User not found." } }, "PAGE-INFO": { @@ -539,7 +539,7 @@ "FIELD-VALIDATOR-TITLE": "Validator Data", "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text", "EXTERNAL-DATASET-TYPE-NAME": "Type", - "EXTERNAL-DATASET-TYPES":{ + "EXTERNAL-DATASET-TYPES": { "PRODUCED": "Produced dataset", "REUSED": "Reused dataset", "OTHER": "Other" @@ -564,9 +564,9 @@ "ACTIONS": { "ADD-RULE": "Add Visibility Rule +" }, - "STATUS":{ - "CALCULATING-PREVIEW":"... calculating preview", - "PREVIEW-UPDATED":"Preview updated!" + "STATUS": { + "CALCULATING-PREVIEW": "... calculating preview", + "PREVIEW-UPDATED": "Preview updated!" } }, "RULE": { @@ -805,9 +805,9 @@ "DOWNLOAD-DOCX": "Download DOCX", "COPY-DESCRIPTION": "Copy Description", "UPDATE-DATASET-PROFILE": "Update Template", - "VALIDATE":"Validate", - "UNDO-FINALIZATION-QUESTION" :"Undo finalization?", - "CONFIRM" : "Yes", + "VALIDATE": "Validate", + "UNDO-FINALIZATION-QUESTION": "Undo finalization?", + "CONFIRM": "Yes", "REJECT": "No" }, "MESSAGES": { @@ -843,8 +843,8 @@ "NEXT": "Next", "ERROR-MESSAGE": "Does not contain this Dataset Template" }, - "LOCKED":{ - "TITLE":"Dataset is locked", + "LOCKED": { + "TITLE": "Dataset is locked", "MESSAGE": "Somebody else is modifying the dataset at this moment. You may view the dataset but you cannot make any changes. If you would like to modify it please come back later." } }, @@ -881,9 +881,9 @@ "AUTHORIZE": "Proceed to authorization", "CANCEL": "Cancel" }, - "LOCKED-DIALOG":{ + "LOCKED-DIALOG": { "TITLE": "DMP is locked", - "MESSAGE":"Somebody else is modifying the DMP at this moment. If you would like to modify or view it, please come back later." + "MESSAGE": "Somebody else is modifying the DMP at this moment. If you would like to modify or view it, please come back later." }, "MIN-DESCRIPTIONS-DIALOG": { "TITLE": "Min({{minMultiplicity}}) datasets needed using this template.", @@ -896,14 +896,14 @@ "DELETED-DATASET": "The requested dataset is deleted", "FORBIDEN-DATASET": "You are not allowed to access this dataset" }, - "LOCKED":{ + "LOCKED": { "TITLE": "Dataset is locked", "MESSAGE": "Somebody else is modifying the dataset at this moment. If you would like to modify or view it, please come back later." }, - "FINALISE-POPUP":{ - "MESSAGE":"You will be directed to Dataset Editor where you can finalize the selected dataset. Would you like to proceed?", - "CONFIRM":"OK", - "CANCEL":"Cancel" + "FINALISE-POPUP": { + "MESSAGE": "You will be directed to Dataset Editor where you can finalize the selected dataset. Would you like to proceed?", + "CONFIRM": "OK", + "CANCEL": "Cancel" } }, "DATASET-LISTING": { @@ -1023,18 +1023,17 @@ "CANCEL": "Cancel", "APPLY-FILTERS": "Apply filters" }, - "CONFIRM-DELETE-DIALOG":{ + "CONFIRM-DELETE-DIALOG": { "MESSAGE": "Would you like to delete this Description Type?", "CONFIRM-BUTTON": "Yes, delete", "CANCEL-BUTTON": "No" }, "ACTIONS": { "DELETE": "Delete", - "EDIT":"Edit" + "EDIT": "Edit" }, "SUCCESSFUL-DELETE": "Successful Delete", "UNSUCCESSFUL-DELETE": "This item could not be deleted." - }, "DATASET-UPLOAD": { "TITLE": "Import Dataset", @@ -1072,10 +1071,23 @@ }, "FIELDS": { "TITLE": "Fields", - "LABEL": "Name", - "TYPE": "Type", - "DATATYPE": "Data Type", - "REQUIRED": "Required", + "NAME": "Name", + "SECTIONS": "Sections", + "SECTION-PREFIX": "Section", + "SECTION-NAME": "Section Name", + "SECTION-DESCRIPTION": "Section Description", + "SYSTEM-FIELDS": "System Fields", + "SYSTEM-FIELD": "System Field", + "FIELD-TYPE": "Field Type", + "FIELD-LABEL": "Label", + "FIELD-PLACEHOLDER": "Placeholder", + "FIELD-DESCRIPTION": "Description", + "FIELD-DATA-TYPE": "Data Type", + "FIELD-REQUIRED": "Required", + "DESCRIPTION-TEMPLATES": "Description Templates", + "DESCRIPTION-TEMPLATE-LABEL": "Label", + "DESCRIPTION-TEMPLATE-MIN-MULTIPLICITY": "Min Multiplicity", + "DESCRIPTION-TEMPLATE-MAX-MULTIPLICITY": "Max Multiplicity", "EXTERNAL-AUTOCOMPLETE": { "TITLE": "Autocomplete Data", "MULTIPLE-AUTOCOMPLETE": "Multiple Autocomplete", @@ -1087,13 +1099,18 @@ } }, "ACTIONS": { + "ADD-EXTRA-FIELD": "Add extra field", + "REMOVE-SYSTEM-FIELD": "Delete", + "REMOVE-EXTRA-FIELD": "Delete", + "REMOVE-SECTION": "Remove Section", + "ADD-SECTION": "Add Section", "SAVE": "Save", "CANCEL": "Cancel", "DELETE": "Delete", "FINALIZE": "Finalize", "DOWNLOAD-XML": "Download XML" }, - "CONFIRM-DELETE-DIALOG":{ + "CONFIRM-DELETE-DIALOG": { "MESSAGE": "Would you like to delete this DMP template?", "CONFIRM-BUTTON": "Yes, delete", "CANCEL-BUTTON": "No" @@ -1226,7 +1243,7 @@ "ORGANISATION-IDENTIFIER-EXSTS": "Organization identifier already exists.", "IDENTIFIER-EXISTS-RESEARCHER-LIST": "This identifier is already used by a researcher in the researchers list.", "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organization in the organizations list.", - "SAVE":"Save", + "SAVE": "Save", "CANCEL": "Cancel" }, "FUNDING-INFO": { @@ -1257,9 +1274,9 @@ "SAVE": "Save", "CANCEL": "Cancel" }, - "LOCKED":{ - "TITLE":"DMP is locked", - "MESSAGE":"Somebody else is modifying the DMP at this moment. You may view the dataset but you cannot make any changes." + "LOCKED": { + "TITLE": "DMP is locked", + "MESSAGE": "Somebody else is modifying the DMP at this moment. You may view the dataset but you cannot make any changes." } }, "DMP-BLUEPRINT-LISTING": { @@ -1278,16 +1295,16 @@ "CANCEL": "Cancel", "APPLY-FILTERS": "Apply filters" }, - "CONFIRM-DELETE-DIALOG":{ + "CONFIRM-DELETE-DIALOG": { "MESSAGE": "Would you like to delete this DMP Blueprint?", "CONFIRM-BUTTON": "Yes, delete", "CANCEL-BUTTON": "No" }, "ACTIONS": { "DELETE": "Delete", - "EDIT":"Edit", + "EDIT": "Edit", "CLONE": "Clone", - "DOWNLOAD-XML":"Download XML" + "DOWNLOAD-XML": "Download XML" }, "IMPORT": { "UPLOAD-XML": "Import", @@ -1298,7 +1315,7 @@ }, "SUCCESSFUL-DELETE": "Successful Delete", "UNSUCCESSFUL-DELETE": "This item could not be deleted.", - "TEMPLATE-UPLOAD-SUCCESS":"Template successfully uploaded" + "TEMPLATE-UPLOAD-SUCCESS": "Template successfully uploaded" }, "DYNAMIC-FORM": { "FIELDS": { @@ -1426,7 +1443,7 @@ "ABOUT": "Versioning is automated.", "QUESTION": "It seems your Dataset Template is outdated. Do you want to update it to the latest version?" }, - "ERRORS":{ + "ERRORS": { "ERROR-OCCURED": "An error occurred.", "MESSAGE": "Message: " }, @@ -1603,9 +1620,28 @@ "PUBLISHED": "Published", "STATUS": "Status" }, - "DESCRIPTION-TEMPLATE-TYPE-STATUS":{ + "DESCRIPTION-TEMPLATE-TYPE-STATUS": { "FINALIZED": "Finalized", "DRAFT": "Draft" + }, + "DMP-BLUEPRINT-SYSTEM-FIELD-TYPE": { + "TEXT": "Title", + "HTML_TEXT": "Description", + "RESEARCHERS": "Researchers", + "ORGANIZATIONS": "Organizations", + "LANGUAGE": "Language", + "CONTACT": "Contact", + "FUNDER": "Funder", + "GRANT": "Grant", + "PROJECT": "Project", + "LICENSE": "License", + "ACCESS_RIGHTS": "Access" + }, + "DMP-BLUEPRINT-EXTRA-FIELD-DATA-TYPE": { + "DATE": "Date", + "NUMBER": "Number", + "TEXT": "Text", + "EXTERNAL-AUTOCOMPLETE": "External AutoComplete" } }, "ADDRESEARCHERS-EDITOR": { @@ -2050,4 +2086,4 @@ "FINALIZED": "Finalized", "DELETED": "Deleted" } -} +} \ No newline at end of file