Skip to content

Commit 06509a9

Browse files
odeimaizpcrespov
authored andcommitted
Study to template (#906)
Provides the tester user a way to convert studies into templates. closes #905 Frontend: - Create template: Provide an option in the study browser to convert a study into a template. - Update template metadata: Enable the textfields of a template if it belongs to that user. - Delete template: Provide an option in the study browser to delete a template if it belongs to that user. Backend: - Update the openapi specs to enable such a feature. (as_template) - Implement the Study -> Template converter - Only super-users allowed to create templates - cleanup all dta/project json resources
1 parent 12bdf2b commit 06509a9

File tree

17 files changed

+243
-1014
lines changed

17 files changed

+243
-1014
lines changed

api/specs/webserver/v0/openapi-projects.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ paths:
4949
schema:
5050
type: string
5151
description: 'Option to create a project from existing template: from_template={template_uuid}'
52+
- name: as_template
53+
in: query
54+
schema:
55+
type: string
56+
description: 'Option to create a template from existing project: as_template={study_uuid}'
5257
requestBody:
5358
content:
5459
application/json:

services/web/client/source/class/qxapp/data/Permissions.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ qx.Class.define("qxapp.data.Permissions", {
8383

8484
members: {
8585
__userRole: null,
86+
__userLogin: null,
8687

8788
getRole() {
8889
return this.__userRole;
@@ -95,6 +96,10 @@ qx.Class.define("qxapp.data.Permissions", {
9596
this.__userRole = role;
9697
},
9798

99+
getLogin() {
100+
return this.__userLogin;
101+
},
102+
98103
getChildrenRoles(role) {
99104
role = role.toLowerCase();
100105
const childrenRoles = [];
@@ -151,7 +156,10 @@ qx.Class.define("qxapp.data.Permissions", {
151156
"preferences.role.update",
152157
"study.nodestree.uuid.read",
153158
"study.filestree.uuid.read",
154-
"study.logger.debug.read"
159+
"study.logger.debug.read",
160+
"studies.template.create",
161+
"studies.template.update",
162+
"studies.template.delete"
155163
],
156164
"admin": []
157165
};
@@ -215,6 +223,7 @@ qx.Class.define("qxapp.data.Permissions", {
215223
profile.addListenerOnce("getSuccess", e => {
216224
let profileData = e.getRequest().getResponse().data;
217225
this.__userRole = profileData.role;
226+
this.__userLogin = profileData.login;
218227
this.fireDataEvent("userProfileRecieved", true);
219228
}, this);
220229
profile.addListenerOnce("getError", e => {

services/web/client/source/class/qxapp/desktop/StudyBrowser.js

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -513,13 +513,19 @@ qx.Class.define("qxapp.desktop.StudyBrowser", {
513513
});
514514
},
515515

516-
__createForm: function(studyData, fromTemplate) {
516+
__createForm: function(studyData, isTemplate) {
517517
while (this.__editStudyLayout.getChildren().length > 1) {
518518
this.__editStudyLayout.removeAt(1);
519519
}
520520

521+
const canCreateTemplate = qxapp.data.Permissions.getInstance().canDo("studies.template.create");
522+
const canUpdateTemplate = qxapp.data.Permissions.getInstance().canDo("studies.template.update");
523+
const canDeleteTemplate = qxapp.data.Permissions.getInstance().canDo("studies.template.delete");
524+
const isMyTemplate = studyData["prjOwner"] === qxapp.data.Permissions.getInstance().getLogin();
525+
521526
const itemsToBeDisplayed = ["name", "description", "thumbnail", "prjOwner", "creationDate", "lastChangeDate"];
522-
const itemsToBeModified = fromTemplate ? [] : ["name", "description", "thumbnail"];
527+
const itemsToBeModified = (isTemplate && !(canUpdateTemplate && isMyTemplate)) ? [] : ["name", "description", "thumbnail"];
528+
523529
let form = new qx.ui.form.Form();
524530
let control;
525531
for (const dataId in studyData) {
@@ -571,7 +577,7 @@ qx.Class.define("qxapp.desktop.StudyBrowser", {
571577
// buttons
572578
let saveButton = new qx.ui.form.Button(this.tr("Save"));
573579
saveButton.setMinWidth(70);
574-
saveButton.setEnabled(!fromTemplate);
580+
saveButton.setEnabled(!isTemplate || (canUpdateTemplate && isMyTemplate));
575581
saveButton.addListener("execute", e => {
576582
for (let i=0; i<itemsToBeModified.length; i++) {
577583
const key = itemsToBeModified[i];
@@ -582,7 +588,11 @@ qx.Class.define("qxapp.desktop.StudyBrowser", {
582588
let resource = this.__studyResources.project;
583589

584590
resource.addListenerOnce("putSuccess", ev => {
585-
this.reloadUserStudies();
591+
if (isTemplate) {
592+
this.reloadTemplateStudies();
593+
} else {
594+
this.reloadUserStudies();
595+
}
586596
}, this);
587597

588598
resource.put({
@@ -593,6 +603,35 @@ qx.Class.define("qxapp.desktop.StudyBrowser", {
593603
}, this);
594604
form.addButton(saveButton);
595605

606+
if (!isTemplate && canCreateTemplate) {
607+
const saveAsButton = new qx.ui.form.Button(this.tr("Save As Template"));
608+
saveAsButton.setMinWidth(70);
609+
610+
saveAsButton.addListener("execute", e => {
611+
for (let i=0; i<itemsToBeModified.length; i++) {
612+
const key = itemsToBeModified[i];
613+
let getter = "get" + qx.lang.String.firstUp(key);
614+
let newVal = model[getter]();
615+
studyData[key] = newVal;
616+
}
617+
618+
const resources = this.__studyResources.projects;
619+
620+
resources.addListenerOnce("postSaveAsTemplateSuccess", ev => {
621+
console.log(ev);
622+
this.reloadTemplateStudies();
623+
}, this);
624+
resources.addListenerOnce("postSaveAsTemplateError", ev => {
625+
console.error(ev);
626+
});
627+
resources.postSaveAsTemplate({
628+
"study_id": studyData["uuid"]
629+
}, studyData);
630+
}, this);
631+
632+
form.addButton(saveAsButton);
633+
}
634+
596635
let cancelButton = new qx.ui.form.Button(this.tr("Cancel"));
597636
cancelButton.setMinWidth(70);
598637
cancelButton.addListener("execute", e => {
@@ -602,14 +641,14 @@ qx.Class.define("qxapp.desktop.StudyBrowser", {
602641

603642
let deleteButton = new qx.ui.form.Button(this.tr("Delete"));
604643
deleteButton.setMinWidth(70);
605-
deleteButton.setEnabled(!fromTemplate);
644+
deleteButton.setEnabled(!isTemplate || (canDeleteTemplate && isMyTemplate));
606645
deleteButton.addListener("execute", e => {
607646
let win = this.__createConfirmWindow();
608647
win.center();
609648
win.open();
610649
win.addListener("close", () => {
611650
if (win["value"] === 1) {
612-
this.__deleteStudy(studyData);
651+
this.__deleteStudy(studyData, isTemplate);
613652
}
614653
}, this);
615654
}, this);
@@ -618,13 +657,17 @@ qx.Class.define("qxapp.desktop.StudyBrowser", {
618657
this.__editStudyLayout.add(new qx.ui.form.renderer.Single(form));
619658
},
620659

621-
__deleteStudy: function(studyData) {
660+
__deleteStudy: function(studyData, isTemplate = false) {
622661
this.__stopInteractiveServicesInStudy(studyData);
623662

624663
let resource = this.__studyResources.project;
625664

626665
resource.addListenerOnce("delSuccess", ev => {
627-
this.reloadUserStudies();
666+
if (isTemplate) {
667+
this.reloadTemplateStudies();
668+
} else {
669+
this.reloadUserStudies();
670+
}
628671
}, this);
629672

630673
resource.del({

services/web/client/source/class/qxapp/io/rest/ResourceFactory.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ qx.Class.define("qxapp.io.rest.ResourceFactory", {
126126
postFromTemplate: {
127127
method: "POST",
128128
url: basePath+"/projects?from_template={template_id}"
129+
},
130+
131+
postSaveAsTemplate: {
132+
method: "POST",
133+
url: basePath+"/projects?as_template={study_id}"
129134
}
130135
});
131136

0 commit comments

Comments
 (0)