]> git.argeo.org Git - gpl/argeo-slc.git/blob - FlowsSelectorView.js
d15b71a1d265a3310c0cdbb57f97580d422f182c
[gpl/argeo-slc.git] / FlowsSelectorView.js
1 /**
2 * The selector view
3 *
4 */
5 qx.Class.define("org.argeo.slc.ria.FlowsSelectorView", {
6 extend : qx.ui.container.Composite,
7 implement : [org.argeo.ria.components.IView],
8 include : [org.argeo.ria.session.MPrefHolder],
9
10 construct : function() {
11 this.base(arguments);
12 this.setLayout(new qx.ui.layout.Dock());
13 },
14
15 properties : {
16 /**
17 * The viewPane inside which this applet is added.
18 */
19 view : {
20 init : null
21 },
22 viewSelection : {
23 nullable : false,
24 check : "org.argeo.ria.components.ViewSelection"
25 },
26 instanceId : {
27 init : ""
28 },
29 instanceLabel : {
30 init : ""
31 },
32 executeAfterAdd : {
33 init : false,
34 check : "Boolean"
35 },
36 /**
37 * Commands definition, see
38 * {@link org.argeo.ria.event.CommandsManager#definitions}
39 */
40 commands : {
41 init : {
42 "addtobatch" : {
43 label : "Add to batch",
44 icon : "org.argeo.slc.ria/list-add.png",
45 shortcut : null,
46 enabled : true,
47 menu : null,
48 toolbar : null,
49 callback : function(e) {
50 if (this.tree.isSelectionEmpty()) {
51 return;
52 }
53 var batchView = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById("batch").getContent();
54 if(!batchView) return;
55 selection = this.tree.getSelection();
56 if (selection.length > 1) {
57 for (var i = 0; i < selection.length; i++) {
58 try{
59 batchView.addFlowToBatch(selection[i], null, true);
60 }catch(e){
61 return;
62 }
63 }
64 return;
65 } else {
66 try{
67 batchView.addFlowToBatch(selection[0], null);
68 }catch(e){
69 return;
70 }
71 }
72 if(this.getExecuteAfterAdd() && batchView.getCommands()){
73 batchView.setForceClearPreference(true);
74 batchView.getCommands()["submitform"].command.execute();
75 }
76 this.setExecuteAfterAdd(false);
77 },
78 selectionChange : function(viewId, selection) {
79 if (viewId != "form:tree")
80 return;
81 if (!selection || selection.length != 1)
82 return;
83 var item = selection[0];
84 this.setEnabled(false);
85 switch (item.classname) {
86 case "qx.ui.tree.TreeFile" :
87 this.setEnabled(true);
88 break;
89 case "org.argeo.ria.components.PersistentTreeFolder" :
90 if (item.getTree() && item.getTree().getRoot() == item)
91 break;
92 this.setEnabled(true);
93 break;
94 case "org.argeo.ria.components.DynamicTreeFolder" :
95 if (item.getTree() && item.getTree().getRoot() == item)
96 break;
97 if (item.getState() == "loaded")
98 this.setEnabled(true);
99 break;
100 }
101 },
102 command : null
103 },
104 "preferredHost" : {
105 label : "Toggle 'Preferred Host'",
106 icon : "ria/bookmark.png",
107 shortcut : null,
108 enabled : true,
109 menu : null,
110 toolbar : null,
111 callback : function(e) {
112 if (this.tree.isSelectionEmpty()) {
113 return;
114 }
115 var selection = this.tree.getSelection();
116 if(selection.length != 1) return;
117 var agentNode = selection[0];
118 if(!agentNode.getUserData("agentHost")) return;
119 this.togglePreferredHost(agentNode);
120 },
121 selectionChange : function(viewId, selection) {
122 if (viewId != "form:tree")
123 return;
124 if (!selection || selection.length != 1)
125 return;
126 var item = selection[0];
127 this.setEnabled(false);
128 if(item.getUserData("agentHost")){
129 this.setEnabled(true);
130 }
131 },
132 command : null
133 },
134 "reloadfull" : {
135 label : "Reload Agents",
136 icon : "org.argeo.slc.ria/view-refresh.png",
137 shortcut : "control+h",
138 enabled : true,
139 menu : "Launcher",
140 toolbar : "list",
141 callback : function(e) {
142 this.rootNode.reload();
143 },
144 command : null
145 },
146 "reloadtree" : {
147 label : "Reload Node",
148 icon : "org.argeo.slc.ria/view-refresh.png",
149 shortcut : null,
150 enabled : false,
151 menu : null,
152 toolbar : null,
153 callback : function(e) {
154 if (this.tree.isSelectionEmpty()) { return; }
155 var selected = this.tree.getSelection()[0];
156 if (selected.classname == "org.argeo.ria.components.DynamicTreeFolder") {
157 if (selected.getUserData("moduleData")) {
158 // It's a "module" node, first trigger the
159 // reloadBundle.service
160 selected.setUserData("dataModel", {});
161 selected.setEnabled(false);
162 selected.setOpen(false);
163 var moduleData = selected
164 .getUserData("moduleData");
165 var bundleService = org.argeo.slc.ria.SlcApi
166 .getReloadBundleService(
167 moduleData.name,
168 moduleData.version);
169 bundleService.addListener("completed",
170 function(response) {
171 selected.setEnabled(true);
172 selected.setOpen(true);
173 selected.reload();
174 }, this);
175 // bundleService.send();
176 // Do not send, not implemented yet, false timer
177 // instead.
178 qx.event.Timer.once(function(response) {
179 selected.setEnabled(true);
180 selected.setOpen(true);
181 selected.reload();
182 }, this, 2000);
183 } else {
184 selected.reload();
185 }
186 }
187 },
188 selectionChange : function(viewId, selection) {
189 if (viewId != "form:tree")
190 return;
191 if (!selection)
192 return;
193 if (selection.length > 1) {
194 this.setEnabled(false);
195 return;
196 }
197 var item = selection[0];
198 if (!qx.Class.isSubClassOf(qx.Class
199 .getByName(item.classname),
200 qx.ui.tree.AbstractTreeItem))
201 return;
202 this.setEnabled(false);
203 if (qx.Class.isSubClassOf(qx.Class
204 .getByName(item.classname),
205 org.argeo.ria.components.DynamicTreeFolder)) {
206 this.setEnabled(true);
207 }
208 },
209 command : null
210 }
211 }
212 }
213 },
214
215 statics : {
216
217 riaPreferences : {
218 "flowSelector.preferred.hosts" : {
219 label : "Preferred Hosts (Execution View)",
220 type : "string"
221 }
222 },
223
224 /**
225 * Static loader for the "agent" level (first level)
226 *
227 * @param folder
228 * {org.argeo.ria.components.PersistentTreeFolder} The root Tree Folder.
229 */
230 agentLoader : function(folder) {
231
232 var preferredHosts = org.argeo.ria.session.MPrefHolder.loadRiaPreferenceValue("flowSelector.preferred.hosts");
233 if(preferredHosts && preferredHosts!=""){
234 preferredHosts = preferredHosts.split(",");
235 }
236 var req = org.argeo.slc.ria.SlcApi.getListAgentsService("agents");
237 var agents = {};
238 if(folder.getState() == "loaded" && folder.getUserData("agentsMap")){
239 // Diff loading, just add new nodes.
240 agents = folder.getUserData("agentsMap");
241 var newAgents = {};
242 }
243 req.addListener("completed", function(response) {
244 var xmlDoc = response.getContent();
245 var nodes = org.argeo.ria.util.Element.selectNodes(xmlDoc,
246 "/slc:object-list/slc:slc-agent-descriptor");
247 var modulesLoader = org.argeo.slc.ria.FlowsSelectorView.modulesLoader;
248
249 for (var i = 0; i < nodes.length; i++) {
250 var uuid = org.argeo.ria.util.Element.getSingleNodeText(nodes[i], "@uuid");
251 var host = org.argeo.ria.util.Element.getSingleNodeText(nodes[i], "slc:host");
252 if(agents[uuid]){
253 newAgents[uuid] = host;
254 continue;
255 }
256 agents[uuid] = host;
257 if(newAgents) newAgents[uuid] = host;
258 var agentFolder = new org.argeo.ria.components.DynamicTreeFolder(
259 host, modulesLoader, "Loading Modules...", folder.getDragData());
260 org.argeo.slc.ria.FlowsSelectorView.attachToolTip(agentFolder, uuid);
261 agentFolder.setPersistentTreeID(folder.getPersistentTreeID()+"_"+uuid);
262 agentFolder.setUserData("agentHost", host); // Used by bookmark system
263 agentFolder.setUserData("agentUuid", uuid);
264 if(preferredHosts && preferredHosts instanceof Array && qx.lang.Array.contains(preferredHosts, host)){
265 folder.addAtBegin(agentFolder);
266 agentFolder.setIcon("org.argeo.slc.ria/computer_bookmarked.png");
267 }else{
268 folder.add(agentFolder);
269 agentFolder.setIcon("org.argeo.slc.ria/computer.png");
270 }
271 }
272 if(newAgents){
273 // Make sure some agents should not be removed
274 for(var agentKey in agents){
275 if(!newAgents[agentKey]){
276 var node = org.argeo.slc.ria.FlowsSelectorView.findAgentNodeById(folder, agentKey);
277 if(node) folder.remove(node);
278 delete agents[agentKey];
279 var batchView = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById("batch").getContent();
280 if(batchView) batchView.clearBatchForAgentId(agentKey);
281 }
282 }
283 }
284 folder.setUserData("agentsMap", agents);
285 folder.setLoaded(true);
286 folder.getTree().fireEvent("changeSelection");
287 });
288 req.addListener("failed", function(response) {
289 folder.setLoaded(true);
290 });
291 req.send();
292
293 },
294
295 /**
296 * Loader for the "modules" level : takes any tree folder, currently the
297 * root folder.
298 *
299 * @param folder
300 * {org.argeo.ria.components.PersistentTreeFolder} The root folder
301 */
302 modulesLoader : function(folder) {
303 var agentId = folder.getUserData("agentUuid");
304 var req = org.argeo.slc.ria.SlcApi.getListModulesService(agentId);
305 req.addListener("completed", function(response) {
306 var descriptors = org.argeo.ria.util.Element.selectNodes(
307 response.getContent(),
308 "slc:object-list/" + org.argeo.slc.ria.execution.Module.XPATH_ROOT);
309 var mods = {};
310 for (var i = 0; i < descriptors.length; i++) {
311 var tmpModule = new org.argeo.slc.ria.execution.Module();
312 try{
313 tmpModule.setXmlNode(descriptors[i]);
314 }catch(e){
315 qx.log.Logger.error(e);
316 }
317 var name = tmpModule.getName();
318 var version = tmpModule.getVersion();
319 if (!mods[name])
320 mods[name] = [];
321 mods[name].push(tmpModule);
322 }
323 var flowLoader = org.argeo.slc.ria.FlowsSelectorView.flowLoader;
324 for (var key in mods) {
325 for (var i = 0; i < mods[key].length; i++) {
326 var module = mods[key][i];
327 var versionFolder = new org.argeo.ria.components.DynamicTreeFolder(
328 module.getLabel(), flowLoader,
329 "Loading Flows", folder.getDragData());
330 versionFolder.setUserData("moduleData", {
331 name : key,
332 version : module.getVersion()
333 });
334 versionFolder.setIcon("org.argeo.slc.ria/archive.png");
335 versionFolder.setUserData("agentUuid", agentId);
336 var sep = (module.getDescription()!=""?" - ":"");
337 org.argeo.slc.ria.FlowsSelectorView.attachToolTip(versionFolder, key + ' (' + module.getVersion() + ')'+sep+module.getDescription());
338 // Warning, we must add it AFTER setting the user data,
339 // because of the persistent loading mechanism.
340 folder.add(versionFolder);
341 }
342 }
343 folder.setLoaded(true);
344 folder.getTree().fireEvent("changeSelection");
345 });
346 req.addListener("failed", function(response) {
347 folder.setLoaded(true);
348 });
349 req.send();
350 },
351
352 /**
353 * Loader for the "flow" level : takes a folder containing "moduleData"
354 * and create its children.
355 *
356 * @param folder
357 * {org.argeo.ria.components.PersistentTreeFolder} A Tree folder containing in the
358 * key "moduleData" of its user data a map containing the
359 * keys {name,version}
360 */
361 flowLoader : function(folder) {
362 var moduleData = folder.getUserData("moduleData");
363 var agentUuid = folder.getUserData("agentUuid");
364
365 var req = org.argeo.slc.ria.SlcApi
366 .getLoadExecutionDescriptorService(agentUuid,
367 moduleData.name, moduleData.version);
368 req.addListener("completed", function(response) {
369 var executionModule = new org.argeo.slc.ria.execution.Module();
370 try {
371 executionModule.setXmlNode(response.getContent());
372 } catch (e) {
373 this.error(e);
374 }
375 var execFlows = executionModule.getExecutionFlows();
376 for (var key in execFlows) {
377 var path = execFlows[key].getPath();
378 var name = execFlows[key].getName();
379 var nodeLabel = key;
380 var sep = "\/";
381 if(name && name.indexOf(sep)>-1 && !path){
382 // Use given name to build the path.
383 // split dirname and basename
384 var parts = name.split(sep);
385 var basename = parts.pop();
386 var dirname = parts.join("/");
387 path = dirname;
388 nodeLabel = basename;
389 }
390 var file = new qx.ui.tree.TreeFile(nodeLabel);
391 if(execFlows[key].getDescription() != ""){
392 org.argeo.slc.ria.FlowsSelectorView.attachToolTip(file, execFlows[key].getDescription());
393 }
394 file.setIcon("org.argeo.slc.ria/system.png");
395 file.setUserData("executionModule", executionModule);
396 file.setUserData("executionFlow", execFlows[key]);
397 file.setUserData("agentUuid", agentUuid);
398 org.argeo.slc.ria.FlowsSelectorView.attachNodeByPath(folder, path, file, {
399 agentUuid : folder.getUserData("agentUuid")
400 });
401 folder.appendDragData(file);
402 }
403 folder.setLoaded(true);
404 folder.getTree().fireEvent("changeSelection");
405 });
406 req.addListener("failed", function(response) {
407 folder.setLoaded(true);
408 });
409 req.send();
410 },
411
412 attachToolTip : function(nodeItem, description){
413 var tt = new qx.ui.tooltip.ToolTip(description);
414 tt.setShowTimeout(0);
415 nodeItem.setToolTip(tt);
416 },
417
418 /**
419 * Parse a string path and search if there is a root node.
420 *
421 * @param rootNode
422 * {org.argeo.ria.components.DynamicTreeFolder} The parent
423 * node (containing data model)
424 * @param path
425 * {String} The path of the node to attach.
426 * @param childNode
427 * {qx.ui.tree.TreeFile} The leaf node
428 * @param userData
429 * {Map} User data to attach at all levels.
430 */
431 attachNodeByPath : function(rootNode, path, childNode, userData) {
432 if (!path || path == "" || path == "/") {
433 rootNode.add(childNode);
434 return;
435 }
436 var model = rootNode.getUserData("dataModel");
437 if (!model) {
438 model = {};
439 rootNode.setUserData("dataModel", model);
440 }
441 var parts = path.split("/");
442 var keys = qx.lang.Object.getKeys(model);
443 var crtPath = "";
444 var crtFolder = rootNode;
445 for (var i = 0; i < parts.length; i++) {
446 if (parts[i] == "")
447 continue;
448 crtPath += "/" + parts[i];
449 if (!model[crtPath]) {
450 var virtualFolder = new org.argeo.ria.components.PersistentTreeFolder(parts[i]);
451 if (userData && qx.lang.Object.getLength(userData)) {
452 for (var key in userData) {
453 virtualFolder.setUserData(key, userData[key]);
454 }
455 }
456 rootNode.appendDragData(virtualFolder);
457 model[crtPath] = virtualFolder;
458 crtFolder.add(virtualFolder);
459 crtFolder = virtualFolder;
460 } else {
461 crtFolder = model[crtPath];
462 }
463 }
464 crtFolder.add(childNode);
465 },
466
467 findAgentNodeById : function(node, agentId){
468 var nodeAgents = node.getItems();
469 for(var i=0;i<nodeAgents.length;i++){
470 if(nodeAgents[i].getUserData("agentUuid") == agentId){
471 return nodeAgents[i];
472 }
473 }
474 }
475 },
476
477 members : {
478 /**
479 * Called at applet creation. Just registers viewPane.
480 *
481 * @param viewPane
482 * {org.argeo.ria.components.ViewPane} The viewPane.
483 */
484 init : function(viewPane) {
485 this.setView(viewPane);
486 this.setViewSelection(new org.argeo.ria.components.ViewSelection(viewPane.getViewId()));
487 this.remoteNotifier = new org.argeo.ria.remote.RemoteNotifier(
488 "/org.argeo.slc.webapp/", "pollEvent.service",
489 "addEventListener.service", "removeEventListener.service");
490 this.remoteNotifier.setEventParamName("slc_eventType");
491 this.remoteNotifier.setEventXPath("/slc:slc-event");
492 this.remoteNotifier
493 .setEventTypeXPath('slc:headers/slc:header[@name="slc_eventType"]');
494 this.remoteNotifier
495 .setEventDataXPath('slc:headers/slc:header[@name="slc_agentId"]');
496 this.remoteNotifier.setAnswerStatusXPath("slc:execution-answer/slc:status");
497 this.remoteNotifier.startPolling();
498 this.UIBus = org.argeo.ria.event.UIBus.getInstance();
499 this.UIBus.registerNotifier(this.remoteNotifier);
500 },
501
502 /**
503 *
504 */
505 load : function() {
506 this._createLayout();
507 this.getView().setViewTitle("Available Scripts");
508 this.UIBus.addListener("agentRegistered", this._addAgentHandler, this);
509 this.UIBus.addListener("agentUnregistered", this._removeAgentHandler, this);
510 },
511
512 _addAgentHandler : function(agentId){
513 this.rootNode.load();
514 },
515
516 _removeAgentHandler : function(agentId){
517 var treeNode = this.self(arguments).findAgentNodeById(this.rootNode, agentId);
518 if(treeNode){
519 this.rootNode.remove(treeNode);
520 }
521 var agentsMap = this.getAgentsMap();
522 if(agentsMap[agentId]){
523 delete agentsMap[agentId];
524 }
525 var batchView = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById("batch").getContent();
526 if(batchView){
527 batchView.clearBatchForAgentId(agentId);
528 }
529 },
530
531 addScroll : function() {
532 return false;
533 },
534
535 close : function() {
536 this.UIBus.removeListener("agentRegistered", this._addAgentHandler, this);
537 this.UIBus.removeListener("agentUnregistered", this._removeAgentHandler, this);
538 this.remoteNotifier.stopPolling();
539 },
540
541 /**
542 *
543 * @param agentNode {qx.ui.tree.AbstractTreeItem}
544 */
545 togglePreferredHost : function(agentNode){
546 var hostName = agentNode.getUserData("agentHost");
547 var pref = this.getRiaPreferenceValue("flowSelector.preferred.hosts");
548 var prefArray = [];
549 if(pref){
550 prefArray = pref.split(",");
551 }
552 if(qx.lang.Array.contains(prefArray, hostName)){
553 qx.lang.Array.remove(prefArray, hostName);
554 agentNode.setIcon("org.argeo.slc.ria/computer.png");
555 }else{
556 prefArray.push(hostName);
557 agentNode.setIcon("org.argeo.slc.ria/computer_bookmarked.png");
558 var parent = agentNode.getParent();
559 parent.remove(agentNode);
560 parent.addAtBegin(agentNode);
561 }
562 this.setRiaPreferenceValue("flowSelector.preferred.hosts", prefArray.join(","));
563 },
564
565 /**
566 * Creates the main applet layout.
567 */
568 _createLayout : function() {
569
570 this.tree = new qx.ui.tree.Tree();
571 this.tree.setDecorator(null);
572 this.tree.setSelectionMode("multi");
573 var dragData = {
574 "file" : {
575 "type" : ["items"],
576 "action" : ["move"]
577 },
578 "folder" : {
579 "type" : ["items"],
580 "action" : ["move"]
581 }
582 };
583
584 this.rootNode = new org.argeo.ria.components.DynamicTreeFolder(
585 "Tests", this.self(arguments).agentLoader,
586 "Loading Agents", dragData);
587 this.tree.setRoot(this.rootNode);
588 this.tree.setHideRoot(true);
589 this.rootNode.setPersistentTreeID("org.argeo.slc.ria.FlowsSelector")
590 this.rootNode.setOpen(true);
591 this.tree.setContextMenu(org.argeo.ria.event.CommandsManager
592 .getInstance().createMenuFromIds(["addtobatch",
593 "reloadtree", "preferredHost"]));
594
595 this.tree.addListener("changeSelection", function(e) {
596 var viewSelection = this.getViewSelection();
597 viewSelection.setViewId("form:tree");
598 viewSelection.clear();
599 var sel = this.tree.getSortedSelection();
600 for (var i = 0; i < sel.length; i++) {
601 viewSelection.addNode(sel[i]);
602 }
603 }, this);
604
605 this.tree.addListener("dblclick", function(e){
606 var sel = this.tree.getSortedSelection();
607 if(sel && sel.length!=1) return;
608 var origin = sel[0];
609 if(origin.classname == "qx.ui.tree.TreeFile"){
610 this.setExecuteAfterAdd(true);
611 this.getCommands()["addtobatch"].command.execute();
612 }
613 }, this);
614
615 this.add(this.tree);
616 },
617
618 getAgentsMap : function(){
619 return this.rootNode.getUserData("agentsMap");
620 }
621 }
622 });