1) update dmp blueprint listing table view, 2) create clone functionality for dmp blueprints, 3) section description is not required in editor, 4) in the deletion of a dmp blueprint check if any dmps are accosiated with it

This commit is contained in:
Bernaldo Mihasi 2023-10-04 09:43:16 +03:00
parent 2bf0a857bc
commit bc1894586b
17 changed files with 301 additions and 55 deletions

View File

@ -1,6 +1,7 @@
package eu.eudat.data.dao.criteria; package eu.eudat.data.dao.criteria;
import eu.eudat.data.entities.DMP; import eu.eudat.data.entities.DMP;
import eu.eudat.data.entities.DMPProfile;
import eu.eudat.data.entities.Grant; import eu.eudat.data.entities.Grant;
import java.util.Date; import java.util.Date;
@ -10,6 +11,7 @@ import java.util.UUID;
public class DataManagementPlanCriteria extends Criteria<DMP> { public class DataManagementPlanCriteria extends Criteria<DMP> {
private Date periodStart; private Date periodStart;
private Date periodEnd; private Date periodEnd;
private DMPProfile profile;
private List<eu.eudat.data.entities.Grant> grants; private List<eu.eudat.data.entities.Grant> grants;
private boolean allVersions; private boolean allVersions;
private List<UUID> groupIds; private List<UUID> groupIds;
@ -37,6 +39,13 @@ public class DataManagementPlanCriteria extends Criteria<DMP> {
this.periodEnd = periodEnd; this.periodEnd = periodEnd;
} }
public DMPProfile getProfile() {
return profile;
}
public void setProfile(DMPProfile profile) {
this.profile = profile;
}
public List<Grant> getGrants() { public List<Grant> getGrants() {
return grants; return grants;
} }

View File

@ -42,6 +42,8 @@ public class DMPDaoImpl extends DatabaseAccess<DMP> implements DMPDao {
query.where((builder, root) -> builder.lessThan(root.get("created"), criteria.getPeriodEnd())); query.where((builder, root) -> builder.lessThan(root.get("created"), criteria.getPeriodEnd()));
if (criteria.getPeriodStart() != null) if (criteria.getPeriodStart() != null)
query.where((builder, root) -> builder.greaterThan(root.get("created"), criteria.getPeriodStart())); query.where((builder, root) -> builder.greaterThan(root.get("created"), criteria.getPeriodStart()));
if (criteria.getProfile() != null)
query.where((builder, root) -> builder.equal(root.get("profile"), criteria.getProfile()));
if (criteria.getGrants() != null && !criteria.getGrants().isEmpty()) if (criteria.getGrants() != null && !criteria.getGrants().isEmpty())
query.where(((builder, root) -> root.get("grant").in(criteria.getGrants()))); query.where(((builder, root) -> root.get("grant").in(criteria.getGrants())));
if (!criteria.getAllVersions()) if (!criteria.getAllVersions())

View File

@ -15,6 +15,8 @@ public class DataManagementPlanBlueprintTableRequest extends TableQuery<DataMana
QueryableList<DMPProfile> query = this.getQuery(); QueryableList<DMPProfile> query = this.getQuery();
if (this.getCriteria().getLike() != null && !this.getCriteria().getLike().isEmpty()) if (this.getCriteria().getLike() != null && !this.getCriteria().getLike().isEmpty())
query.where((builder, root) -> builder.like(root.get("label"), "%" + this.getCriteria().getLike() + "%")); query.where((builder, root) -> builder.like(root.get("label"), "%" + this.getCriteria().getLike() + "%"));
if (this.getCriteria().getStatus() != null)
query.where((builder, root) -> builder.equal(root.get("status"), this.getCriteria().getStatus()));
return query; return query;
} }

View File

@ -5,6 +5,7 @@ import eu.eudat.data.entities.DMPProfile;
import eu.eudat.data.entities.DescriptionTemplate; import eu.eudat.data.entities.DescriptionTemplate;
import eu.eudat.data.query.items.dmpblueprint.DataManagementPlanBlueprintTableRequest; import eu.eudat.data.query.items.dmpblueprint.DataManagementPlanBlueprintTableRequest;
import eu.eudat.data.query.items.table.dmpprofile.DataManagementPlanProfileTableRequest; import eu.eudat.data.query.items.table.dmpprofile.DataManagementPlanProfileTableRequest;
import eu.eudat.exceptions.dmpblueprint.DmpBlueprintUsedException;
import eu.eudat.logic.managers.DataManagementProfileManager; import eu.eudat.logic.managers.DataManagementProfileManager;
import eu.eudat.logic.security.claims.ClaimedAuthorities; import eu.eudat.logic.security.claims.ClaimedAuthorities;
import eu.eudat.logic.services.ApiContext; import eu.eudat.logic.services.ApiContext;
@ -29,6 +30,7 @@ import java.io.IOException;
import java.util.List; import java.util.List;
import static eu.eudat.types.Authorities.ADMIN; import static eu.eudat.types.Authorities.ADMIN;
import static eu.eudat.types.Authorities.DATASET_PROFILE_MANAGER;
/** /**
* Created by ikalyvas on 3/21/2018. * Created by ikalyvas on 3/21/2018.
@ -71,7 +73,7 @@ public class DMPProfileController extends BaseController {
@RequestMapping(method = RequestMethod.GET, value = {"/getSingleBlueprint/{id}"}, produces = "application/json") @RequestMapping(method = RequestMethod.GET, value = {"/getSingleBlueprint/{id}"}, produces = "application/json")
public @ResponseBody public @ResponseBody
ResponseEntity<ResponseItem<DataManagementPlanBlueprintListingModel>> getSingleBlueprint(@PathVariable String id, Principal principal) throws IllegalAccessException, InstantiationException { ResponseEntity<ResponseItem<DataManagementPlanBlueprintListingModel>> getSingleBlueprint(@PathVariable String id, Principal principal) {
DataManagementPlanBlueprintListingModel dataManagementPlanBlueprintListingModel = this.dataManagementProfileManager.getSingleBlueprint(id, principal); DataManagementPlanBlueprintListingModel dataManagementPlanBlueprintListingModel = this.dataManagementProfileManager.getSingleBlueprint(id, principal);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DataManagementPlanBlueprintListingModel>().status(ApiMessageCode.NO_MESSAGE).payload(dataManagementPlanBlueprintListingModel)); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DataManagementPlanBlueprintListingModel>().status(ApiMessageCode.NO_MESSAGE).payload(dataManagementPlanBlueprintListingModel));
} }
@ -90,6 +92,26 @@ public class DMPProfileController extends BaseController {
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DataTableData<DataManagementPlanBlueprintListingModel>>().status(ApiMessageCode.NO_MESSAGE).payload(dataTable)); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DataTableData<DataManagementPlanBlueprintListingModel>>().status(ApiMessageCode.NO_MESSAGE).payload(dataTable));
} }
@Transactional
@RequestMapping(method = RequestMethod.POST, value = {"/clone/{id}"}, consumes = "application/json", produces = "application/json")
public ResponseEntity<ResponseItem<DataManagementPlanBlueprintListingModel>> clone(@PathVariable String id, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) {
DataManagementPlanBlueprintListingModel dmpBlueprint = this.dataManagementProfileManager.getSingleBlueprint(id, principal);
dmpBlueprint.setLabel(dmpBlueprint.getLabel() + " new ");
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DataManagementPlanBlueprintListingModel>().payload(dmpBlueprint));
}
@Transactional
@RequestMapping(method = RequestMethod.DELETE, value = {"{id}"}, consumes = "application/json", produces = "application/json")
public @ResponseBody
ResponseEntity<ResponseItem<Void>> inactivate(@PathVariable String id, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) {
try {
this.dataManagementProfileManager.inactivate(id);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<Void>().status(ApiMessageCode.SUCCESS_MESSAGE));
} catch (DmpBlueprintUsedException exception) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem<Void>().status(ApiMessageCode.UNSUCCESS_DELETE).message(exception.getMessage()));
}
}
@RequestMapping(method = RequestMethod.GET, value = {"/getXml/{id}"}, produces = "application/json") @RequestMapping(method = RequestMethod.GET, value = {"/getXml/{id}"}, produces = "application/json")
public @ResponseBody public @ResponseBody
ResponseEntity getXml( @RequestHeader("Content-Type") String contentType, @PathVariable String id, Principal principal) throws IOException { ResponseEntity getXml( @RequestHeader("Content-Type") String contentType, @PathVariable String id, Principal principal) throws IOException {

View File

@ -0,0 +1,18 @@
package eu.eudat.exceptions.dmpblueprint;
public class DmpBlueprintUsedException extends RuntimeException {
public DmpBlueprintUsedException() {
}
public DmpBlueprintUsedException(String message) {
super(message);
}
public DmpBlueprintUsedException(String message, Throwable cause) {
super(message, cause);
}
public DmpBlueprintUsedException(Throwable cause) {
super(cause);
}
}

View File

@ -2,11 +2,17 @@ package eu.eudat.logic.managers;
import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.JsonPath;
import eu.eudat.data.dao.criteria.DataManagementPlanCriteria;
import eu.eudat.data.dao.criteria.RequestItem; import eu.eudat.data.dao.criteria.RequestItem;
import eu.eudat.data.dao.entities.DatasetDao;
import eu.eudat.data.dao.entities.DatasetProfileDao;
import eu.eudat.data.entities.DMPProfile; import eu.eudat.data.entities.DMPProfile;
import eu.eudat.data.entities.DescriptionTemplate;
import eu.eudat.data.query.items.dmpblueprint.DataManagementPlanBlueprintTableRequest; import eu.eudat.data.query.items.dmpblueprint.DataManagementPlanBlueprintTableRequest;
import eu.eudat.data.query.items.item.dmpprofile.DataManagementPlanProfileCriteriaRequest; import eu.eudat.data.query.items.item.dmpprofile.DataManagementPlanProfileCriteriaRequest;
import eu.eudat.data.query.items.table.dmpprofile.DataManagementPlanProfileTableRequest; import eu.eudat.data.query.items.table.dmpprofile.DataManagementPlanProfileTableRequest;
import eu.eudat.exceptions.datasetprofile.DatasetProfileWithDatasetsExeption;
import eu.eudat.exceptions.dmpblueprint.DmpBlueprintUsedException;
import eu.eudat.logic.services.operations.DatabaseRepository; import eu.eudat.logic.services.operations.DatabaseRepository;
import eu.eudat.logic.utilities.builders.XmlBuilder; import eu.eudat.logic.utilities.builders.XmlBuilder;
import eu.eudat.logic.utilities.documents.helpers.FileEnvelope; import eu.eudat.logic.utilities.documents.helpers.FileEnvelope;
@ -145,6 +151,18 @@ public class DataManagementProfileManager {
apiContext.getOperationsContext().getDatabaseRepository().getDmpProfileDao().createOrUpdate(dmpProfile); apiContext.getOperationsContext().getDatabaseRepository().getDmpProfileDao().createOrUpdate(dmpProfile);
} }
public void inactivate(String id) {
DMPProfile dmpProfile = databaseRepository.getDmpProfileDao().find(UUID.fromString(id));
DataManagementPlanCriteria dataManagementPlanCriteria = new DataManagementPlanCriteria();
dataManagementPlanCriteria.setProfile(dmpProfile);
if (dmpProfile.getStatus() == DMPProfile.Status.SAVED.getValue() || databaseRepository.getDmpDao().getWithCriteria(dataManagementPlanCriteria).count() == 0) {
dmpProfile.setStatus(DMPProfile.Status.DELETED.getValue());
databaseRepository.getDmpProfileDao().createOrUpdate(dmpProfile);
} else {
throw new DmpBlueprintUsedException("This blueprint can not deleted, because DMPs are associated with it");
}
}
public ResponseEntity<byte[]> getDocument(DataManagementPlanBlueprintListingModel dmpProfile) throws IOException { public ResponseEntity<byte[]> getDocument(DataManagementPlanBlueprintListingModel dmpProfile) throws IOException {
FileEnvelope envelope = getXmlDocument(dmpProfile); FileEnvelope envelope = getXmlDocument(dmpProfile);
InputStream resource = new FileInputStream(envelope.getFile()); InputStream resource = new FileInputStream(envelope.getFile());

View File

@ -67,6 +67,14 @@ export class DmpProfileService {
return this.http.post(this.actionUrl + "upload", formData, { params: params }); return this.http.post(this.actionUrl + "upload", formData, { params: params });
} }
clone(id: string): Observable<DmpBlueprint> {
return this.http.post<DmpBlueprint>(this.actionUrl + 'clone/' + id, { headers: this.headers });
}
delete(id: string): Observable<any> {
return this.http.delete<any>(this.actionUrl + id, { headers: this.headers });
}
externalAutocomplete(lookUpItem: RequestItem<DmpProfileExternalAutocompleteCriteria>): Observable<any> { externalAutocomplete(lookUpItem: RequestItem<DmpProfileExternalAutocompleteCriteria>): Observable<any> {
return this.httpClient.post(this.actionUrl + 'search/autocomplete', lookUpItem); return this.httpClient.post(this.actionUrl + 'search/autocomplete', lookUpItem);
} }

View File

@ -8,6 +8,7 @@ import { AdminAuthGuard } from '@app/core/admin-auth-guard.service';
const routes: Routes = [ const routes: Routes = [
{ path: '', component: DmpProfileListingComponent, canActivate: [AdminAuthGuard] }, { path: '', component: DmpProfileListingComponent, canActivate: [AdminAuthGuard] },
{ path: 'new', component: DmpProfileEditorComponent, canActivate: [AdminAuthGuard], data: { title: 'GENERAL.TITLES.DMP-BLUEPRINT-NEW' } }, { path: 'new', component: DmpProfileEditorComponent, canActivate: [AdminAuthGuard], data: { title: 'GENERAL.TITLES.DMP-BLUEPRINT-NEW' } },
{ path: 'clone/:cloneid', component: DmpProfileEditorComponent, canActivate: [AdminAuthGuard], data: { title: 'GENERAL.TITLES.DMP-BLUEPRINT-CLONE' } },
{ path: ':id', component: DmpProfileEditorComponent, canActivate: [AdminAuthGuard], data: { title: 'GENERAL.TITLES.DMP-BLUEPRINT-EDIT' } }, { path: ':id', component: DmpProfileEditorComponent, canActivate: [AdminAuthGuard], data: { title: 'GENERAL.TITLES.DMP-BLUEPRINT-EDIT' } },
]; ];

View File

@ -121,7 +121,7 @@ export class SectionDmpBlueprintEditor {
const baseContext: ValidationContext = new ValidationContext(); const baseContext: ValidationContext = new ValidationContext();
baseContext.validation.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] }); baseContext.validation.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
baseContext.validation.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] }); baseContext.validation.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] });
baseContext.validation.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] }); baseContext.validation.push({ key: 'description', validators: [BackendErrorValidator(this.validationErrorModel, 'description')] });
baseContext.validation.push({ key: 'ordinal', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'ordinal')] }); baseContext.validation.push({ key: 'ordinal', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'ordinal')] });
baseContext.validation.push({ key: 'hasTemplates', validators: [BackendErrorValidator(this.validationErrorModel, 'hasTemplates')] }); baseContext.validation.push({ key: 'hasTemplates', validators: [BackendErrorValidator(this.validationErrorModel, 'hasTemplates')] });
baseContext.validation.push({ key: 'descriptionTemplates', validators: [BackendErrorValidator(this.validationErrorModel, 'descriptionTemplates')] }); baseContext.validation.push({ key: 'descriptionTemplates', validators: [BackendErrorValidator(this.validationErrorModel, 'descriptionTemplates')] });

View File

@ -2,7 +2,11 @@
<div class="container-fluid dmp-profile-editor"> <div class="container-fluid dmp-profile-editor">
<div class="row align-items-center mb-4" *ngIf="formGroup"> <div class="row align-items-center mb-4" *ngIf="formGroup">
<div class="col-auto"> <div class="col-auto">
<h3 *ngIf="isNew">{{'DMP-PROFILE-EDITOR.TITLE.NEW' | translate}}</h3> <h3 *ngIf="isNew && !isClone">{{'DMP-PROFILE-EDITOR.TITLE.NEW' | translate}}</h3>
<h3 *ngIf="isNew && isClone">
<span>{{'DMP-PROFILE-EDITOR.TITLE.NEW-PROFILE-CLONE' | translate}}</span>
{{formGroup.get('label').value}}
</h3>
<h3 *ngIf="!isNew">{{formGroup.get('label').value}}</h3> <h3 *ngIf="!isNew">{{formGroup.get('label').value}}</h3>
</div> </div>
<div class="col"></div> <div class="col"></div>
@ -65,7 +69,7 @@
<div class="col-6"> <div class="col-6">
<mat-form-field> <mat-form-field>
<mat-label>Section description</mat-label> <mat-label>Section description</mat-label>
<input matInput type="text" name="description" formControlName="description" required> <input matInput type="text" name="description" formControlName="description">
<mat-error *ngIf="section.get('description').hasError('required')"> <mat-error *ngIf="section.get('description').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> {{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>

View File

@ -49,6 +49,7 @@ import { FormValidationErrorsDialogComponent } from '@common/forms/form-validati
export class DmpProfileEditorComponent extends BaseComponent implements AfterViewInit { export class DmpProfileEditorComponent extends BaseComponent implements AfterViewInit {
isNew = true; isNew = true;
isClone = false;
viewOnly = false; viewOnly = false;
dmpProfileModel: DmpProfileEditorModel; dmpProfileModel: DmpProfileEditorModel;
dmpBlueprintModel: DmpBlueprintEditor; dmpBlueprintModel: DmpBlueprintEditor;
@ -112,6 +113,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
.pipe(takeUntil(this._destroyed)) .pipe(takeUntil(this._destroyed))
.subscribe((params: Params) => { .subscribe((params: Params) => {
this.dmpProfileId = params['id']; this.dmpProfileId = params['id'];
const cloneId = params['cloneid'];
if (this.dmpProfileId != null) { if (this.dmpProfileId != null) {
this.isNew = false; this.isNew = false;
@ -132,6 +134,20 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
url: '/dmp-profiles/' + this.dmpProfileId url: '/dmp-profiles/' + this.dmpProfileId
}]); }]);
}); });
} else if (cloneId != null) {
this.isClone = true;
this.dmpProfileService.clone(cloneId).pipe(map(data => data as DmpBlueprint), takeUntil(this._destroyed))
.subscribe(
data => {
this.dmpBlueprintModel = new DmpBlueprintEditor().fromModel(data);
this.dmpBlueprintModel.id = null;
this.dmpBlueprintModel.status = DmpProfileStatus.Draft;
this.formGroup = this.dmpBlueprintModel.buildForm();
this.buildSystemFields();
this.fillDescriptionTemplatesInMultAutocomplete();
},
error => this.onCallbackError(error)
);
} else { } else {
this.dmpProfileModel = new DmpProfileEditorModel(); this.dmpProfileModel = new DmpProfileEditorModel();
this.dmpBlueprintModel = new DmpBlueprintEditor(); this.dmpBlueprintModel = new DmpBlueprintEditor();
@ -553,6 +569,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
.subscribe( .subscribe(
confirmed =>{ confirmed =>{
if(confirmed){ if(confirmed){
if(this.formGroup.get('status').value == DmpProfileStatus.Draft) {
this.formGroup.get('status').setValue(DmpProfileStatus.Deleted); this.formGroup.get('status').setValue(DmpProfileStatus.Deleted);
this.dmpProfileService.createBlueprint(this.formGroup.value) this.dmpProfileService.createBlueprint(this.formGroup.value)
.pipe(takeUntil(this._destroyed)) .pipe(takeUntil(this._destroyed))
@ -561,6 +578,21 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
error => this.onCallbackError(error) error => this.onCallbackError(error)
); );
} }
else {
this.dmpProfileService.delete(this.dmpProfileId)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
error => {
if (error.error.statusCode == 674) {
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DMP-BLUEPRINT-DELETE'), SnackBarNotificationLevel.Error);
} else {
this.uiNotificationService.snackBarNotification(this.language.instant(error.message), SnackBarNotificationLevel.Error);
}
}
);
}
}
} }
) )

View File

@ -1,11 +1,23 @@
<div class="dmp-criteria"> <div class="row align-items-end">
<div class="row justify-content-end"> <div class="col-auto">
<div class="col-auto search-container"> <div class="d-lg-inline-block pr-2 text-muted">
<span>{{'CRITERIA.USERS.SHOW' | translate}}:</span>
</div>
<mat-form-field class="status-form-field">
<mat-select [(ngModel)]="criteria.status" (ngModelChange)="controlModified()" placeholder=" {{'CRITERIA.BLUEPRINT.STATUS' | translate}}">
<mat-option [value]="null">{{'BLUEPRINT-STATUS.NONE' | translate}}</mat-option>
<mat-option [value]="0">{{'BLUEPRINT-STATUS.DRAFT' | translate}}</mat-option>
<mat-option [value]="1">{{'BLUEPRINT-STATUS.FINALIZED' | translate}}</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="col-auto ml-lg-auto">
<mat-form-field class="search-form-field"> <mat-form-field class="search-form-field">
<input matInput placeholder=" {{'CRITERIA.DMP.LIKE'| translate}}" name="grantCriteriaLike" <input matInput placeholder=" {{'CRITERIA.BLUEPRINT.LIKE'| translate}}" name="dmpBlueprintLike"
[(ngModel)]="criteria.like" (ngModelChange)="controlModified()"> [(ngModel)]="criteria.like" (ngModelChange)="controlModified()">
<mat-icon matPrefix>search</mat-icon> <mat-icon matPrefix>search</mat-icon>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</div>

View File

@ -1,13 +1,17 @@
.mat-form-field{
display: inline-block !important;
}
:host ::ng-deep .status-form-field .mat-form-field-wrapper {
background-color: white !important;
padding-bottom: 0 !important;
}
:host ::ng-deep .search-form-field .mat-form-field-wrapper { :host ::ng-deep .search-form-field .mat-form-field-wrapper {
background-color: white !important; background-color: white !important;
padding-bottom: 0 !important; padding-bottom: 0 !important;
} }
:host ::ng-deep .search-container .mat-form-field-appearance-outline .mat-form-field-infix {
:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix {
padding: 0.3rem 0rem 0.6rem 0rem !important; padding: 0.3rem 0rem 0.6rem 0rem !important;
} }
.dmp-criteria{
margin-top: 3em;
margin-bottom: 0em;
}

View File

@ -1,14 +1,10 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { GrantListingModel } from '@app/core/model/grant/grant-listing'; import { DmpBlueprintCriteria } from '@app/core/query/dmp/dmp-blueprint-criteria';
import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria';
import { DmpProfileCriteria } from '@app/core/query/dmp/dmp-profile-criteria';
import { DmpProfileService } from '@app/core/services/dmp/dmp-profile.service'; import { DmpProfileService } from '@app/core/services/dmp/dmp-profile.service';
import { DialodConfirmationUploadDmpProfiles } from '@app/ui/admin/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component';
import { BaseCriteriaComponent } from '@app/ui/misc/criteria/base-criteria.component'; import { BaseCriteriaComponent } from '@app/ui/misc/criteria/base-criteria.component';
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'app-dmp-profile-criteria-component', selector: 'app-dmp-profile-criteria-component',
@ -17,11 +13,8 @@ import { takeUntil } from 'rxjs/operators';
}) })
export class DmpProfileCriteriaComponent extends BaseCriteriaComponent implements OnInit { export class DmpProfileCriteriaComponent extends BaseCriteriaComponent implements OnInit {
@Input() public criteria: DmpBlueprintCriteria = new DmpBlueprintCriteria();
showGrant: boolean;
public criteria: DmpProfileCriteria = new DmpProfileCriteria();
filteringGrantsAsync = false;
filteredGrants: GrantListingModel[];
constructor( constructor(
private dmpProfileService: DmpProfileService, private dmpProfileService: DmpProfileService,
private dialog: MatDialog, private dialog: MatDialog,
@ -32,10 +25,10 @@ export class DmpProfileCriteriaComponent extends BaseCriteriaComponent implement
ngOnInit() { ngOnInit() {
super.ngOnInit(); super.ngOnInit();
if (this.criteria == null) { this.criteria = new DmpCriteria(); } if (this.criteria == null) { this.criteria = new DmpBlueprintCriteria(); }
} }
setCriteria(criteria: DmpProfileCriteria): void { setCriteria(criteria: DmpBlueprintCriteria): void {
this.criteria = criteria; this.criteria = criteria;
} }

View File

@ -19,9 +19,14 @@
</div> </div>
</div> </div>
<div class="row">
<div class="col-12 mt-5">
<app-dmp-profile-criteria-component></app-dmp-profile-criteria-component> <app-dmp-profile-criteria-component></app-dmp-profile-criteria-component>
</div>
</div>
<div class="mat-elevation-z6"> <div class="mat-elevation-z6">
<mat-table [dataSource]="dataSource" matSort (matSortChange)="refresh()"> <mat-table [dataSource]="dataSource" matSort (matSortChange)="refresh()" matSortActive="created" matSortDirection="desc">
<!-- Column Definition: Name --> <!-- Column Definition: Name -->
<ng-container cdkColumnDef="label"> <ng-container cdkColumnDef="label">
@ -30,6 +35,13 @@
<mat-cell *matCellDef="let row">{{row.label}}</mat-cell> <mat-cell *matCellDef="let row">{{row.label}}</mat-cell>
</ng-container> </ng-container>
<!-- Column Definition: Created -->
<ng-container cdkColumnDef="created">
<mat-header-cell *matHeaderCellDef mat-sort-header="created">
{{'DMP-PROFILE-LISTING.COLUMNS.CREATED' | translate}}</mat-header-cell>
<mat-cell *matCellDef="let row">{{row.created | date:'short'}}</mat-cell>
</ng-container>
<!-- Column Definition: status --> <!-- Column Definition: status -->
<ng-container cdkColumnDef="status"> <ng-container cdkColumnDef="status">
<mat-header-cell *matHeaderCellDef mat-sort-header="status"> <mat-header-cell *matHeaderCellDef mat-sort-header="status">
@ -41,14 +53,29 @@
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Column Definition: Created -->
<ng-container cdkColumnDef="created">
<mat-header-cell *matHeaderCellDef mat-sort-header="created">
{{'DMP-PROFILE-LISTING.COLUMNS.CREATED' | translate}}</mat-header-cell>
<mat-cell *matCellDef="let row">{{row.created | date:'shortDate'}}</mat-cell>
</ng-container>
<!-- Column Definition: Submission Time --> <!-- Column Definition: Submission Time -->
<ng-container cdkColumnDef="actions">
<mat-header-cell *matHeaderCellDef><!-- {{'DATASET-PROFILE-LISTING.COLUMNS.ACTIONS' | translate}} -->
</mat-header-cell>
<mat-cell *matCellDef="let row" (click)="$event.stopPropagation()">
<mat-menu #actionsMenu="matMenu">
<button mat-menu-item (click)="clone(row.id)">
<mat-icon>filter_none</mat-icon>{{'DMP-PROFILE-LISTING.ACTIONS.CLONE' | translate}}
</button>
<button mat-menu-item (click)="downloadXML(row.id)" *ngIf="row.status === dmpBlueprintStatus.Finalized">
<mat-icon>download</mat-icon>
{{'DMP-PROFILE-LISTING.ACTIONS.DOWNLOAD-XML' | translate}}
</button>
<button mat-menu-item (click)="deleteTemplate(row.id)">
<mat-icon>delete</mat-icon>
{{'DMP-PROFILE-LISTING.ACTIONS.DELETE' | translate}}
</button>
</mat-menu>
<button mat-icon-button [matMenuTriggerFor]="actionsMenu">
<mat-icon>more_horiz</mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns" (click)="rowClick(row.id)"></mat-row> <mat-row *matRowDef="let row; columns: displayedColumns" (click)="rowClick(row.id)"></mat-row>

View File

@ -1,6 +1,6 @@
import { DataSource } from '@angular/cdk/table'; import { DataSource } from '@angular/cdk/table';
import { HttpClient } from '@angular/common/http'; import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { MatPaginator, PageEvent } from '@angular/material/paginator';
@ -21,6 +21,11 @@ import { TranslateService } from '@ngx-translate/core';
import { merge as observableMerge, Observable, of as observableOf } from 'rxjs'; import { merge as observableMerge, Observable, of as observableOf } from 'rxjs';
import { map, startWith, switchMap, takeUntil } from 'rxjs/operators'; import { map, startWith, switchMap, takeUntil } from 'rxjs/operators';
import { DialodConfirmationUploadDmpProfiles } from './criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component'; import { DialodConfirmationUploadDmpProfiles } from './criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component';
import { DmpBlueprintCriteria } from '@app/core/query/dmp/dmp-blueprint-criteria';
import { DmpBlueprintListing } from '@app/core/model/dmp/dmp-blueprint/dmp-blueprint-listing';
import { DmpProfileStatus } from '@app/core/common/enum/dmp-profile-status';
import * as FileSaver from 'file-saver';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
@Component({ @Component({
@ -35,11 +40,12 @@ export class DmpProfileListingComponent extends BaseComponent implements OnInit
@ViewChild(DmpProfileCriteriaComponent, { static: true }) criteria: DmpProfileCriteriaComponent; @ViewChild(DmpProfileCriteriaComponent, { static: true }) criteria: DmpProfileCriteriaComponent;
dataSource: DatasetDataSource | null; dataSource: DatasetDataSource | null;
displayedColumns: String[] = ['label', 'status', 'created']; displayedColumns: String[] = ['label', 'created', 'status', 'actions'];
pageEvent: PageEvent; pageEvent: PageEvent;
titlePrefix: String; titlePrefix: String;
dmpId: String; dmpId: String;
breadCrumbs: Observable<BreadcrumbItem[]>; breadCrumbs: Observable<BreadcrumbItem[]>;
dmpBlueprintStatus = DmpProfileStatus;
statuses = [ statuses = [
{ value: '0', viewValue: 'DMP-PROFILE-LISTING.STATUS.DRAFT' },// active { value: '0', viewValue: 'DMP-PROFILE-LISTING.STATUS.DRAFT' },// active
@ -81,12 +87,81 @@ export class DmpProfileListingComponent extends BaseComponent implements OnInit
this.dataSource = new DatasetDataSource(this.dmpProfileService, this._paginator, this.sort, this.criteria); this.dataSource = new DatasetDataSource(this.dmpProfileService, this._paginator, this.sort, this.criteria);
} }
clone(id: string) {
this.router.navigate(['dmp-profiles/clone/' + id]);
}
rowClick(rowId: String) { rowClick(rowId: String) {
this.router.navigate(['dmp-profiles/' + rowId]); this.router.navigate(['dmp-profiles/' + rowId]);
} }
getDefaultCriteria(): DmpProfileCriteria { downloadXML(dmpProfileId: string): void {
const defaultCriteria = new DmpProfileCriteria(); this.dmpProfileService.downloadXML(dmpProfileId)
.pipe(takeUntil(this._destroyed))
.subscribe(response => {
const blob = new Blob([response.body], { type: 'application/xml' });
const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition'));
FileSaver.saveAs(blob, filename);
});
}
getFilenameFromContentDispositionHeader(header: string): string {
const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g);
const matches = header.match(regex);
let filename: string;
for (let i = 0; i < matches.length; i++) {
const match = matches[i];
if (match.includes('filename="')) {
filename = match.substring(10, match.length - 1);
break;
} else if (match.includes('filename=')) {
filename = match.substring(9);
break;
}
}
return filename;
}
deleteTemplate(id: string) {
if (id) {
this.dialog.open(ConfirmationDialogComponent,{data:{
isDeleteConfirmation: true,
confirmButton: this.languageService.instant('DMP-PROFILE-EDITOR.CONFIRM-DELETE-DIALOG.CONFIRM-BUTTON'),
cancelButton: this.languageService.instant("DMP-PROFILE-EDITOR.CONFIRM-DELETE-DIALOG.CANCEL-BUTTON"),
message: this.languageService.instant("DMP-PROFILE-EDITOR.CONFIRM-DELETE-DIALOG.MESSAGE")
}})
.afterClosed()
.subscribe(
confirmed =>{
if(confirmed){
this.dmpProfileService.delete(id)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => {
this.uiNotificationService.snackBarNotification(this.languageService.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DMP-BLUEPRINT-DELETE'), SnackBarNotificationLevel.Success);
this.refresh();
},
error => {
this.onCallbackError(error);
if (error.error.statusCode == 674) {
this.uiNotificationService.snackBarNotification(this.languageService.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DMP-BLUEPRINT-DELETE'), SnackBarNotificationLevel.Error);
} else {
this.uiNotificationService.snackBarNotification(this.languageService.instant(error.message), SnackBarNotificationLevel.Error);
}
}
);
}
}
)
}
}
onCallbackError(errorResponse: HttpErrorResponse) {
this.uiNotificationService.snackBarNotification(errorResponse.message, SnackBarNotificationLevel.Warning);
}
getDefaultCriteria(): DmpBlueprintCriteria {
const defaultCriteria = new DmpBlueprintCriteria();
return defaultCriteria; return defaultCriteria;
} }
@ -142,7 +217,7 @@ export class DmpProfileListingComponent extends BaseComponent implements OnInit
} }
} }
export class DatasetDataSource extends DataSource<DmpProfileListing> { export class DatasetDataSource extends DataSource<DmpBlueprintListing> {
totalCount = 0; totalCount = 0;
@ -156,7 +231,7 @@ export class DatasetDataSource extends DataSource<DmpProfileListing> {
} }
connect(): Observable<DmpProfileListing[]> { connect(): Observable<DmpBlueprintListing[]> {
const displayDataChanges = [ const displayDataChanges = [
this._paginator.page this._paginator.page
//this._sort.matSortChange //this._sort.matSortChange
@ -168,9 +243,9 @@ export class DatasetDataSource extends DataSource<DmpProfileListing> {
const startIndex = this._paginator.pageIndex * this._paginator.pageSize; const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
let fields: Array<string> = new Array(); let fields: Array<string> = new Array();
if (this._sort.active) { fields = this._sort.direction === 'asc' ? ['+' + this._sort.active] : ['-' + this._sort.active]; } if (this._sort.active) { fields = this._sort.direction === 'asc' ? ['+' + this._sort.active] : ['-' + this._sort.active]; }
const request = new DataTableRequest<DmpProfileCriteria>(startIndex, this._paginator.pageSize, { fields: fields }); const request = new DataTableRequest<DmpBlueprintCriteria>(startIndex, this._paginator.pageSize, { fields: fields });
request.criteria = this._criteria.criteria; request.criteria = this._criteria.criteria;
return this._service.getPaged(request); return this._service.getPagedBlueprint(request);
}), }),
/*.catch((error: any) => { /*.catch((error: any) => {
this._snackBar.openFromComponent(SnackBarNotificationComponent, { this._snackBar.openFromComponent(SnackBarNotificationComponent, {

View File

@ -28,6 +28,8 @@
"UNSUCCESSFUL-LOGIN": "Unsuccessful Login", "UNSUCCESSFUL-LOGIN": "Unsuccessful Login",
"SUCCESSFUL-DATASET-PROFILE-DELETE": "Successful Delete", "SUCCESSFUL-DATASET-PROFILE-DELETE": "Successful Delete",
"UNSUCCESSFUL-DATASET-PROFILE-DELETE": "This template can not deleted, because Datasets are associated with it", "UNSUCCESSFUL-DATASET-PROFILE-DELETE": "This template can not deleted, because Datasets are associated with it",
"SUCCESSFUL-DMP-BLUEPRINT-DELETE": "Successful Delete",
"UNSUCCESSFUL-DMP-BLUEPRINT-DELETE": "This blueprint can not deleted, because DMPs are associated with it",
"SUCCESSFUL-DESCRIPTION-TEMPLATE-TYPE-DELETE": "Successful Delete", "SUCCESSFUL-DESCRIPTION-TEMPLATE-TYPE-DELETE": "Successful Delete",
"UNSUCCESSFUL-DESCRIPTION-TEMPLATE-TYPE-DELETE": "This type can not deleted, because Descriptions are associated with it", "UNSUCCESSFUL-DESCRIPTION-TEMPLATE-TYPE-DELETE": "This type can not deleted, because Descriptions are associated with it",
"SUCCESSFUL-DELETE": "Successful Delete", "SUCCESSFUL-DELETE": "Successful Delete",
@ -155,6 +157,7 @@
"GRANT-NEW": "New Grant", "GRANT-NEW": "New Grant",
"GRANT-EDIT": "View/Edit Grant", "GRANT-EDIT": "View/Edit Grant",
"DMP-BLUEPRINT-NEW": "New DMP Blueprint", "DMP-BLUEPRINT-NEW": "New DMP Blueprint",
"DMP-BLUEPRINT-CLONE": "New Clone of DMP Blueprint",
"DMP-BLUEPRINT-EDIT": "Edit DMP Blueprint", "DMP-BLUEPRINT-EDIT": "Edit DMP Blueprint",
"DATASET-PROFILES-NEW": "New Dataset Template", "DATASET-PROFILES-NEW": "New Dataset Template",
"DATASET-PROFILES-EDIT": "Edit Dataset Template", "DATASET-PROFILES-EDIT": "Edit Dataset Template",
@ -999,6 +1002,7 @@
"DMP-PROFILE-EDITOR": { "DMP-PROFILE-EDITOR": {
"TITLE": { "TITLE": {
"NEW": "New DMP Blueprint", "NEW": "New DMP Blueprint",
"NEW-PROFILE-CLONE": "New Clone Of ",
"EDIT": "Edit" "EDIT": "Edit"
}, },
"FIELDS": { "FIELDS": {
@ -1203,6 +1207,11 @@
"PUBLISHED": "Published", "PUBLISHED": "Published",
"LAST-EDITED": "Last Edited" "LAST-EDITED": "Last Edited"
}, },
"ACTIONS": {
"CLONE": "Clone",
"DOWNLOAD-XML":"Download XML",
"DELETE": "Delete"
},
"UPLOAD": { "UPLOAD": {
"UPLOAD-XML": "Import", "UPLOAD-XML": "Import",
"UPLOAD-XML-FILE-TITLE": "Import Data Management Plan Template", "UPLOAD-XML-FILE-TITLE": "Import Data Management Plan Template",
@ -1277,6 +1286,10 @@
"SELECT-DATASET-TEMPLATES": "Select Dataset Templates", "SELECT-DATASET-TEMPLATES": "Select Dataset Templates",
"RELATED-DATASET-TEMPLATES": "Related Dataset Templates" "RELATED-DATASET-TEMPLATES": "Related Dataset Templates"
}, },
"BLUEPRINT": {
"LIKE": "Search",
"STATUS": "Status"
},
"USERS": { "USERS": {
"LABEL": "Search", "LABEL": "Search",
"ROLE": "Role", "ROLE": "Role",
@ -1949,5 +1962,11 @@
"DRAFT": "Draft", "DRAFT": "Draft",
"FINALIZED": "Finalized", "FINALIZED": "Finalized",
"DELETED": "Deleted" "DELETED": "Deleted"
},
"BLUEPRINT-STATUS": {
"NONE": "None",
"DRAFT": "Draft",
"FINALIZED": "Finalized",
"DELETED": "Deleted"
} }
} }