0012932: file picker dialog in fileuploadgrid
[tine20] / tine20 / Filemanager / js / NodeTreePanel.js
1 /*
2  * Tine 2.0
3  *
4  * @package     Tinebase
5  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
6  * @author      Philipp Schüle <p.schuele@metaways.de>
7  * @copyright   Copyright (c) 2010-2012 Metaways Infosystems GmbH (http://www.metaways.de)
8  */
9
10 Ext.ns('Tine.Filemanager');
11
12 /**
13  * @namespace Tine.Filemanager
14  * @class Tine.Filemanager.NodeTreePanel
15  * @extends Tine.widgets.container.TreePanel
16  *
17  * @author Martin Jatho <m.jatho@metaways.de>
18  */
19
20 Tine.Filemanager.NodeTreePanel = function(config) {
21     Ext.apply(this, config);
22
23     this.addEvents(
24         /**
25          * @event containeradd
26          * Fires when a folder was added
27          * @param {folder} the new folder
28          */
29         'containeradd',
30         /**
31          * @event containerdelete
32          * Fires when a folder got deleted
33          * @param {folder} the deleted folder
34          */
35         'containerdelete',
36         /**
37          * @event containerrename
38          * Fires when a folder got renamed
39          * @param {folder} the renamed folder
40          */
41         'containerrename'
42     );
43
44     Tine.Filemanager.NodeTreePanel.superclass.constructor.call(this);
45 };
46
47 Ext.extend(Tine.Filemanager.NodeTreePanel, Tine.widgets.container.TreePanel, {
48
49     filterMode : 'filterToolbar',
50
51     recordClass : Tine.Filemanager.Model.Node,
52
53     allowMultiSelection : false,
54
55     defaultContainerPath: '/personal',
56
57     ddGroup: 'fileDDGroup',
58
59     enableDD: true,
60
61     initComponent: function() {
62         if (!this.readOnly) {
63             this.on('containeradd', this.onFolderAdd, this);
64             this.on('containerrename', this.onFolderRename, this);
65             this.on('containerdelete', this.onFolderDelete, this);
66             this.on('nodedragover', this.onNodeDragOver, this);
67         }
68
69         if (! this.app) {
70             this.app = Tine.Tinebase.appMgr.get('Filemanager');
71         }
72
73         if (!this.readOnly) {
74             Tine.Tinebase.uploadManager.on('update', this.onUpdate);
75         }
76
77         if (this.readOnly) {
78             this.hasContextMenu = false;
79             this.enableDD = false;
80         }
81
82         Tine.Filemanager.NodeTreePanel.superclass.initComponent.call(this);
83
84         this.plugins = this.plugins || [];
85
86         if (!this.readOnly) {
87             this.plugins.push({
88                 ptype : 'ux.browseplugin',
89                 enableFileDialog: false,
90                 multiple : true,
91                 handler : this.dropIntoTree
92             });
93         }
94     },
95
96     /**
97      * Setups drop zone
98      */
99     initDrop: function() {
100         // init drop zone
101         this.dropConfig = {
102             ddGroup: this.ddGroup || 'fileDDGroup',
103             appendOnly: this.ddAppendOnly === true,
104             /**
105              * @todo check acl!
106              * @todo combine with repeated code from isValidDropPoint. DRY!
107              */
108             onNodeOver : function(n, dd, e, data) {
109                 var preventDrop = false,
110                     selectionContainsFiles = false;
111
112                 if (dd.dragData.selections) {
113                     for (var i=0; i<dd.dragData.selections.length; i++) {
114                         if (n.node.id == dd.dragData.selections[i].id) {
115                             preventDrop = true;
116                         }
117                         if(dd.dragData.selections[i].data.type == 'file') {
118                             selectionContainsFiles = true;
119                         }
120                     }
121                 }
122                 else if(dd.dragData.node && dd.dragData.node.id == n.node.id) {
123                     preventDrop = true;
124                 }
125
126                 if(selectionContainsFiles && !n.node.attributes.account_grants) {
127                     preventDrop = true;
128                 }
129
130                 if(n.node.isAncestor(dd.dragData.node)) {
131                     preventDrop = true;
132                 }
133
134                 return n.node.attributes.nodeRecord.isCreateFolderAllowed()
135                     && (!dd.dragData.node || dd.dragData.node.attributes.nodeRecord.isDragable())
136                     && !preventDrop ? 'x-dd-drop-ok' : false;
137             },
138
139             /**
140              * this is called on drop
141              *
142              * @TODO: combine with repeated code from onNodeOver. DRY!
143              */
144             isValidDropPoint: function(n, op, dd, e){
145                 var preventDrop = false,
146                     selectionContainsFiles = false;
147
148                 if (dd.dragData.selections) {
149                     for(var i=0; i<dd.dragData.selections.length; i++) {
150                         if (n.node.id == dd.dragData.selections[i].id) {
151                             preventDrop = true;
152                         }
153
154                         if(dd.dragData.selections[i].data.type == 'file') {
155                             selectionContainsFiles = true;
156                         }
157                     }
158                 }
159                 else if(dd.dragData.node && dd.dragData.node.id == n.node.id) {
160                     preventDrop = true;
161                 }
162
163                 if(selectionContainsFiles && !n.node.attributes.account_grants) {
164                     preventDrop = true;
165                 }
166
167                 if(n.node.isAncestor(dd.dragData.node)) {
168                     preventDrop = true;
169                 }
170
171                 return n.node.attributes.nodeRecord.isCreateFolderAllowed()
172                         && (!dd.dragData.node || dd.dragData.node.attributes.nodeRecord.isDragable())
173                         && !preventDrop;
174             },
175
176             completeDrop: function(de) {
177                 var ns = de.dropNode, p = de.point, t = de.target;
178                 t.ui.endDrop();
179                 this.tree.fireEvent("nodedrop", de);
180             },
181         };
182
183         this.dragConfig = {
184             ddGroup: this.ddGroup || 'fileDDGroup',
185             scroll: this.ddScroll,
186             /**
187              * tree node dragzone modified, dragged node doesn't get selected
188              *
189              * @param e
190              */
191             onInitDrag: function(e) {
192                 var data = this.dragData;
193                 this.tree.eventModel.disable();
194                 this.proxy.update("");
195                 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
196                 this.tree.fireEvent("startdrag", this.tree, data.node, e);
197             }
198         };
199
200         this.plugins = this.plugins || [];
201         this.plugins.push({
202             ptype : 'ux.browseplugin',
203             enableFileDialog: false,
204             multiple : true,
205             handler : this.dropIntoTree
206         });
207     },
208
209     // TODO we should use getRoot of the superclass - this currently does not work (personal folders)
210     getRoot: function(extraItems)
211     {
212         return {
213             path: '/',
214             cls: 'tinebase-tree-hide-collapsetool',
215             expanded: true,
216             children: [{
217                 path: this.getRootPath(),
218                 id: 'personal'
219             }, {
220                 path: '/shared',
221                 id: 'shared'
222             }, {
223                 path: '/personal',
224                 id: 'otherUsers'
225             }]
226         };
227     },
228
229     /**
230      * Tine.widgets.tree.FilterPlugin
231      * returns a filter plugin to be used in a grid
232      */
233     // Tine.widgets.tree.FilterPlugin
234     // Tine.Filemanager.PathFilterPlugin
235     getFilterPlugin: function() {
236         if (!this.filterPlugin) {
237             this.filterPlugin = new Tine.Filemanager.PathFilterPlugin({
238                 treePanel: this,
239                 field: 'path',
240                 nodeAttributeField: 'path'
241             });
242         }
243
244         return this.filterPlugin;
245     },
246
247     /**
248      * returns the personal root path
249      * @returns {String}
250      */
251     getRootPath: function() {
252         return Tine.Tinebase.container.getMyFileNodePath();
253     },
254
255     /**
256      * returns params for async request
257      *
258      * @param {Ext.tree.TreeNode} node
259      * @return {Object}
260      */
261     onBeforeLoad: function(node) {
262
263         var path = node.attributes.path;
264         var type = Tine.Tinebase.container.path2type(path);
265         var owner = Tine.Tinebase.container.pathIsPersonalNode(path);
266         var loginName = Tine.Tinebase.registry.get('currentAccount').accountLoginName;
267
268         if (type === 'personal' && owner != loginName) {
269             type = 'otherUsers';
270         }
271
272         var newPath = path;
273
274         if (type === 'personal' && owner) {
275             var pathParts = path.toString().split('/');
276             newPath = '/' + pathParts[1] + '/' + loginName;
277             if(pathParts[3]) {
278                 newPath += '/' + pathParts[3];
279             }
280         }
281
282         var params = {
283             method: 'Filemanager.searchNodes',
284             application: this.app.appName,
285             owner: owner,
286             filter: [
287                      {field: 'path', operator:'equals', value: newPath},
288                      {field: 'type', operator:'equals', value: 'folder'}
289                      ],
290             paging: {dir: 'ASC', limit: 50, sort: 'name', start: 0}
291         };
292
293         return params;
294     },
295
296     onBeforeCreateNode: function(attr) {
297         Tine.Filemanager.NodeTreePanel.superclass.onBeforeCreateNode.apply(this, arguments);
298
299         attr.leaf = false;
300
301         if(attr.name && typeof attr.name == 'object') {
302             Ext.apply(attr, {
303                 text: Ext.util.Format.htmlEncode(attr.name.name),
304                 qtip: Tine.Tinebase.common.doubleEncode(attr.name.name)
305             });
306         }
307
308         // copy 'real' data to a node record NOTE: not a full record as we have no record reader here
309         var nodeData = Ext.copyTo({}, attr, Tine.Filemanager.Model.Node.getFieldNames());
310         attr.nodeRecord = new Tine.Filemanager.Model.Node(nodeData);
311     },
312
313     /**
314      * initiates tree context menues
315      *
316      * @private
317      */
318     initContextMenu: function() {
319
320         this.contextMenuUserFolder = Tine.widgets.tree.ContextMenu.getMenu({
321             nodeName: this.app.i18n._(this.containerName),
322             actions: ['add', 'reload', 'delete', 'rename', 'properties'],
323             scope: this,
324             backend: 'Filemanager',
325             backendModel: 'Node'
326         });
327
328         this.contextMenuRootFolder = Tine.widgets.tree.ContextMenu.getMenu({
329             nodeName: this.app.i18n._(this.containerName),
330             actions: ['add', 'reload'],
331             scope: this,
332             backend: 'Filemanager',
333             backendModel: 'Node'
334         });
335
336         this.contextMenuOtherUserFolder = Tine.widgets.tree.ContextMenu.getMenu({
337             nodeName: this.app.i18n._(this.containerName),
338             actions: ['reload'],
339             scope: this,
340             backend: 'Filemanager',
341             backendModel: 'Node'
342         });
343
344         this.contextMenuContainerFolder = Tine.widgets.tree.ContextMenu.getMenu({
345             nodeName: this.app.i18n._(this.containerName),
346             actions: ['add', 'reload', 'delete', 'rename', 'grants', 'properties'],
347             scope: this,
348             backend: 'Filemanager',
349             backendModel: 'Node'
350         });
351
352         this.contextMenuReloadFolder = Tine.widgets.tree.ContextMenu.getMenu({
353             nodeName: this.app.i18n._(this.containerName),
354             actions: ['reload', 'properties'],
355             scope: this,
356             backend: 'Filemanager',
357             backendModel: 'Node'
358         });
359     },
360
361     /**
362      * @private
363      * - select default path
364      */
365     afterRender: function() {
366         Tine.Filemanager.NodeTreePanel.superclass.afterRender.call(this);
367     },
368
369     /**
370      * show context menu
371      *
372      * @param {Ext.tree.TreeNode} node
373      * @param {Ext.EventObject} event
374      */
375     onContextMenu: function(node, event) {
376
377         var currentAccount = Tine.Tinebase.registry.get('currentAccount');
378
379         this.ctxNode = node;
380         var container = node.attributes.nodeRecord.data,
381             path = container.path;
382
383         if (! Ext.isString(path) || node.isRoot) {
384             return;
385         }
386
387         Tine.log.debug('Tine.Filemanager.NodeTreePanel::onContextMenu - context node:');
388         Tine.log.debug(node);
389
390         if (node.id == 'otherUsers' || (node.parentNode && node.parentNode.id == 'otherUsers')) {
391             this.contextMenuOtherUserFolder.showAt(event.getXY());
392         } else if (node.id == 'personal' || node.id == 'shared') {
393             this.contextMenuRootFolder.showAt(event.getXY());
394         } else if (path.match(/^\/shared/)
395                 && (Tine.Tinebase.common.hasRight('admin', this.app.appName)
396                     || Tine.Tinebase.common.hasRight('manage_shared_folders', this.app.appName))
397                     || container.account_grants && container.account_grants.adminGrant
398                 )
399             {
400             if (typeof container.name == 'object') {
401                 this.contextMenuContainerFolder.showAt(event.getXY());
402             } else {
403                 this.contextMenuUserFolder.showAt(event.getXY());
404             }
405         } else if (path.match(/^\/shared/)){
406             this.contextMenuReloadFolder.showAt(event.getXY());
407         } else if (path.match(/^\/personal/) && path.match('/personal/' + currentAccount.accountLoginName)) {
408             if (typeof container.name == 'object') {
409                 this.contextMenuContainerFolder.showAt(event.getXY());
410             } else {
411                 this.contextMenuUserFolder.showAt(event.getXY());
412             }
413         } else if (path.match(/^\/personal/) && container.account_grants) {
414             this.contextMenuUserFolder.showAt(event.getXY());
415         }
416     },
417
418     /**
419      * updates grid actions
420      * @todo move to grid / actionUpdater
421      *
422      * @param {} sm     SelectionModel
423      * @param {Ext.tree.TreeNode} node
424      */
425     updateActions: function(sm, node) {
426         var grid = this.app.getMainScreen().getCenterPanel();
427
428         grid.action_deleteRecord.disable();
429         grid.action_upload.disable();
430
431         if (!!node && !!node.isRoot) {
432             grid.action_goUpFolder.disable();
433         } else {
434             grid.action_goUpFolder.enable();
435         }
436
437         if (node && node.attributes && node.attributes.nodeRecord.isCreateFolderAllowed()) {
438             grid.action_createFolder.enable();
439         } else {
440             grid.action_createFolder.disable();
441         }
442
443         if (node && node.attributes && node.attributes.nodeRecord.isDropFilesAllowed()) {
444             grid.action_upload.enable();
445         } else {
446             grid.action_upload.disable();
447         }
448     },
449
450     /**
451      * called when tree selection changes
452      *
453      * @param {} sm     SelectionModel
454      * @param {Ext.tree.TreeNode} node
455      */
456     onSelectionChange: function(sm, node) {
457         this.updateActions(sm, node);
458         var grid = this.app.getMainScreen().getCenterPanel();
459
460         grid.currentFolderNode = node;
461         Tine.Filemanager.NodeTreePanel.superclass.onSelectionChange.call(this, sm, node);
462
463     },
464
465     /**
466      * convert filesystem path to treePath
467      *
468      * NOTE: only the first depth gets converted!
469      *       fs pathes of not yet loaded tree nodes can't be converted!
470      *
471      * @param {String} containerPath
472      * @return {String} tree path
473      */
474     getTreePath: function(containerPath) {
475         var treePath = '/' + this.getRootNode().id + (containerPath !== '/' ? containerPath : '');
476
477         // replace personal with otherUsers if personal && ! personal/myaccountid
478         var matches = containerPath.match(/^\/personal\/{0,1}([^\/]*)\/{0,1}/i);
479         if (matches) {
480             if (matches[1] != Tine.Tinebase.registry.get('currentAccount').accountLoginName) {
481                 treePath = treePath.replace('personal', 'otherUsers');
482             } else {
483                 treePath = treePath.replace('personal/'  + Tine.Tinebase.registry.get('currentAccount').accountLoginName, 'personal');
484             }
485         }
486
487         return treePath;
488     },
489
490     /**
491      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Ext.data.Node#getPath}
492      *
493      * NOTE: path does not consist of id's starting from the second depth
494      *
495      * @param {String} path
496      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)
497      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
498      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
499      */
500     expandPath : function(path, attr, callback){
501         var keys = path.split(this.pathSeparator);
502         var curNode = this.root;
503         var curPath = curNode.attributes.path;
504         var index = 1;
505         var f = function(){
506             if(++index == keys.length){
507                 if(callback){
508                     callback(true, curNode);
509                 }
510                 return;
511             }
512
513             if (index > 2) {
514                 var c = curNode.findChild('path', curPath + '/' + keys[index]);
515             } else {
516                 var c = curNode.findChild('id', keys[index]);
517             }
518             if(!c){
519                 if(callback){
520                     callback(false, curNode);
521                 }
522                 return;
523             }
524             curNode = c;
525             curPath = c.attributes.path;
526             c.expand(false, false, f);
527         };
528         curNode.expand(false, false, f);
529     },
530
531     /**
532      * files/folder got dropped on node
533      *
534      * @param {Object} dropEvent
535      * @private
536      */
537     onBeforeNodeDrop: function(dropEvent) {
538         var nodes, target = dropEvent.target;
539
540         if(dropEvent.data.selections) {
541             nodes = dropEvent.data.grid.selModel.selections.items;
542         }
543
544         if(!nodes && dropEvent.data.node) {
545             nodes = [dropEvent.data.node];
546         }
547
548         Tine.Filemanager.fileRecordBackend.copyNodes(nodes, target, !dropEvent.rawEvent.ctrlKey);
549
550         dropEvent.dropStatus = true;
551         return true;
552     },
553
554     /**
555      * folder delete handler
556      */
557     onFolderDelete: function(node) {
558         var grid = this.app.getMainScreen().getCenterPanel();
559         if(grid.currentFolderNode.isAncestor && typeof grid.currentFolderNode.isAncestor == 'function'
560             && grid.currentFolderNode.isAncestor(node)) {
561             node.parentNode.select();
562         }
563         grid.getStore().reload();
564     },
565
566     /**
567      * clone a tree node / create a node from grid node
568      *
569      * @param node
570      * @returns {Ext.tree.AsyncTreeNode}
571      */
572     cloneTreeNode: function(node, target) {
573         var targetPath = target.attributes.path,
574             newPath = '',
575             copy;
576
577         if(node.attributes) {
578             var nodeName = node.attributes.name;
579             if(typeof nodeName == 'object') {
580                 nodeName = nodeName.name;
581             }
582             newPath = targetPath + '/' + nodeName;
583
584             copy = new Ext.tree.AsyncTreeNode({text: node.text, path: newPath, name: node.attributes.name
585                 , nodeRecord: node.attributes.nodeRecord, account_grants: node.attributes.account_grants});
586         }
587         else {
588             var nodeName = node.data.name;
589             if(typeof nodeName == 'object') {
590                 nodeName = nodeName.name;
591             }
592
593             var nodeData = Ext.copyTo({}, node.data, Tine.Filemanager.Model.Node.getFieldNames());
594             var newNodeRecord = new Tine.Filemanager.Model.Node(nodeData);
595
596             newPath = targetPath + '/' + nodeName;
597             copy = new Ext.tree.AsyncTreeNode({text: nodeName, path: newPath, name: node.data.name
598                 , nodeRecord: newNodeRecord, account_grants: node.data.account_grants});
599         }
600
601         copy.attributes.nodeRecord.beginEdit();
602         copy.attributes.nodeRecord.set('path', newPath);
603         copy.attributes.nodeRecord.endEdit();
604
605         copy.parentNode = target;
606         return copy;
607     },
608
609     /**
610      * create Tree node by given node data
611      *
612      * @param nodeData
613      * @param target
614      * @returns {Ext.tree.AsyncTreeNode}
615      */
616     createTreeNode: function(nodeData, target) {
617         var nodeName = nodeData.name;
618         if(typeof nodeName == 'object') {
619             nodeName = nodeName.name;
620         }
621
622         var newNodeRecord = new Tine.Filemanager.Model.Node(nodeData);
623
624         var newNode = new Ext.tree.AsyncTreeNode({
625             text: nodeName,
626             path: nodeData.path,
627             name: nodeData.name,
628             nodeRecord: newNodeRecord,
629             account_grants: nodeData.account_grants,
630             id: nodeData.id
631         })
632
633         newNode.attributes.nodeRecord.beginEdit();
634         newNode.attributes.nodeRecord.set('path', nodeData.path);
635         newNode.attributes.nodeRecord.endEdit();
636
637         newNode.parentNode = target;
638         return newNode;
639
640     },
641
642     /**
643      * TODO: move to Upload class or elsewhere??
644      * updating fileRecord after creating node
645      *
646      * @param response
647      * @param request
648      * @param upload
649      */
650     onNodeCreated: function(response, request, upload) {
651
652         var app = Tine.Tinebase.appMgr.get('Filemanager'),
653         grid = app.getMainScreen().getCenterPanel();
654
655         var record = Ext.util.JSON.decode(response.responseText);
656
657         var fileRecord = upload.fileRecord;
658         fileRecord.beginEdit();
659         fileRecord.set('contenttype', record.contenttype);
660         fileRecord.set('created_by', Tine.Tinebase.registry.get('currentAccount'));
661         fileRecord.set('creation_time', record.creation_time);
662         fileRecord.set('revision', record.revision);
663         fileRecord.set('last_modified_by', record.last_modified_by);
664         fileRecord.set('last_modified_time', record.last_modified_time);
665         fileRecord.set('status', 'complete');
666         fileRecord.set('progress', 100);
667         fileRecord.set('name', record.name);
668         fileRecord.set('path', record.path);
669         fileRecord.commit(false);
670
671         upload.fireEvent('update', 'uploadfinished', upload, fileRecord);
672
673         grid.pagingToolbar.refresh.enable();
674
675     },
676
677     /**
678      * copies uploaded temporary file to target location
679      *
680      * @param upload    {Ext.ux.file.Upload}
681      * @param file  {Ext.ux.file.Upload.file}
682      */
683     onUploadComplete: function(upload, file) {
684         if (this.readOnly) {
685             return;
686         }
687
688         var app = Tine.Tinebase.appMgr.get('Filemanager'),
689             treePanel = app.getMainScreen().getWestPanel().getContainerTreePanel();
690
691      // check if we are responsible for the upload
692         if (upload.fmDirector != treePanel) return;
693
694         // $filename, $type, $tempFileId, $forceOverwrite
695         Ext.Ajax.request({
696             timeout: 10*60*1000, // Overriding Ajax timeout - important!
697             params: {
698                 method: 'Filemanager.createNode',
699                 filename: upload.id,
700                 type: 'file',
701                 tempFileId: file.get('id'),
702                 forceOverwrite: true
703             },
704             success: treePanel.onNodeCreated.createDelegate(this, [upload], true),
705             failure: treePanel.onNodeCreated.createDelegate(this, [upload], true)
706         });
707
708     },
709
710     /**
711      * on upload failure
712      *
713      * @private
714      */
715     onUploadFail: function () {
716         Ext.MessageBox.alert(
717             i18n._('Upload Failed'),
718             i18n._('Could not upload file. Filesize could be too big. Please notify your Administrator.')
719         ).setIcon(Ext.MessageBox.ERROR);
720     },
721
722     /**
723      * add folder handler
724      */
725     onFolderAdd: function(nodeData) {
726
727         var app = Tine.Tinebase.appMgr.get('Filemanager'),
728             grid = app.getMainScreen().getCenterPanel();
729
730         grid.getStore().reload();
731         if(nodeData.error) {
732             Tine.log.debug(nodeData);
733         }
734     },
735
736     /**
737      * handles renaming of a tree node / aka folder
738      */
739     onFolderRename: function(nodeData, node, newName) {
740         var app = Tine.Tinebase.appMgr.get('Filemanager'),
741             grid = app.getMainScreen().getCenterPanel();
742
743         if(nodeData[0]) {
744             nodeData = nodeData[0];
745         }
746
747         node.attributes.nodeRecord.beginEdit();
748         if (typeof node.attributes.name == 'object') {
749             node.attributes.name.name = newName;
750             node.attributes.nodeRecord.data.name.name = newName;
751         } else {
752             node.attributes.name = newName;
753             node.attributes.nodeRecord.set('name', newName);
754         }
755         node.attributes.path = nodeData.path;
756         node.attributes.nodeRecord.set('path', nodeData.path);
757         node.attributes.nodeRecord.commit(false);
758
759         grid.currenFolderNode = node;
760
761         Tine.Filemanager.NodeTreePanel.superclass.onSelectionChange.call(this, this.getSelectionModel(), node);
762
763     },
764
765     /**
766      * upload update handler
767      *
768      * @param change {String} kind of change
769      * @param upload {Ext.ux.file.Upload} upload
770      * @param fileRecord {file} fileRecord
771      *
772      */
773     onUpdate: function(change, upload, fileRecord) {
774
775         var app = Tine.Tinebase.appMgr.get('Filemanager'),
776             grid = app.getMainScreen().getCenterPanel(),
777             treePanel = app.getMainScreen().getWestPanel().getContainerTreePanel(),
778             rowsToUpdate = grid.getStore().query('name', fileRecord.get('name'));
779
780         if(change == 'uploadstart') {
781             Tine.Tinebase.uploadManager.onUploadStart();
782         }
783         else if(change == 'uploadfailure') {
784             treePanel.onUploadFail();
785         }
786
787         if(rowsToUpdate.get(0)) {
788             if(change == 'uploadcomplete') {
789                 treePanel.onUploadComplete(upload, fileRecord);
790             }
791             else if(change == 'uploadfinished') {
792                 rowsToUpdate.get(0).set('size', upload.fileSize);
793                 rowsToUpdate.get(0).set('contenttype', fileRecord.get('contenttype'));
794             }
795             rowsToUpdate.get(0).afterEdit();
796             rowsToUpdate.get(0).commit(false);
797         }
798     },
799
800     /**
801      * handels tree drop of object from outside the browser
802      *
803      * @param fileSelector
804      * @param targetNodeId
805      */
806     dropIntoTree: function(fileSelector, event) {
807
808         var treePanel = fileSelector.component,
809             app = treePanel.app,
810             grid = app.getMainScreen().getCenterPanel(),
811             targetNode,
812             targetNodePath;
813
814
815         var targetNodeId;
816         var treeNodeAttribute = event.getTarget('div').attributes['ext:tree-node-id'];
817         if(treeNodeAttribute) {
818             targetNodeId = treeNodeAttribute.nodeValue;
819             targetNode = treePanel.getNodeById(targetNodeId);
820             targetNodePath = targetNode.attributes.path;
821
822         }
823
824         if(!targetNode.attributes.nodeRecord.isDropFilesAllowed()) {
825             Ext.MessageBox.alert(
826                     i18n._('Upload Failed'),
827                     app.i18n._('Putting files in this folder is not allowed!')
828                 ).setIcon(Ext.MessageBox.ERROR);
829
830             return;
831         }
832
833         var files = fileSelector.getFileList(),
834             filePathsArray = [],
835             uploadKeyArray = [],
836             addToGridStore = false;
837
838         Ext.each(files, function (file) {
839
840             var fileName = file.name || file.fileName,
841                 filePath = targetNodePath + '/' + fileName;
842
843             var upload = new Ext.ux.file.Upload({
844                 fmDirector: treePanel,
845                 file: file,
846                 fileSelector: fileSelector,
847                 id: filePath
848             });
849
850             var uploadKey = Tine.Tinebase.uploadManager.queueUpload(upload);
851
852             filePathsArray.push(filePath);
853             uploadKeyArray.push(uploadKey);
854
855             addToGridStore = grid.currentFolderNode.id === targetNodeId;
856
857         }, this);
858
859         var params = {
860                 filenames: filePathsArray,
861                 type: "file",
862                 tempFileIds: [],
863                 forceOverwrite: false
864         };
865         Tine.Filemanager.fileRecordBackend.createNodes(params, uploadKeyArray, addToGridStore);
866     }
867 });