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