0010132: Create Folder inside a folder with the same name
[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                         if (nodeData.length == 0) {
237                             Tine.log.err('Server returned empty node data!');
238                             Ext.MessageBox.hide();
239                             return;
240                         }
241
242                         // TODO add + icon if it wasn't expandable before
243                         if(nodeData.type == 'folder') {
244                             var nodeData = Ext.util.JSON.decode(result.responseText);
245
246                             var app = Tine.Tinebase.appMgr.get(this.scope.app.appName);
247                             var newNode = app.getMainScreen().getWestPanel().getContainerTreePanel().createTreeNode(nodeData, parentNode);
248                             parentNode.appendChild(newNode);
249                         }
250                         else {
251                             var newNode = this.scope.loader.createNode(nodeData);
252                             parentNode.appendChild(newNode);
253                         }
254                         
255                         parentNode.expand();
256                         this.scope.fireEvent('containeradd', nodeData);
257                                               
258                         Ext.MessageBox.hide();
259                     },
260                     failure: function(result, request) {
261                         var nodeData = Ext.util.JSON.decode(result.responseText);
262                         
263                         var appContext = Tine[this.scope.app.appName];
264                         if(appContext && appContext.handleRequestException) {
265                             appContext.handleRequestException(nodeData.data);
266                         }
267                     }
268                 });
269                 
270             }
271         }, this);
272     },
273     
274     /**
275      * rename tree node
276      */
277     renameNode: function() {
278         if (this.scope.ctxNode) {
279     
280             var node = this.scope.ctxNode;
281             Ext.MessageBox.show({
282                 title: String.format(_('Rename {0}'), this.nodeName),
283                 msg: String.format(_('Please enter the new name of the {0}:'), this.nodeName),
284                 buttons: Ext.MessageBox.OKCANCEL,
285                 value: node.attributes.longName || node.text,
286                 fn: function(_btn, _text){
287                     if (_btn == 'ok') {
288                         if (! _text) {
289                             Ext.Msg.alert(String.format(_('Not renamed {0}'), this.nodeName), String.format(_('You have to supply a {0} name!'), this.nodeName));
290                             return;
291                         }
292                         
293                         var params = {
294                             method: this.backend + '.rename' + this.backendModel,
295                             newName: _text
296                         };
297                         
298                         params.application = this.scope.app.appName || this.scope.appName;
299
300                         if (this.backendModel == 'Node') {
301                             var filename = node.attributes.path;
302                             params.sourceFilenames = [filename];
303                             
304                             var targetFilename = "/";
305                             var sourceSplitArray = filename.split("/");
306                             for (var i=1; i<sourceSplitArray.length-1; i++) {
307                                 targetFilename += sourceSplitArray[i] + '/';
308                             }
309                             
310                             params.destinationFilenames = [targetFilename + _text];
311                             params.method = this.backend + '.moveNodes';
312                         }
313                         
314                         // TODO try to generalize this
315                         if (this.backendModel == 'Container') {
316                             params.containerId = node.attributes.container.id;
317                         } else if (this.backendModel == 'Folder') {
318                             var folder = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore().getById(node.attributes.folder_id);
319                             params.oldGlobalName = folder.get('globalname');
320                             params.accountId = folder.get('account_id');
321                         }
322                         
323                         Ext.Ajax.request({
324                             params: params,
325                             scope: this,
326                             success: function(_result, _request){
327                             
328                                 var nodeData = Ext.util.JSON.decode(_result.responseText);
329                                 node.setText(_text);
330                                 
331                                 this.scope.fireEvent('containerrename', nodeData, node, _text);
332                                                               
333                             },
334                             failure: function(result, request) {
335                                 var nodeData = Ext.util.JSON.decode(result.responseText);
336                                 
337                                 var appContext = Tine[this.scope.app.appName];
338                                 if(appContext && appContext.handleRequestException) {
339                                     appContext.handleRequestException(nodeData.data);
340                                 }
341                             }
342                         });
343                     }
344                 },
345                 scope: this,
346                 prompt: true,
347                 icon: Ext.MessageBox.QUESTION
348             });
349         }
350     },
351     
352     /**
353      * delete tree node
354      */
355     deleteNode: function() {
356         
357         if (this.scope.ctxNode) {
358             var node = this.scope.ctxNode;
359             
360             Tine.log.debug('Tine.widgets.tree.ContextMenu::deleteNode()');
361             Tine.log.debug(node);
362             
363             Ext.MessageBox.confirm(_('Confirm'), String.format(_('Do you really want to delete the {0} "{1}"?'), this.nodeName, node.text), function(_btn){
364                 if ( _btn == 'yes') {
365                     
366                     var params = {
367                         method: this.backend + '.delete' + this.backendModel
368                     };
369                     
370                     if (this.backendModel == 'Node') {
371                                            
372                         var filenames = [node.attributes.path];
373                         params.application = this.scope.app.appName || this.scope.appName;
374                         params.filenames = filenames;
375                         params.method = this.backend + ".deleteNodes";
376                     
377                     } else if (this.backendModel == 'Container') {
378                         params.containerId = node.attributes.container.id;
379                     } else if (this.backendModel == 'Folder') {
380                         var folder = Tine.Tinebase.appMgr.get('Felamimail').getFolderStore().getById(node.attributes.folder_id);
381                         params.folder = folder.get('globalname');
382                         params.accountId = folder.get('account_id');
383                     } else {
384                         // use default json api style
385                         params.ids = [node.id];
386                         params.method = params.method + 's';
387                     }
388                     
389                     Ext.Ajax.request({
390                         params: params,
391                         scope: this,
392                         success: function(_result, _request){
393                               
394                             if(node) {
395                                 if(node.isSelected()) {
396                                     this.scope.getSelectionModel().select(node.parentNode);
397                                     this.scope.fireEvent('click', node.parentNode, Ext.EventObject.setEvent());
398                                 }
399
400                                 node.remove();
401                                 if (this.backendModel == 'Container') {
402                                     this.scope.fireEvent('containerdelete', node.attributes.container);
403                                 } else if (this.backendModel == 'Node') {
404                                     this.scope.fireEvent('containerdelete', node);
405                                 } else {
406                                     this.scope.fireEvent('containerdelete', node.attributes);
407                                 }
408                             }
409                            
410                         },
411                         failure: function(result, request) {
412                             var nodeData = Ext.util.JSON.decode(result.responseText);
413                             var appContext = Tine[this.scope.app.appName];
414                             if(appContext && appContext.handleRequestException) {
415                                 appContext.handleRequestException(nodeData.data);
416                             }
417                         }
418                     });
419                 }
420             }, this);
421         }
422     },
423     
424     /**
425      * change tree node color
426      */
427     changeNodeColor: function(cp, color) {
428         if (this.scope.ctxNode) {
429             var node = this.scope.ctxNode;
430             node.getUI().addClass("x-tree-node-loading");
431                 Ext.Ajax.request({
432                     params: {
433                         method: this.backend + '.set' + this.backendModel + 'Color',
434                         containerId: node.attributes.container.id,
435                         color: '#' + color
436                     },
437                     scope: this,
438                     success: function(_result, _request){
439                         var nodeData = Ext.util.JSON.decode(_result.responseText);
440                         node.getUI().colorNode.setStyle({color: nodeData.color});
441                         node.attributes.color = nodeData.color;
442                         this.scope.fireEvent('containercolorset', nodeData);
443                         node.getUI().removeClass("x-tree-node-loading");
444                     },
445                     failure: function(result, request) {
446                         var nodeData = Ext.util.JSON.decode(result.responseText);
447                         
448                         var appContext = Tine[this.scope.app.appName];
449                         if(appContext && appContext.handleRequestException) {
450                             appContext.handleRequestException(nodeData.data);
451                         }
452                     }
453                 });
454         
455         }
456     },
457     
458     /**
459      * manage permissions
460      * 
461      */
462     managePermissions: function() {
463
464         if (this.scope.ctxNode) {
465             var node = this.scope.ctxNode,
466                 grantsContainer;
467             if(node.attributes.nodeRecord && node.attributes.nodeRecord.data.name) {
468                 grantsContainer = node.attributes.nodeRecord.data.name;
469             } else if(node.attributes.container) {
470                 grantsContainer = node.attributes.container;
471             }
472             
473             var window = Tine.widgets.container.GrantsDialog.openWindow({
474                 title: String.format(_('Manage Permissions for {0} "{1}"'), this.nodeName, Ext.util.Format.htmlEncode(grantsContainer.name)),
475                 containerName: this.nodeName,
476                 grantContainer: grantsContainer,
477                 app: this.scope.app.appName
478             });
479         }
480         
481     },
482     
483     /**
484      * manage properties
485      * 
486      */
487     manageProperties: function() {
488         if (this.scope.ctxNode) {
489             var node = this.scope.ctxNode,
490                 grantsContainer,
491                 ctxNodeName;
492             if (node.attributes.nodeRecord && node.attributes.nodeRecord.data.name) {
493                 grantsContainer = node.attributes.nodeRecord.data.name;
494             } else if(node.attributes.container) {
495                 grantsContainer = node.attributes.container;
496             }
497
498             ctxNodeName = grantsContainer.name ? grantsContainer.name : grantsContainer;
499             
500             var window = Tine.widgets.container.PropertiesDialog.openWindow({
501                 title: String.format(_('Properties for {0} "{1}"'), this.nodeName, Ext.util.Format.htmlEncode(ctxNodeName)),
502                 containerName: this.nodeName,
503                 // TODO fix this in Filemanager! sub-nodes do not have a container...
504                 grantContainer: grantsContainer,
505                 app: this.scope.app.appName
506             });
507         }
508     },
509     
510     /**
511      * reload node
512      */
513     reloadNode: function() {
514         if (this.scope.ctxNode) {
515             var tree = this.scope;
516             this.scope.ctxNode.reload(function(node) {
517                 node.expand();
518                 node.select();
519                 // update grid
520                 tree.filterPlugin.onFilterChange();
521             });
522         }
523     }
524     
525 };