0013080: Extend document preview
[tine20] / tine20 / Filemanager / js / NodeGridPanel.js
1 /*
2  * Tine 2.0
3  * 
4  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
5  * @author      Martin Jatho <m.jatho@metaways.de>
6  * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
7  */
8 Ext.ns('Tine.Filemanager');
9
10 require('./nodeActions');
11 require('./nodeContextMenu');
12
13 /**
14  * File grid panel
15  *
16  * @namespace   Tine.Filemanager
17  * @class       Tine.Filemanager.NodeGridPanel
18  * @extends     Tine.widgets.grid.GridPanel
19  *
20  * <p>Node Grid Panel</p>
21  * <p><pre>
22  * </pre></p>
23  *
24  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
25  * @author      Martin Jatho <m.jatho@metaways.de>
26  * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
27  *
28  * @param       {Object} config
29  * @constructor
30  * Create a new Tine.Filemanager.FileGridPanel
31  */
32 Tine.Filemanager.NodeGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
33     /**
34      * @cfg filesProperty
35      * @type String
36      */
37     filesProperty: 'files',
38
39     /**
40      * config values
41      * @private
42      */
43     header: false,
44     border: false,
45     deferredRender: false,
46     autoExpandColumn: 'name',
47     showProgress: true,
48
49     recordClass: Tine.Filemanager.Model.Node,
50     listenMessageBus: true,
51     hasDetailsPanel: false,
52     evalGrants: true,
53     // initialLoadAfterRender: false,
54
55     /**
56      * grid specific
57      * @private
58      */
59     currentFolderNode: null,
60
61     /**
62      * Prevent download and edit of nodes/files and so on. Only allow the selection of items
63      */
64     selectOnly: false,
65
66     previewsEnabled: false,
67
68     /**
69      * inits this cmp
70      * @private
71      */
72     initComponent: function() {
73         Ext.applyIf(this.defaultSortInfo, {field: 'name', direction: 'DESC'});
74         Ext.applyIf(this.gridConfig, {
75             autoExpandColumn: 'name',
76             enableFileDialog: false,
77             enableDragDrop: true,
78             ddGroup: 'fileDDGroup',
79             listeners: {
80                 scope: this,
81                 afterrender: this.initDragDrop
82             }
83         });
84
85         if (this.readOnly) {
86             this.gridConfig.enableDragDrop = false;
87         }
88
89         this.previewsEnabled = Tine.Tinebase.configManager.get('filesystem').createPreviews;
90
91         this.recordProxy = Tine.Filemanager.fileRecordBackend;
92
93         this.gridConfig.cm = this.getColumnModel();
94
95         this.defaultFilters = [
96             {field: 'query', operator: 'contains', value: ''},
97             {field: 'path', operator: 'equals', value: Tine.Tinebase.container.getMyFileNodePath()}
98         ];
99
100         this.plugins = this.plugins || [];
101
102         this.filterToolbar = this.filterToolbar || this.getFilterToolbar();
103
104         this.plugins.push(this.filterToolbar);
105
106         if (!this.readOnly) {
107             this.plugins.push({
108                 ptype: 'ux.browseplugin',
109                 multiple: true,
110                 scope: this,
111                 enableFileDialog: false,
112                 handler: this.onFilesSelect.createDelegate(this)
113             });
114         }
115
116         if (this.hasQuickSearchFilterToolbarPlugin) {
117             this.filterToolbar.getQuickFilterPlugin().criteriaIgnores.push({field: 'path'});
118         }
119
120         Tine.Filemanager.NodeGridPanel.superclass.initComponent.call(this);
121         this.getStore().on('load', this.onLoad.createDelegate(this));
122
123         // // cope with empty selections - dosn't work. It's confusing if e.g. the delte btn is enabled with no selections
124         // this.selectionModel.on('selectionchange', function(sm) {
125         //     if (sm.getSelections().length) {
126         //         return;
127         //     }
128         //
129         //     var _ = window.lodash,
130         //         recordData = _.get(this, 'currentFolderNode.attributes'),
131         //         record = recordData ? Tine.Tinebase.data.Record.setFromJson(JSON.stringify(recordData), this.recordClass) : null;
132         //
133         //     if (record) {
134         //         this.actionUpdater.updateActions(record);
135         //     }
136         // }, this);
137
138     },
139
140     /**
141      * after grid renderd
142      */
143     initDragDrop: function () {
144         var grid = this.grid,
145             view = grid.getView();
146
147         view.dragZone.onBeforeDrag = this.onBeforeDrag.createDelegate(this);
148
149         this.dropZone = new Ext.dd.DropZone(this.getEl(), {
150             ddGroup: 'fileDDGroup',
151             onNodeOver: this.onNodeOver.createDelegate(this),
152             onNodeDrop: this.onNodeDrop.createDelegate(this),
153             getTargetFromEvent: function(e) {
154                 var idx = view.findRowIndex(e.target),
155                     record = grid.getStore().getAt(idx);
156
157                 return record;
158             }
159         })
160     },
161
162     /**
163      * An empty function by default, but provided so that you can perform a custom action before the initial
164      * drag event begins and optionally cancel it.
165      * @param {Object} data An object containing arbitrary data to be shared with drop targets
166      * @param {Event} e The event object
167      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
168      */
169     onBeforeDrag: function(data, e) {
170         var _ = window.lodash,
171             // @TODO: rethink: do I need delte on the record or parent?
172             requiredGrant = e.ctrlKey || e.altKey ? 'readGrant' : 'editGrant';
173
174         return !this.selectionModel.isFilterSelect &&
175             _.reduce(this.selectionModel.getSelections(), function(allowed, record) {
176                 return allowed && !! _.get(record, 'data.account_grants.' + requiredGrant);
177             }, true);
178     },
179
180     /*
181      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from {@link #getTargetFromEvent} for this node)
182      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
183      * @param {Event} e The event
184      * @param {Object} data An object containing arbitrary data supplied by the drag source
185      * @return {String} status The CSS class that communicates the drop status back to the source so that the underlying {@link Ext.dd.StatusProxy} can be updated
186      */
187     onNodeOver: function(record, source, e, data) {
188         var _ = window.lodash,
189             dropAllowed =
190                 record.get('type') == 'folder'
191                 && _.get(record, 'data.account_grants.addGrant', false)
192                 && source == this.grid.getView().dragZone,
193             action = e.ctrlKey || e.altKey ? 'copy' : 'move'
194
195         return dropAllowed ?
196             'tinebase-dd-drop-ok-' + action :
197             Ext.dd.DropZone.prototype.dropNotAllowed;
198     },
199
200     /**
201      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped onto
202      * the drop node.  The default implementation returns false, so it should be overridden to provide the
203      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
204      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
205      * {@link #getTargetFromEvent} for this node)
206      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
207      * @param {Event} e The event
208      * @param {Object} data An object containing arbitrary data supplied by the drag source
209      * @return {Boolean} True if the drop was valid, else false
210      */
211     onNodeDrop: function(target, dd, e, data) {
212         Tine.Filemanager.fileRecordBackend.copyNodes(data.selections, target, !(e.ctrlKey || e.altKey));
213         this.grid.getStore().remove(data.selections);
214         return true;
215     },
216
217     /**
218      * returns cm
219      *
220      * @return Ext.grid.ColumnModel
221      * @private
222      */
223     getColumnModel: function(){
224         var columns = [{
225                 id: 'tags',
226                 header: this.app.i18n._('Tags'),
227                 dataIndex: 'tags',
228                 width: 50,
229                 renderer: Tine.Tinebase.common.tagsRenderer,
230                 sortable: false,
231                 hidden: false
232             }, {
233                 id: 'name',
234                 header: this.app.i18n._("Name"),
235                 width: 70,
236                 sortable: true,
237                 dataIndex: 'name',
238                 renderer: Ext.ux.PercentRendererWithName
239             },{
240                 id: 'size',
241                 header: this.app.i18n._("Size"),
242                 width: 40,
243                 sortable: true,
244                 dataIndex: 'size',
245                 renderer: Tine.Tinebase.common.byteRenderer.createDelegate(this, [2, true], 3)
246             },{
247                 id: 'contenttype',
248                 header: this.app.i18n._("Contenttype"),
249                 width: 50,
250                 sortable: true,
251                 dataIndex: 'contenttype',
252                 renderer: function(value, metadata, record) {
253
254                     var app = Tine.Tinebase.appMgr.get('Filemanager');
255                     if(record.data.type == 'folder') {
256                         return app.i18n._("Folder");
257                     }
258                     else {
259                         return value;
260                     }
261                 }
262             },{
263                 id: 'creation_time',
264                 header: this.app.i18n._("Creation Time"),
265                 width: 50,
266                 sortable: true,
267                 dataIndex: 'creation_time',
268                 renderer: Tine.Tinebase.common.dateTimeRenderer
269             },{
270                 id: 'created_by',
271                 header: this.app.i18n._("Created By"),
272                 width: 50,
273                 sortable: true,
274                 dataIndex: 'created_by',
275                 renderer: Tine.Tinebase.common.usernameRenderer
276             },{
277                 id: 'last_modified_time',
278                 header: this.app.i18n._("Last Modified Time"),
279                 width: 80,
280                 sortable: true,
281                 dataIndex: 'last_modified_time',
282                 renderer: Tine.Tinebase.common.dateTimeRenderer
283             },{
284                 id: 'last_modified_by',
285                 header: this.app.i18n._("Last Modified By"),
286                 width: 50,
287                 sortable: true,
288                 dataIndex: 'last_modified_by',
289                 renderer: Tine.Tinebase.common.usernameRenderer
290             }
291         ];
292
293         if (Tine.Tinebase.configManager.get('filesystem.modLogActive', 'Tinebase')) {
294             columns.push({
295                 id: 'revision_size',
296                 header: this.app.i18n._("Revision Size"),
297                 tooltip: this.app.i18n._("Total size of all available revisions"),
298                 width: 40,
299                 sortable: true,
300                 dataIndex: 'revision_size',
301                 hidden: true,
302                 renderer: Tine.Tinebase.common.byteRenderer.createDelegate(this, [2, true], 3)
303             });
304         }
305
306         return new Ext.grid.ColumnModel({
307             defaults: {
308                 sortable: true,
309                 resizable: true
310             },
311             columns: columns
312         });
313     },
314
315     /**
316      * status column renderer
317      * @param {string} value
318      * @return {string}
319      */
320     statusRenderer: function(value) {
321         return this.app.i18n._hidden(value);
322     },
323
324     /**
325      * Capture space to toggle document preview
326      */
327     onKeyDown: function(e) {
328         Tine.Filemanager.NodeGridPanel.superclass.onKeyDown.apply(this, arguments);
329
330
331         if (!this.previewsEnabled) {
332             return true;
333         }
334
335         // Open preview on space if a node is selected and the node type equals file
336         if (e.getKey() === 32) {
337             this.action_preview.execute()
338         }
339     },
340
341     /**
342      * init ext grid panel
343      * @private
344      */
345     initGrid: function() {
346         Tine.Filemanager.NodeGridPanel.superclass.initGrid.call(this);
347
348         if (this.usePagingToolbar) {
349            this.initPagingToolbar();
350         }
351     },
352
353     /**
354      * inserts a quota Message when using old Browsers with html4upload
355      */
356     initPagingToolbar: function() {
357         if(!this.pagingToolbar || !this.pagingToolbar.rendered) {
358             this.initPagingToolbar.defer(50, this);
359             return;
360         }
361         // old browsers
362         if (!((! Ext.isGecko && window.XMLHttpRequest && window.File && window.FileList) || (Ext.isGecko && window.FileReader))) {
363             var text = new Ext.Panel({padding: 2, html: String.format(this.app.i18n._('The max. Upload Filesize is {0} MB'), Tine.Tinebase.registry.get('maxFileUploadSize') / 1048576 )});
364             this.pagingToolbar.insert(12, new Ext.Toolbar.Separator());
365             this.pagingToolbar.insert(12, text);
366             this.pagingToolbar.doLayout();
367         }
368     },
369
370     /**
371      * returns filter toolbar -> supress OR filters
372      * @private
373      */
374     getFilterToolbar: function(config) {
375         config = config || {};
376         var plugins = [];
377
378         if (this.hasQuickSearchFilterToolbarPlugin) {
379             this.quickSearchFilterToolbarPlugin = new Tine.widgets.grid.FilterToolbarQuickFilterPlugin();
380             plugins.push(this.quickSearchFilterToolbarPlugin);
381         }
382
383         return new Tine.widgets.grid.FilterToolbar(Ext.apply(config, {
384             app: this.app,
385             recordClass: this.recordClass,
386             filterModels: this.recordClass.getFilterModel().concat(this.getCustomfieldFilters()),
387             defaultFilter: 'query',
388             filters: this.defaultFilters || [],
389             plugins: plugins
390         }));
391     },
392
393     /**
394      * returns add action / test
395      *
396      * @return {Object} add action config
397      */
398     getAddAction: function () {
399         return {
400             requiredGrant: 'addGrant',
401             actionType: 'add',
402             text: this.app.i18n._('Upload'),
403             handler: this.onFilesSelect,
404             disabled: true,
405             scope: this,
406             plugins: [{
407                 ptype: 'ux.browseplugin',
408                 multiple: true,
409                 enableFileDrop: false,
410                 disable: true
411             }],
412             iconCls: this.app.appName + 'IconCls',
413             actionUpdater: function(action) {
414                 var _ = window.lodash,
415                     allowAdd = _.get(this, 'currentFolderNode.attributes.account_grants.addGrant', false);
416
417                 action.setDisabled(!allowAdd);
418             }.createDelegate(this)
419         };
420     },
421
422     initLayout: function() {
423         this.supr().initLayout.call(this);
424
425         var northPanel = lodash.find(this.items, function (i) {
426             return i.region == 'north'
427         });
428
429         northPanel.tbar = new Tine.Filemanager.RecursiveFilter({
430             gridPanel: this,
431             hidden: true
432         });
433     },
434
435     /**
436      * init actions with actionToolbar, contextMenu and actionUpdater
437      * @private
438      */
439     initActions: function() {
440         // generic node actions - work on selections grid/tree nodes
441         this.action_createFolder = Tine.Filemanager.nodeActionsMgr.get('createFolder');
442         this.action_editFile = Tine.Filemanager.nodeActionsMgr.get('edit');
443         this.action_deleteRecord = Tine.Filemanager.nodeActionsMgr.get('delete');
444         this.action_download = Tine.Filemanager.nodeActionsMgr.get('download');
445         this.action_moveRecord = Tine.Filemanager.nodeActionsMgr.get('move');
446         this.action_publish = Tine.Filemanager.nodeActionsMgr.get('publish');
447
448         if (this.previewsEnabled) {
449             this.action_preview = Tine.Filemanager.nodeActionsMgr.get('preview');
450         }
451
452         // grid only actions - work on node which is displayed (this.currentFolderNode)
453         // @TODO: fixme - ux problems with filterselect / initialData
454         this.action_upload = new Ext.Action(this.getAddAction());
455         this.action_goUpFolder = new Ext.Action({
456             allowMultiple: true,
457             actionType: 'goUpFolder',
458             text: this.app.i18n._('Folder Up'),
459             handler: this.onLoadParentFolder,
460             iconCls: 'action_filemanager_folder_up',
461             disabled: true,
462             scope: this,
463             actionUpdater: function(action) {
464                 var _ = window.lodash,
465                     path = _.get(this, 'currentFolderNode.attributes.path', false);
466
467                 action.setDisabled(path == '/');
468             }.createDelegate(this)
469         });
470
471         var contextActions = [this.action_deleteRecord, 'rename', this.action_moveRecord, this.action_download, 'resume', 'pause', this.action_editFile, this.action_publish];
472
473         if (this.previewsEnabled) {
474             contextActions.push(this.action_preview);
475         }
476
477         this.contextMenu = Tine.Filemanager.nodeContextMenu.getMenu({
478             nodeName: Tine.Filemanager.Model.Node.getRecordName(),
479             actions: contextActions,
480             scope: this,
481             backend: 'Filemanager',
482             backendModel: 'Node'
483         });
484
485         this.folderContextMenu = Tine.Filemanager.nodeContextMenu.getMenu({
486             nodeName: Tine.Filemanager.Model.Node.getContainerName(),
487             actions: [this.action_deleteRecord, 'rename', this.action_moveRecord, this.action_editFile, this.action_publish],
488             scope: this,
489             backend: 'Filemanager',
490             backendModel: 'Node'
491         });
492
493         this.actionUpdater.addActions(this.contextMenu.items);
494         this.actionUpdater.addActions(this.folderContextMenu.items);
495
496         var actions = [
497             this.action_upload,
498             this.action_createFolder,
499             this.action_goUpFolder,
500             this.action_download,
501             this.action_deleteRecord,
502             this.action_editFile,
503             this.action_publish
504         ];
505
506         if (this.previewsEnabled) {
507             actions.push(this.action_preview);
508         }
509
510         this.actionUpdater.addActions(actions);
511     },
512
513     /**
514      * get the right contextMenu
515      */
516     getContextMenu: function(grid, row, e) {
517         var r = this.store.getAt(row),
518             type = r ? r.get('type') : null;
519
520         return type === 'folder' ? this.folderContextMenu : this.contextMenu;
521     },
522
523     /**
524      * get action toolbar
525      *
526      * @return {Ext.Toolbar}
527      */
528     getActionToolbar: function() {
529         if (! this.actionToolbar) {
530             var items = [
531                 this.splitAddButton ?
532                     Ext.apply(new Ext.SplitButton(this.action_upload), {
533                         scale: 'medium',
534                         rowspan: 2,
535                         iconAlign: 'top',
536                         arrowAlign:'right',
537                         menu: new Ext.menu.Menu({
538                             items: [],
539                             plugins: [{
540                                 ptype: 'ux.itemregistry',
541                                 key:   'Tine.widgets.grid.GridPanel.addButton'
542                             }, {
543                                 ptype: 'ux.itemregistry',
544                                 key:   'Tinebase-MainContextMenu'
545                             }]
546                         })
547                     }) :
548                     Ext.apply(new Ext.Button(this.action_upload), {
549                         scale: 'medium',
550                         rowspan: 2,
551                         iconAlign: 'top'
552                     }),
553
554                 Ext.apply(new Ext.Button(this.action_editFile), {
555                     scale: 'medium',
556                     rowspan: 2,
557                     iconAlign: 'top'
558                 }),
559                 Ext.apply(new Ext.Button(this.action_deleteRecord), {
560                     scale: 'medium',
561                     rowspan: 2,
562                     iconAlign: 'top'
563                 }),
564                 Ext.apply(new Ext.Button(this.action_createFolder), {
565                     scale: 'medium',
566                     rowspan: 2,
567                     iconAlign: 'top'
568                 }),
569                 Ext.apply(new Ext.Button(this.action_goUpFolder), {
570                     scale: 'medium',
571                     rowspan: 2,
572                     iconAlign: 'top'
573                 }),
574                 Ext.apply(new Ext.Button(this.action_download), {
575                     scale: 'medium',
576                     rowspan: 2,
577                     iconAlign: 'top'
578                 }),
579                 Ext.apply(new Ext.Button(this.action_publish), {
580                     scale: 'medium',
581                     rowspan: 2,
582                     iconAlign: 'top'
583                 })
584             ];
585
586             if (this.previewsEnabled) {
587                 items.push(Ext.apply(new Ext.Button(this.action_preview), {
588                     scale: 'medium',
589                     rowspan: 2,
590                     iconAlign: 'top'
591                 }));
592             }
593
594             this.actionToolbar = new Ext.Toolbar({
595                 defaults: {height: 55},
596                 items: [{
597                     xtype: 'buttongroup',
598                     layout: 'toolbar',
599                     buttonAlign: 'left',
600                     columns: 8,
601                     defaults: {minWidth: 60},
602                     items: items
603                 }, this.getActionToolbarItems()]
604             });
605
606             this.actionToolbar.on('resize', this.onActionToolbarResize, this, {buffer: 250});
607             this.actionToolbar.on('show', this.onActionToolbarResize, this);
608
609             if (this.filterToolbar && typeof this.filterToolbar.getQuickFilterField == 'function') {
610                 this.actionToolbar.add('->', this.filterToolbar.getQuickFilterField());
611             }
612         }
613
614         return this.actionToolbar;
615     },
616
617     /**
618      * grid row doubleclick handler
619      *
620      * @param {Tine.Filemanager.NodeGridPanel} grid
621      * @param {} row record
622      * @param {Ext.EventObjet} e
623      */
624     onRowDblClick: function (grid, row, e) {
625         var app = this.app;
626         var rowRecord = grid.getStore().getAt(row);
627         var _ = window.lodash;
628         var prefs = this.app.getRegistry().get('preferences');
629
630         if (prefs.get('dbClickAction') === 'download' && rowRecord.data.type == 'file' && !this.readOnly && _.get(rowRecord, 'data.account_grants.downloadGrant', false)) {
631             Tine.Filemanager.downloadFile(rowRecord);
632         } else if (this.previewsEnabled && prefs.get('dbClickAction') === 'preview' && rowRecord.data.type == 'file' && !this.readOnly && _.get(rowRecord, 'data.account_grants.readGrant', false)) {
633             this.action_preview.execute();
634         } else if (rowRecord.data.type == 'folder') {
635             var treePanel = this.treePanel || app.getMainScreen().getWestPanel().getContainerTreePanel();
636
637             var currentFolderNode = treePanel.getNodeById(rowRecord.id);
638
639             if (currentFolderNode) {
640                 currentFolderNode.select();
641                 currentFolderNode.expand();
642                 this.currentFolderNode = currentFolderNode;
643             } else {
644                 // get   ftb path filter
645                 this.filterToolbar.filterStore.each(function (filter) {
646                     var field = filter.get('field');
647                     if (field === 'path') {
648                         filter.set('value', '');
649                         filter.set('value', rowRecord.data);
650                         filter.formFields.value.setValue(rowRecord.get('path'));
651
652                         this.filterToolbar.onFiltertrigger();
653                         return false;
654                     }
655                 }, this);
656             }
657         }
658     },
659
660     /**
661      * on remove handler
662      *
663      * @param {} button
664      * @param {} event
665      */
666     onRemove: function (button, event) {
667         var selectedRows = this.selectionModel.getSelections();
668         for (var i = 0; i < selectedRows.length; i += 1) {
669             this.store.remove(selectedRows[i]);
670             var upload = Tine.Tinebase.uploadManager.getUpload(selectedRows[i].get('uploadKey'));
671
672             if (upload) {
673                 upload.setPaused(true);
674             }
675         }
676     },
677
678     /**
679      * populate grid store
680      *
681      * @param {} record
682      */
683     loadRecord: function (record) {
684         if (record && record.get(this.filesProperty)) {
685             var files = record.get(this.filesProperty);
686             for (var i = 0; i < files.length; i += 1) {
687                 var file = new Ext.ux.file.Upload.file(files[i]);
688                 file.set('status', 'complete');
689                 file.set('nodeRecord', new Tine.Filemanager.Model.Node(file.data));
690                 this.store.add(file);
691             }
692         }
693     },
694
695     /**
696      * upload new file and add to store
697      *
698      * @param {ux.BrowsePlugin} fileSelector
699      * @param {} e
700      */
701     onFilesSelect: function (fileSelector, event) {
702         var app = Tine.Tinebase.appMgr.get('Filemanager'),
703             grid = this,
704             targetNode = grid.currentFolderNode,
705             gridStore = grid.store,
706             rowIndex = false,
707             targetFolderPath = grid.currentFolderNode.attributes.path,
708             addToGrid = true,
709             dropAllowed = false,
710             nodeRecord = null;
711
712         if(event && event.getTarget()) {
713             rowIndex = grid.getView().findRowIndex(event.getTarget());
714         }
715
716
717         if(targetNode.attributes) {
718             nodeRecord = targetNode.attributes.nodeRecord;
719         }
720
721         if(rowIndex !== false && rowIndex > -1) {
722             var newTargetNode = gridStore.getAt(rowIndex);
723             if(newTargetNode && newTargetNode.data.type == 'folder') {
724                 targetFolderPath = newTargetNode.data.path;
725                 addToGrid = false;
726                 nodeRecord = new Tine.Filemanager.Model.Node(newTargetNode.data);
727             }
728         }
729
730         if(!nodeRecord.isDropFilesAllowed()) {
731             Ext.MessageBox.alert(
732                     i18n._('Upload Failed'),
733                     app.i18n._('Putting files in this folder is not allowed!')
734             ).setIcon(Ext.MessageBox.ERROR);
735
736             return;
737         }
738
739         var files = fileSelector.getFileList();
740
741         if(files.length > 0) {
742             grid.pagingToolbar.refresh.disable();
743         }
744
745         var filePathsArray = [], uploadKeyArray = [];
746
747         Ext.each(files, function (file) {
748             var fileRecord = Tine.Filemanager.Model.Node.createFromFile(file),
749                 filePath = targetFolderPath + '/' + fileRecord.get('name');
750
751             fileRecord.set('path', filePath);
752             var existingRecordIdx = gridStore.find('name', fileRecord.get('name'));
753             if(existingRecordIdx < 0) {
754                 gridStore.add(fileRecord);
755             }
756
757             var upload = new Ext.ux.file.Upload({
758                 fmDirector: grid,
759                 file: file,
760                 fileSelector: fileSelector,
761                 id: filePath
762             });
763
764             var uploadKey = Tine.Tinebase.uploadManager.queueUpload(upload);
765
766             filePathsArray.push(filePath);
767             uploadKeyArray.push(uploadKey);
768
769         }, this);
770
771         var params = {
772                 filenames: filePathsArray,
773                 type: "file",
774                 tempFileIds: [],
775                 forceOverwrite: false
776         };
777         Tine.Filemanager.fileRecordBackend.createNodes(params, uploadKeyArray, true);
778     },
779
780     /**
781      * grid on load handler
782      *
783      * @param grid
784      * @param records
785      * @param options
786      */
787     onLoad: function(store, records, options){
788         var grid = this;
789
790         for(var i=records.length-1; i>=0; i--) {
791             var record = records[i];
792             if(record.get('type') == 'file' && (!record.get('size') || record.get('size') == 0)) {
793                 var upload = Tine.Tinebase.uploadManager.getUpload(record.get('path'));
794
795                 if(upload) {
796                       if(upload.fileRecord && record.get('name') == upload.fileRecord.get('name')) {
797                           grid.updateNodeRecord(record, upload.fileRecord);
798                           record.afterEdit();
799                     }
800                 }
801             }
802         }
803     },
804
805     /**
806      * update grid nodeRecord with fileRecord data
807      *
808      * @param nodeRecord
809      * @param fileRecord
810      */
811     updateNodeRecord: function(nodeRecord, fileRecord) {
812         for(var field in fileRecord.fields) {
813             nodeRecord.set(field, fileRecord.get(field));
814         }
815         nodeRecord.fileRecord = fileRecord;
816     }
817 });