2a89c32913ddc8f9fa1f3e06d4368b1d086caf84
[tine20] / tine20 / Tinebase / js / widgets / tree / ContextMenu.js
1 /*
2  * Tine 2.0
3  * 
4  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
5  * @author      Philipp Schüle <p.schuele@metaways.de>
6  * @copyright   Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
7  *
8  */
9 Ext.ns('Tine.widgets', 'Tine.widgets.tree');
10
11 /**
12  * returns generic tree context menu with
13  * - create/add
14  * - rename
15  * - delete
16  * - edit grants
17  * 
18  * ctxNode class var is required in calling class
19  */
20 Tine.widgets.tree.ContextMenu = {
21     
22     /**
23      * create new Ext.menu.Menu with actions
24      * 
25      * @param {} config has the node name, actions, etc.
26      * @return {}
27      */
28     getMenu: function(config) {
29         
30         this.config = config;
31                 
32         /****************** create ITEMS array ****************/
33               
34         this.action_add = new Ext.Action({
35             text: String.format(_('Add {0}'), this.config.nodeName),
36             iconCls: 'action_add',
37             handler: this.addNode,
38             requiredGrant: 'addGrant',
39             scope: this.config
40         });
41         
42         this.action_rename = new Ext.Action({
43             text: String.format(_('Rename {0}'), this.config.nodeName),
44             iconCls: 'action_rename',
45             handler: this.renameNode,
46             scope: this.config,
47             requiredGrant: 'editGrant',
48             allowMultiple: false
49         });
50         
51         this.action_delete = new Ext.Action({
52             text: String.format(Tine.Tinebase.translation.ngettext('Delete {0}', 'Delete {0}', 1), this.config.nodeName),
53             iconCls: 'action_delete',
54             handler: this.deleteNode,
55             scope: this.config,
56             requiredGrant: 'deleteGrant',
57             allowMultiple: true
58         });
59         
60         this.action_grants = new Ext.Action({
61             text: String.format(_('Manage {0} Permissions'), this.config.nodeName),
62             iconCls: 'action_managePermissions',
63             handler: this.managePermissions,
64             requiredGrant: 'editGrant',
65             scope: this.config
66         });
67         
68         this.action_properties = new Ext.Action({
69             text: String.format(_('{0} Properties'), this.config.nodeName),
70             iconCls: 'action_manageProperties',
71             handler: this.manageProperties,
72             requiredGrant: 'readGrant',
73             scope: this.config
74         });
75         
76         // TODO is edit grant required?
77         this.action_changecolor = new Ext.Action({
78             text: String.format(_('Set {0} color'), this.config.nodeName),
79             iconCls: 'action_changecolor',
80 //            requiredGrant: 'editGrant',
81             allowMultiple: true,
82             menu: new Ext.menu.ColorMenu({
83                 scope: this,
84                 listeners: {
85                     select: this.changeNodeColor,
86                     scope: this.config
87                 }
88             })
89         });
90         
91         this.action_reload = new Ext.Action({
92             text: String.format(_('Reload {0}'), this.config.nodeName),
93             iconCls: 'x-tbar-loading',
94             handler: this.reloadNode,
95             scope: this.config
96         });
97         
98         // TODO move the next 5 to Filemanager!
99         this.action_resume = new Ext.Action({
100             text: String.format(_('Resume upload'), config.nodeName),
101             iconCls: 'action_resume',
102             handler: this.onResume,
103             scope: this.config,
104             actionUpdater: this.isResumeEnabled
105         });
106         this.action_editFile = new Ext.Action({
107             requiredGrant: 'editGrant',
108             allowMultiple: false,
109             text: _('Edit Properties'),
110             handler: this.onEditFile,
111             iconCls: 'action_edit_file',
112             actionType: 'edit',
113             scope: this
114         });
115         this.action_pause = new Ext.Action({
116             text: String.format(_('Pause upload'), config.nodeName),
117             iconCls: 'action_pause',
118             handler: this.onPause,
119             actionUpdater: this.isPauseEnabled,
120             scope: this.config
121         });
122         
123         this.action_download = new Ext.Action({
124             text: String.format(_('Download'), config.nodeName),
125             iconCls: 'action_filemanager_save_all',
126             handler: this.downloadFile,
127             actionUpdater: this.isDownloadEnabled,
128             scope: this.config
129         });
130         
131         this.action_publish = new Ext.Action({
132             text: String.format(_('Publish'), config.nodeName),
133             iconCls: 'action_publish',
134             handler: this.publishFile,
135             actionUpdater: true,
136             scope: this.config
137         });
138         
139         var items = [];
140         for (var i=0; i < config.actions.length; i++) {
141             switch(config.actions[i]) {
142                 case 'add':
143                     items.push(this.action_add);
144                     break;
145                 case 'delete':
146                     items.push(this.action_delete);
147                     break;
148                 case 'rename':
149                     items.push(this.action_rename);
150                     break;
151                 case 'properties':
152                     items.push(this.action_properties);
153                     break;
154                 case 'changecolor':
155                     items.push(this.action_changecolor);
156                     break;
157                 case 'grants':
158                     items.push(this.action_grants);
159                     break;
160                 case 'reload':
161                     items.push(this.action_reload);
162                     break;
163                 case 'resume':
164                     items.push(this.action_resume);
165                     break;
166                 case 'pause':
167                     items.push(this.action_pause);
168                     break;
169                 case 'download':
170                     items.push(this.action_download);
171                     break;
172                 case 'edit':
173                     items.push(this.action_editFile);
174                     break;
175                 case 'publish':
176                     items.push(this.action_publish);
177                     break;
178                 default:
179                     // add custom actions
180                     items.push(new Ext.Action(config.actions[i]));
181             }
182         }
183
184              
185         /******************* return menu **********************/
186         
187         return new Ext.menu.Menu({
188             items: items
189         });
190     },
191     
192     /**
193      * create tree node
194      */
195     addNode: function() {
196         Ext.MessageBox.prompt(String.format(_('New {0}'), this.nodeName), String.format(_('Please enter the name of the new {0}:'), this.nodeName), function(_btn, _text) {
197             if( this.scope.ctxNode && _btn == 'ok') {
198                 if (! _text) {
199                     Ext.Msg.alert(String.format(_('No {0} added'), this.nodeName), String.format(_('You have to supply a {0} name!'), this.nodeName));
200                     return;
201                 }
202                 Ext.MessageBox.wait(_('Please wait'), String.format(_('Creating {0}...' ), this.nodeName));
203                 var parentNode = this.scope.ctxNode;
204                 
205                 var params = {
206                     method: this.backend + '.add' + this.backendModel,
207                     name: _text
208                 };
209                 
210                 // TODO try to generalize this and move app specific stuff to app
211                 
212                 if (this.backendModel == 'Node') {
213                     params.application = this.scope.app.appName || this.scope.appName;
214                     var filename = parentNode.attributes.nodeRecord.data.path + '/' + _text;
215                     params.filename = filename;
216                     params.type = 'folder';
217                     params.method = this.backend + ".createNode";
218                 }
219                 else if (this.backendModel == 'Container') {
220                     params.application = this.scope.app.appName || this.scope.appName;
221                     params.containerType = Tine.Tinebase.container.path2type(parentNode.attributes.path);
222                     params.modelName = this.scope.app.getMainScreen().getActiveContentType();
223                 } 
224                 else if (this.backendModel == 'Folder') {
225                     var parentFolder = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore().getById(parentNode.attributes.folder_id);
226                     params.parent = parentFolder.get('globalname');
227                     params.accountId = parentFolder.get('account_id');
228                 }
229                 
230                 Ext.Ajax.request({
231                     params: params,
232                     scope: this,
233                     success: function(result, request){
234                         var nodeData = Ext.util.JSON.decode(result.responseText);
235
236                         // TODO add + icon if it wasn't expandable before
237                         if(nodeData.type == 'folder') {
238                             var nodeData = Ext.util.JSON.decode(result.responseText);
239
240                             var app = Tine.Tinebase.appMgr.get(this.scope.app.appName);
241                             var newNode = app.getMainScreen().getWestPanel().getContainerTreePanel().createTreeNode(nodeData, parentNode);
242                             parentNode.appendChild(newNode);
243                         }
244                         else {
245                             var newNode = this.scope.loader.createNode(nodeData);
246                             parentNode.appendChild(newNode);
247                         }
248                         
249                         parentNode.expand();
250                         this.scope.fireEvent('containeradd', nodeData);
251                                               
252                         Ext.MessageBox.hide();
253                     },
254                     failure: function(result, request) {
255                         var nodeData = Ext.util.JSON.decode(result.responseText);
256                         
257                         var appContext = Tine[this.scope.app.appName];
258                         if(appContext && appContext.handleRequestException) {
259                             appContext.handleRequestException(nodeData.data);
260                         }
261                     }
262                 });
263                 
264             }
265         }, this);
266     },
267     
268     /**
269      * rename tree node
270      */
271     renameNode: function() {
272         if (this.scope.ctxNode) {
273     
274             var node = this.scope.ctxNode;
275             Ext.MessageBox.show({
276                 title: String.format(_('Rename {0}'), this.nodeName),
277                 msg: String.format(_('Please enter the new name of the {0}:'), this.nodeName),
278                 buttons: Ext.MessageBox.OKCANCEL,
279                 value: node.attributes.longName || node.text,
280                 fn: function(_btn, _text){
281                     if (_btn == 'ok') {
282                         if (! _text) {
283                             Ext.Msg.alert(String.format(_('Not renamed {0}'), this.nodeName), String.format(_('You have to supply a {0} name!'), this.nodeName));
284                             return;
285                         }
286                         
287                         var params = {
288                             method: this.backend + '.rename' + this.backendModel,
289                             newName: _text
290                         };
291                         
292                         params.application = this.scope.app.appName || this.scope.appName;
293
294                         if (this.backendModel == 'Node') {
295                             var filename = node.attributes.path;
296                             params.sourceFilenames = [filename];
297                             
298                             var targetFilename = "/";
299                             var sourceSplitArray = filename.split("/");
300                             for (var i=1; i<sourceSplitArray.length-1; i++) {
301                                 targetFilename += sourceSplitArray[i] + '/';
302                             }
303                             
304                             params.destinationFilenames = [targetFilename + _text];
305                             params.method = this.backend + '.moveNodes';
306                         }
307                         
308                         // TODO try to generalize this
309                         if (this.backendModel == 'Container') {
310                             params.containerId = node.attributes.container.id;
311                         } else if (this.backendModel == 'Folder') {
312                             var folder = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore().getById(node.attributes.folder_id);
313                             params.oldGlobalName = folder.get('globalname');
314                             params.accountId = folder.get('account_id');
315                         }
316                         
317                         Ext.Ajax.request({
318                             params: params,
319                             scope: this,
320                             success: function(_result, _request){
321                             
322                                 var nodeData = Ext.util.JSON.decode(_result.responseText);
323                                 node.setText(_text);
324                                 
325                                 this.scope.fireEvent('containerrename', nodeData, node, _text);
326                                                               
327                             },
328                             failure: function(result, request) {
329                                 var nodeData = Ext.util.JSON.decode(result.responseText);
330                                 
331                                 var appContext = Tine[this.scope.app.appName];
332                                 if(appContext && appContext.handleRequestException) {
333                                     appContext.handleRequestException(nodeData.data);
334                                 }
335                             }
336                         });
337                     }
338                 },
339                 scope: this,
340                 prompt: true,
341                 icon: Ext.MessageBox.QUESTION
342             });
343         }
344     },
345     
346     /**
347      * delete tree node
348      */
349     deleteNode: function() {
350         
351         if (this.scope.ctxNode) {
352             var node = this.scope.ctxNode;
353             
354             Tine.log.debug('Tine.widgets.tree.ContextMenu::deleteNode()');
355             Tine.log.debug(node);
356             
357             Ext.MessageBox.confirm(_('Confirm'), String.format(_('Do you really want to delete the {0} "{1}"?'), this.nodeName, node.text), function(_btn){
358                 if ( _btn == 'yes') {
359                     
360                     var params = {
361                         method: this.backend + '.delete' + this.backendModel
362                     };
363                     
364                     if (this.backendModel == 'Node') {
365                                            
366                         var filenames = [node.attributes.path];
367                         params.application = this.scope.app.appName || this.scope.appName;
368                         params.filenames = filenames;
369                         params.method = this.backend + ".deleteNodes";
370                     
371                     } else if (this.backendModel == 'Container') {
372                         params.containerId = node.attributes.container.id;
373                     } else if (this.backendModel == 'Folder') {
374                         var folder = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore().getById(node.attributes.folder_id);
375                         params.folder = folder.get('globalname');
376                         params.accountId = folder.get('account_id');
377                     } else {
378                         // use default json api style
379                         params.ids = [node.id];
380                         params.method = params.method + 's';
381                     }
382                     
383                     Ext.Ajax.request({
384                         params: params,
385                         scope: this,
386                         success: function(_result, _request){
387                               
388                             if(node) {
389                                 if(node.isSelected()) {
390                                     this.scope.getSelectionModel().select(node.parentNode);
391                                     this.scope.fireEvent('click', node.parentNode, Ext.EventObject.setEvent());
392                                 }
393
394                                 node.remove();
395                                 if (this.backendModel == 'Container') {
396                                     this.scope.fireEvent('containerdelete', node.attributes.container);
397                                 } else if (this.backendModel == 'Node') {
398                                     this.scope.fireEvent('containerdelete', node);
399                                 } else {
400                                     this.scope.fireEvent('containerdelete', node.attributes);
401                                 }
402                             }
403                            
404                         },
405                         failure: function(result, request) {
406                             var nodeData = Ext.util.JSON.decode(result.responseText);
407                             var appContext = Tine[this.scope.app.appName];
408                             if(appContext && appContext.handleRequestException) {
409                                 appContext.handleRequestException(nodeData.data);
410                             }
411                         }
412                     });
413                 }
414             }, this);
415         }
416     },
417     
418     /**
419      * change tree node color
420      */
421     changeNodeColor: function(cp, color) {
422         if (this.scope.ctxNode) {
423             var node = this.scope.ctxNode;
424             node.getUI().addClass("x-tree-node-loading");
425                 Ext.Ajax.request({
426                     params: {
427                         method: this.backend + '.set' + this.backendModel + 'Color',
428                         containerId: node.attributes.container.id,
429                         color: '#' + color
430                     },
431                     scope: this,
432                     success: function(_result, _request){
433                         var nodeData = Ext.util.JSON.decode(_result.responseText);
434                         node.getUI().colorNode.setStyle({color: nodeData.color});
435                         node.attributes.color = nodeData.color;
436                         this.scope.fireEvent('containercolorset', nodeData);
437                         node.getUI().removeClass("x-tree-node-loading");
438                     },
439                     failure: function(result, request) {
440                         var nodeData = Ext.util.JSON.decode(result.responseText);
441                         
442                         var appContext = Tine[this.scope.app.appName];
443                         if(appContext && appContext.handleRequestException) {
444                             appContext.handleRequestException(nodeData.data);
445                         }
446                     }
447                 });
448         
449         }
450     },
451     
452     /**
453      * manage permissions
454      * 
455      */
456     managePermissions: function() {
457
458         if (this.scope.ctxNode) {
459             var node = this.scope.ctxNode,
460                 grantsContainer;
461             if(node.attributes.nodeRecord && node.attributes.nodeRecord.data.name) {
462                 grantsContainer = node.attributes.nodeRecord.data.name;
463             } else if(node.attributes.container) {
464                 grantsContainer = node.attributes.container;
465             }
466             
467             var window = Tine.widgets.container.GrantsDialog.openWindow({
468                 title: String.format(_('Manage Permissions for {0} "{1}"'), this.nodeName, Ext.util.Format.htmlEncode(grantsContainer.name)),
469                 containerName: this.nodeName,
470                 grantContainer: grantsContainer,
471                 app: this.scope.app.appName
472             });
473         }
474         
475     },
476     
477     /**
478      * manage properties
479      * 
480      */
481     manageProperties: function() {
482         if (this.scope.ctxNode) {
483             var node = this.scope.ctxNode,
484                 grantsContainer;
485             if(node.attributes.nodeRecord && node.attributes.nodeRecord.data.name) {
486                 grantsContainer = node.attributes.nodeRecord.data.name;
487             } else if(node.attributes.container) {
488                 grantsContainer = node.attributes.container;
489             }
490             
491             var window = Tine.widgets.container.PropertiesDialog.openWindow({
492                 title: String.format(_('Properties for {0} "{1}"'), this.nodeName, Ext.util.Format.htmlEncode(grantsContainer.name)),
493                 containerName: this.nodeName,
494                 grantContainer: grantsContainer,
495                 app: this.scope.app.appName
496             });
497         }
498     },
499     
500     /**
501      * reload node
502      */
503     reloadNode: function() {
504         if (this.scope.ctxNode) {
505             var tree = this.scope;
506             this.scope.ctxNode.reload(function(node) {
507                 node.expand();
508                 node.select();
509                 // update grid
510                 tree.filterPlugin.onFilterChange();
511             });
512         }
513     }
514     
515 };