more blueprint editor fixes

This commit is contained in:
Diamantis Tziotzios 2023-10-25 17:47:48 +03:00
parent 45f8f51288
commit d1bac40f0f
24 changed files with 1425 additions and 1642 deletions

View File

@ -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
],
};
}

View File

@ -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`; }

View File

@ -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');
}
}
}

View File

@ -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;
}
}

View File

@ -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 = {

View File

@ -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) {

View File

@ -23,7 +23,7 @@
<button mat-button class="finalize-btn" (click)="finalize()" [disabled]="!this.isFormValid()" type="button">{{'DMP-BLUEPRINT-EDITOR.ACTIONS.FINALIZE' | translate }}</button>
</div>
</div>
<form *ngIf="formGroup" (ngSubmit)="formSubmit()" [formGroup]="formGroup">
<form *ngIf="formGroup" (ngSubmit)="formSubmit()">
<mat-card>
<!-- <mat-card-header>
<mat-card-title *ngIf="isNew">
@ -35,206 +35,167 @@
</mat-card-header> -->
<mat-card-content>
<div class="row">
<mat-form-field class="col-lg-6">
<mat-label>Name</mat-label>
<input matInput type="text" name="label" formControlName="label" required>
<mat-error *ngIf="formGroup.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<h4 class="col-12">Sections</h4>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.NAME' | translate}}</mat-label>
<input matInput type="text" name="label" [formControl]="formGroup.get('label')" required>
<mat-error *ngIf="formGroup.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<h4 class="col-12">{{'DMP-BLUEPRINT-EDITOR.FIELDS.SECTIONS' | translate}}</h4>
<div class="col-12" cdkDropList (cdkDropListDropped)="dropSections($event)">
<div *ngFor="let section of formGroup.get('definition').get('sections').controls; let sectionIndex=index;" class="row section-input" cdkDrag [cdkDragDisabled]="viewOnly">
<div *ngFor="let section of formGroup.get('definition').get('sections').controls; let sectionIndex=index;" class="row mb-3" cdkDrag [cdkDragDisabled]="viewOnly">
<div class="col-12">
<mat-card>
<mat-card-header>
<mat-card-title>Section {{sectionIndex + 1}}</mat-card-title>
<mat-icon cdkDragHandle style="cursor: move; color: #129d99;">drag_indicator</mat-icon>
<div class="row mb-3 d-flex align-items-center">
<div class="col-auto d-flex">
<mat-card-title>{{'DMP-BLUEPRINT-EDITOR.FIELDS.SECTION-PREFIX' | translate}} {{sectionIndex + 1}}</mat-card-title>
</div>
<div class="col-auto d-flex"><mat-icon cdkDragHandle style="cursor: move; color: #129d99;">drag_indicator</mat-icon></div>
<div [hidden]="viewOnly" class="col-auto d-flex" (click)="removeSection(sectionIndex)" [disabled]="viewOnly">
<mat-icon class="action-list-icon" matTooltip="{{'DMP-BLUEPRINT-EDITOR.ACTIONS.REMOVE-SECTION' | translate}}">delete</mat-icon>
</div>
</div>
</mat-card-header>
<div class="row">
<mat-card-content>
<div class="row">
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.SECTION-NAME' | translate}}</mat-label>
<input matInput type="text" name="label" [formControl]="section.get('label')" required>
<mat-error *ngIf="section.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.SECTION-DESCRIPTION' | translate}}</mat-label>
<input matInput type="text" name="description" [formControl]="section.get('description')">
<mat-error *ngIf="section.get('description').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.SYSTEM-FIELDS' | translate}}</mat-label>
<mat-select multiple [disabled]="viewOnly" [(ngModel)]="selectedSystemFields" [ngModelOptions]="{standalone: true}">
<mat-option *ngFor="let systemFieldType of dmpBlueprintSystemFieldTypeEnum" [disabled]="systemFieldDisabled(systemFieldType)" [value]="systemFieldType">{{enumUtils.toDmpBlueprintSystemFieldTypeString(systemFieldType)}}</mat-option>
</mat-select>
<!-- <mat-error *ngIf="fieldsArray(sectionIndex).hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> -->
</mat-form-field>
</div>
<div class="col-auto">
<button mat-button class="action-btn" type="button" (click)="addExtraField(sectionIndex)" [disabled]="viewOnly">{{'DMP-BLUEPRINT-EDITOR.ACTIONS.ADD-EXTRA-FIELD' | translate}}</button>
</div>
<mat-form-field class="col-6">
<mat-label>Section name</mat-label>
<input matInput type="text" name="label" [formControl]="section.get('label')" required>
<mat-error *ngIf="section.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<mat-form-field class="col-6">
<mat-label>Section description</mat-label>
<input matInput type="text" name="description" [formControl]="section.get('description')">
<mat-error *ngIf="section.get('description').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<div class="col-12">
<div class="row">
<div cdkDropList class="col-12" (cdkDropListDropped)="dropFields($event, sectionIndex)">
<div *ngFor="let field of section.get('fields').controls; let fieldIndex=index;" cdkDrag class="row align-items-center" [cdkDragDisabled]="viewOnly">
<div class="col-12">
<div class="row">
<div class="col-6">
<mat-form-field>
<mat-label>System fields</mat-label>
<mat-select multiple [disabled]="viewOnly" [value]="systemFieldListPerSection[sectionIndex]">
<mat-option *ngFor="let f of fieldList" [disabled]="systemFieldDisabled(f.type, sectionIndex)" [value]="f.type" (click)="selectedFieldType(f.type, sectionIndex)">{{f.label}}</mat-option>
</mat-select>
<mat-error *ngIf="fieldsArray(sectionIndex).hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<button mat-button class="action-btn" type="button" (click)="addExtraField(sectionIndex)" [disabled]="viewOnly">Add extra field</button>
</div>
<div class="col-auto">
<span style="font-size: 15px;">{{fieldIndex + 1}}</span>
</div>
</div>
<div class="col-12">
<div cdkDropList (cdkDropListDropped)="drop($event, sectionIndex)">
<div *ngFor="let field of section.get('fields').controls; let fieldIndex=index;" cdkDrag [cdkDragDisabled]="viewOnly">
<div class="col-12">
<div class="row">
<div class="col-xx-1" style="padding-left: 0;">
<div class="row">
<div class="col-4">
<span style="font-size: 15px;">{{fieldIndex + 1}}</span>
</div>
<div class="col-8">
<mat-icon cdkDragHandle style="cursor: move; color: #129d99;">drag_indicator</mat-icon>
</div>
</div>
</div>
<ng-container *ngIf="field.get('category').value === dmpBlueprintSectionFieldCategory.SYSTEM">
<div class="col-2">
<mat-form-field>
<mat-label>System Field</mat-label>
<input matInput disabled value="{{transfromEnumToString(field.get('type').value)}}" type="text" name="name">
</mat-form-field>
</div>
<div class="col-2">
<mat-form-field>
<mat-label>Label</mat-label>
<input matInput type="text" name="label" [formControl]="field.get('label')" required>
<mat-error *ngIf="field.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-2">
<mat-form-field>
<mat-label>Placeholder</mat-label>
<input matInput type="text" name="placeholder" [formControl]="field.get('placeholder')">
</mat-form-field>
</div>
<div class="col-2">
<mat-form-field>
<mat-label>Description</mat-label>
<input matInput type="text" name="description" [formControl]="field.get('description')">
</mat-form-field>
</div>
<div class="centered-row-item col-1">
<mat-checkbox [disabled]="field.get('type').value === 0 || field.get('type').value === 1" [formControl]="field.get('required')">Required</mat-checkbox>
</div>
<div [hidden]="viewOnly" class="field-delete col-1" (click)="removeSystemFieldWithIndex(sectionIndex, fieldIndex)">
<mat-icon class="field-delete-icon">delete</mat-icon>
<span class="field-delete-text">{{'DMP-BLUEPRINT-EDITOR.STEPS.TOOLKIT.DELETE' | translate}}</span>
</div>
</ng-container>
<ng-container *ngIf="field.get('category').value === dmpBlueprintSectionFieldCategory.EXTRA">
<div class="col-2">
<mat-form-field>
<mat-label>Type</mat-label>
<mat-select placeholder="Type" formControlName="type" required>
<mat-option *ngFor="let extraFieldType of getExtraFieldTypes()" [value]="extraFieldType">
{{getExtraFieldTypeValue(extraFieldType)}}
</mat-option>
</mat-select>
<mat-error *ngIf="field.get('type').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-2">
<mat-form-field>
<mat-label>Label</mat-label>
<input matInput type="text" name="label" formControlName="label" required>
<mat-error *ngIf="field.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-2">
<mat-form-field>
<mat-label>Placeholder</mat-label>
<input matInput type="text" name="placeholder" formControlName="placeholder">
</mat-form-field>
</div>
<div class="col-2">
<mat-form-field>
<mat-label>Description</mat-label>
<input matInput type="text" name="description" formControlName="description">
</mat-form-field>
</div>
<div class="centered-row-item col-1">
<mat-checkbox formControlName="required">
Required
</mat-checkbox>
</div>
<div [hidden]="viewOnly" class="field-delete col-1" (click)="removeExtraField(sectionIndex, fieldIndex)">
<mat-icon class="field-delete-icon">delete</mat-icon>
<span class="field-delete-text">{{'DMP-BLUEPRINT-EDITOR.STEPS.TOOLKIT.DELETE' | translate}}</span>
</div>
</ng-container>
</div>
</div>
</div>
<div class="col-auto">
<mat-icon cdkDragHandle style="cursor: move; color: #129d99;">drag_indicator</mat-icon>
</div>
<div class="col-auto" *ngIf="field.get('category').value === dmpBlueprintSectionFieldCategory.SYSTEM">
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.SYSTEM-FIELD' | translate}}</mat-label>
<input matInput disabled value="{{enumUtils.toDmpBlueprintSystemFieldTypeString(field.get('systemFieldType').value)}}" type="text" name="name">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="field.get('category').value === dmpBlueprintSectionFieldCategory.EXTRA">
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.FIELD-DATA-TYPE' | translate}}</mat-label>
<mat-select [formControl]="field.get('dataType')" required>
<mat-option *ngFor="let extraFieldDataType of dmpBlueprintExtraFieldDataTypeEnum" [value]="extraFieldDataType">
{{enumUtils.toDmpBlueprintExtraFieldDataTypeString(extraFieldDataType)}}
</mat-option>
</mat-select>
<mat-error *ngIf="field.get('dataType').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col">
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.FIELD-LABEL' | translate}}</mat-label>
<input matInput type="text" name="label" [formControl]="field.get('label')" required>
<mat-error *ngIf="field.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col">
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.FIELD-PLACEHOLDER' | translate}}</mat-label>
<input matInput type="text" name="placeholder" [formControl]="field.get('placeholder')">
</mat-form-field>
</div>
<div class="col">
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.FIELD-DESCRIPTION' | translate}}</mat-label>
<input matInput type="text" name="description" [formControl]="field.get('description')">
</mat-form-field>
</div>
<div class="centered-row-item col-auto">
<mat-checkbox [disabled]="field.get('systemFieldType')?.value === dmpBlueprintSystemFieldType.TEXT || field.get('systemFieldType')?.value === dmpBlueprintSystemFieldType.HTML_TEXT" [formControl]="field.get('required')">{{'DMP-BLUEPRINT-EDITOR.FIELDS.FIELD-REQUIRED' | translate}}</mat-checkbox>
</div>
<div *ngIf="field.get('category').value === dmpBlueprintSectionFieldCategory.SYSTEM" [hidden]="viewOnly" class="field-delete col-auto" (click)="removeSystemField(sectionIndex, fieldIndex)">
<mat-icon class="field-delete-icon">delete</mat-icon>
<span class="field-delete-text">{{'DMP-BLUEPRINT-EDITOR.ACTIONS.REMOVE-SYSTEM-FIELD' | translate}}</span>
</div>
<div *ngIf="field.get('category').value === dmpBlueprintSectionFieldCategory.EXTRA" [hidden]="viewOnly" class="field-delete col-auto" (click)="removeExtraField(sectionIndex, fieldIndex)">
<mat-icon class="field-delete-icon">delete</mat-icon>
<span class="field-delete-text">{{'DMP-BLUEPRINT-EDITOR.ACTIONS.REMOVE-EXTRA-FIELD' | translate}}</span>
</div>
</div>
</div>
</div>
</div>
</mat-card-content>
<div class="col-12">
<div class="row">
<div class="col-12">
<div class="col-auto">
<mat-checkbox [formControl]="section.get('hasTemplates')" (change)="checkForBlueprints($event, sectionIndex)">
Description Templates
{{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATES' | translate}}
</mat-checkbox>
</div>
</div>
</div>
<!-- <div class="col-12" *ngIf="section.get('hasTemplates').value == true">
<div class="row">
<div class="col-12">
<mat-form-field>
<mat-label>Description Templates</mat-label>
<app-multiple-auto-complete placeholder="Description Templates" [disabled]="viewOnly" [value]="descriptionTemplatesPerSection[sectionIndex]" [hidePlaceholder]="true" required='false' [configuration]="blueprintsAutoCompleteConfiguration" (optionRemoved)="onRemoveTemplate($event, sectionIndex)" (optionSelected)="onOptionSelected($event, sectionIndex)">
</app-multiple-auto-complete>
</mat-form-field>
</div>
</div>
</div> -->
<div *ngFor="let descriptionTemplate of section.get('descriptionTemplates').controls; let j=index;" class="section-input" style="width: 100%;">
<div class="col-12" *ngIf="section.get('hasTemplates').value == true">
<div class="row">
<div class="col-12">
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATES' | translate}}</mat-label>
<app-multiple-auto-complete [disabled]="viewOnly" [hidePlaceholder]="true" required='false' [configuration]="blueprintsAutoCompleteConfiguration" (optionRemoved)="onRemoveDescritionTemplate($event, sectionIndex)" (optionSelected)="onSelectDescritionTemplate($event, sectionIndex)">
</app-multiple-auto-complete>
</mat-form-field>
</div>
</div>
</div>
<div *ngFor="let descriptionTemplate of section.get('descriptionTemplates').controls; let j=index;" class="col-12">
<div class="col-12">
<div class="row">
<div class="col-4">
<mat-form-field>
<mat-label>Label</mat-label>
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATE-LABEL' | translate}}</mat-label>
<input matInput type="text" value="descriptionTemplate.get('label')" name="label" [formControl]="descriptionTemplate.get('label')">
</mat-form-field>
</div>
<div class="col-4">
<mat-form-field>
<mat-label>Min Multiplicity</mat-label>
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATE-MIN-MULTIPLICITY' | translate}}</mat-label>
<input matInput type="number" min="0" name="minMultiplicity" [formControl]="descriptionTemplate.get('minMultiplicity')">
</mat-form-field>
</div>
<div class="col-4">
<mat-form-field>
<mat-label>Max Multiplicity</mat-label>
<mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATE-MAX-MULTIPLICITY' | translate}}</mat-label>
<input matInput type="number" min="1" name="maxMultiplicity" [formControl]="descriptionTemplate.get('maxMultiplicity')">
</mat-form-field>
</div>
@ -242,17 +203,7 @@
</div>
</div>
</mat-card>
<div class="col-1">
<div class="row">
<!-- <div class="col-auto dlt-section-btn">
<button mat-button class="action-btn" type="button" click="removeSection(sectionIndex)">Delete</button>
</div> -->
<div [hidden]="viewOnly" class="action-list-item col-auto dlt-section-btn" (click)="removeSection(sectionIndex)" [disabled]="viewOnly">
<mat-icon class="action-list-icon">delete</mat-icon>
<span class="action-list-text">{{'DMP-BLUEPRINT-EDITOR.STEPS.TOOLKIT.DELETE' | translate}}</span>
</div>
</div>
</div>
</div>
</div>
@ -262,7 +213,7 @@
<div class="col-12">
<div class="row">
<div class="col-auto">
<button mat-button class="action-btn" type="button" (click)="addSection()" [disabled]="viewOnly">Add section</button>
<button mat-button class="action-btn" type="button" (click)="addSection()" [disabled]="viewOnly">{{'DMP-BLUEPRINT-EDITOR.ACTIONS.ADD-SECTION' | translate}}</button>
</div>
</div>
</div>
@ -281,5 +232,4 @@
</mat-card-content>
</mat-card>
</form>
</div>
<!-- </div> -->
</div>

View File

@ -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);
}

View File

@ -182,7 +182,7 @@ export class DmpBlueprintListingComponent extends BaseListingComponent<DmpBluepr
// .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);
// });
}
@ -209,27 +209,10 @@ export class DmpBlueprintListingComponent extends BaseListingComponent<DmpBluepr
// .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) {

View File

@ -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<UserListingModel> {
@ -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';
}

View File

@ -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<string> = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value];
const allDataTableRequest: DataTableMultiTypeRequest<RecentActivityCriteria> = new DataTableMultiTypeRequest(this.dmpOffset, this.datasetOffset, 5, {fields: fields});
const allDataTableRequest: DataTableMultiTypeRequest<RecentActivityCriteria> = 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<string> = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value];
// const fields: Array<string> = ["-modified"];
let request;
if(more) {
request = new DataTableMultiTypeRequest<RecentActivityCriteria>(this.dmpOffset, this.datasetOffset, this.pageSize, {fields: fields});
if (more) {
request = new DataTableMultiTypeRequest<RecentActivityCriteria>(this.dmpOffset, this.datasetOffset, this.pageSize, { fields: fields });
} else {
request = new DataTableMultiTypeRequest<RecentActivityCriteria>(this.offsetLess, this.offsetLess, this.pageSize, {fields: fields});
request = new DataTableMultiTypeRequest<RecentActivityCriteria>(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;

View File

@ -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<string> = [((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<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(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<string> = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value];
// const fields: Array<string> = ["-modified"];
let request;
if(more) {
request = new DataTableMultiTypeRequest<RecentActivityCriteria>(this.dmpOffset, this.datasetOffset, this.pageSize, {fields: fields});
if (more) {
request = new DataTableMultiTypeRequest<RecentActivityCriteria>(this.dmpOffset, this.datasetOffset, this.pageSize, { fields: fields });
} else {
request = new DataTableMultiTypeRequest<RecentActivityCriteria>(this.offsetLess, this.offsetLess, this.pageSize, {fields: fields});
request = new DataTableMultiTypeRequest<RecentActivityCriteria>(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;

View File

@ -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<string> = ["-modified"];
if(!this.formGroup.get('order').value) {
if (!this.formGroup.get('order').value) {
this.formGroup.get('order').setValue(this.order.MODIFIED);
}
const fields: Array<string> = [((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<string>, more: boolean = true): DataTableRequest<DatasetCriteria> {
@ -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<string> = [((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<DatasetCriteria>(this.startIndex, this.pageSize, { fields: fields });
} else {
request = new DataTableRequest<DatasetCriteria>(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);

View File

@ -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<string> = ["-modified"];
if(!this.formGroup.get('order').value) {
if (!this.formGroup.get('order').value) {
this.formGroup.get('order').setValue(this.order.MODIFIED);
}
const fields: Array<string> = [((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<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(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<string> = [((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<DmpCriteria>(this.startIndex, this.pageSize, {fields: fields})
if (this.isAuthenticated()) {
if (more) {
request = new DataTableRequest<DmpCriteria>(this.startIndex, this.pageSize, { fields: fields })
} else {
request = new DataTableRequest<DmpCriteria>(this.offsetLess, this.pageSize, {fields: fields})
request = new DataTableRequest<DmpCriteria>(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;

View File

@ -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]);
}

View File

@ -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',

View File

@ -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)
);
});
}
});
}

View File

@ -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;

View File

@ -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;

View File

@ -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<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(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 => {

View File

@ -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<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(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'
});
}
});
}

View File

@ -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;

View File

@ -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"
}
}
}