Merge branch '2013.03'
[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 /**
11  * File grid panel
12  * 
13  * @namespace   Tine.Filemanager
14  * @class       Tine.Filemanager.NodeGridPanel
15  * @extends     Tine.widgets.grid.GridPanel
16  * 
17  * <p>Node Grid Panel</p>
18  * <p><pre>
19  * </pre></p>
20  * 
21  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
22  * @author      Martin Jatho <m.jatho@metaways.de>
23  * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
24  * 
25  * @param       {Object} config
26  * @constructor
27  * Create a new Tine.Filemanager.FileGridPanel
28  */
29 Tine.Filemanager.NodeGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {   
30     /**
31      * @cfg filesProperty
32      * @type String
33      */
34     filesProperty: 'files',
35     
36     /**
37      * @cfg showTopToolbar
38      * @type Boolean
39      * TODO     think about that -> when we deactivate the top toolbar, we lose the dropzone for files!
40      */
41     //showTopToolbar: null,
42     
43     /**
44      * config values
45      * @private
46      */
47     header: false,
48     border: false,
49     deferredRender: false,
50     autoExpandColumn: 'name',
51     showProgress: true,
52     
53     recordClass: Tine.Filemanager.Model.Node,
54     hasDetailsPanel: false,
55     evalGrants: true,
56     
57     /**
58      * grid specific
59      * @private
60      */
61     defaultSortInfo: {field: 'name', direction: 'DESC'},
62     gridConfig: {
63         autoExpandColumn: 'name',
64         enableFileDialog: false,
65         enableDragDrop: true,
66         ddGroup: 'fileDDGroup'
67     },
68      
69     ddGroup : 'fileDDGroup',  
70     currentFolderNode : '/',
71     
72     /**
73      * inits this cmp
74      * @private
75      */
76     initComponent: function() {
77         this.recordProxy = Tine.Filemanager.fileRecordBackend;
78         
79         this.gridConfig.cm = this.getColumnModel();
80         
81         this.defaultFilters = [
82             {field: 'query', operator: 'contains', value: ''},
83             {field: 'path', operator: 'equals', value: '/'}
84         ];
85         
86         this.filterToolbar = this.filterToolbar || this.getFilterToolbar();
87         this.filterToolbar.getQuickFilterPlugin().criteriaIgnores.push({field: 'path'});
88         
89         this.plugins = this.plugins || [];
90         this.plugins.push(this.filterToolbar);
91         this.plugins.push({
92             ptype: 'ux.browseplugin',
93             multiple: true,
94             scope: this,
95             enableFileDialog: false,
96             handler: this.onFilesSelect
97         });
98         
99         Tine.Filemanager.NodeGridPanel.superclass.initComponent.call(this);
100         this.getStore().on('load', this.onLoad);
101         Tine.Tinebase.uploadManager.on('update', this.onUpdate);
102     },
103     
104     initFilterPanel: function() {},
105     
106     /**
107      * after render handler
108      */
109     afterRender: function() {
110         Tine.Filemanager.NodeGridPanel.superclass.afterRender.call(this);
111         this.action_upload.setDisabled(true);
112         this.initDropTarget();
113         this.currentFolderNode = this.app.getMainScreen().getWestPanel().getContainerTreePanel().getRootNode();
114     },
115     
116     /**
117      * returns cm
118      * 
119      * @return Ext.grid.ColumnModel
120      * @private
121      * 
122      * TODO    add more columns
123      */
124     getColumnModel: function(){
125         var columns = [{ 
126                 id: 'tags',
127                 header: this.app.i18n._('Tags'),
128                 dataIndex: 'tags',
129                 width: 50,
130                 renderer: Tine.Tinebase.common.tagsRenderer,
131                 sortable: false,
132                 hidden: false
133             }, {
134                 id: 'name',
135                 header: this.app.i18n._("Name"),
136                 width: 70,
137                 sortable: true,
138                 dataIndex: 'name',
139                 renderer: Ext.ux.PercentRendererWithName
140             },{
141                 id: 'size',
142                 header: this.app.i18n._("Size"),
143                 width: 40,
144                 sortable: true,
145                 dataIndex: 'size',
146                 renderer: Tine.Tinebase.common.byteRenderer.createDelegate(this, [2, true], 3)
147             },{
148                 id: 'contenttype',
149                 header: this.app.i18n._("Contenttype"),
150                 width: 50,
151                 sortable: true,
152                 dataIndex: 'contenttype',
153                 renderer: function(value, metadata, record) {
154                     
155                     var app = Tine.Tinebase.appMgr.get('Filemanager');
156                     if(record.data.type == 'folder') {
157                         return app.i18n._("Folder");
158                     }
159                     else {
160                         return value;
161                     }
162                 }
163             },
164 //            {
165 //                id: 'revision',
166 //                header: this.app.i18n._("Revision"),
167 //                width: 10,
168 //                sortable: true,
169 //                dataIndex: 'revision',
170 //                renderer: function(value, metadata, record) {
171 //                    if(record.data.type == 'folder') {
172 //                        return '';
173 //                    }
174 //                    else {
175 //                        return value;
176 //                    }
177 //                }
178 //            },
179             {
180                 id: 'creation_time',
181                 header: this.app.i18n._("Creation Time"),
182                 width: 50,
183                 sortable: true,
184                 dataIndex: 'creation_time',
185                 renderer: Tine.Tinebase.common.dateTimeRenderer
186                 
187             },{
188                 id: 'created_by',
189                 header: this.app.i18n._("Created By"),
190                 width: 50,
191                 sortable: true,
192                 dataIndex: 'created_by',
193                 renderer: Tine.Tinebase.common.usernameRenderer
194             },{
195                 id: 'last_modified_time',
196                 header: this.app.i18n._("Last Modified Time"),
197                 width: 80,
198                 sortable: true,
199                 dataIndex: 'last_modified_time',
200                 renderer: Tine.Tinebase.common.dateTimeRenderer
201             },{
202                 id: 'last_modified_by',
203                 header: this.app.i18n._("Last Modified By"),
204                 width: 50,
205                 sortable: true,
206                 dataIndex: 'last_modified_by',
207                 renderer: Tine.Tinebase.common.usernameRenderer 
208             }
209         ];
210         
211         return new Ext.grid.ColumnModel({
212             defaults: {
213                 sortable: true,
214                 resizable: true
215             },
216             columns: columns
217         });
218     },
219     
220     /**
221      * status column renderer
222      * @param {string} value
223      * @return {string}
224      */
225     statusRenderer: function(value) {
226         return this.app.i18n._hidden(value);
227     },
228     
229     /**
230      * init ext grid panel
231      * @private
232      */
233     initGrid: function() {
234         Tine.Filemanager.NodeGridPanel.superclass.initGrid.call(this);
235         
236         if (this.usePagingToolbar) {
237            this.initPagingToolbar();
238         }
239     },
240     
241     /**
242      * inserts a quota Message when using old Browsers with html4upload
243      */
244     initPagingToolbar: function() {
245         if(!this.pagingToolbar || !this.pagingToolbar.rendered) {
246             this.initPagingToolbar.defer(50, this);
247             return;
248         }
249         // old browsers
250         if (!((! Ext.isGecko && window.XMLHttpRequest && window.File && window.FileList) || (Ext.isGecko && window.FileReader))) {
251             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 )});
252             this.pagingToolbar.insert(12, new Ext.Toolbar.Separator());
253             this.pagingToolbar.insert(12, text);
254             this.pagingToolbar.doLayout();
255         }
256     },
257     
258     /**
259      * returns filter toolbar -> supress OR filters
260      * @private
261      */
262     getFilterToolbar: function(config) {
263         config = config || {};
264         var plugins = [];
265         if (! Ext.isDefined(this.hasQuickSearchFilterToolbarPlugin) || this.hasQuickSearchFilterToolbarPlugin) {
266             this.quickSearchFilterToolbarPlugin = new Tine.widgets.grid.FilterToolbarQuickFilterPlugin();
267             plugins.push(this.quickSearchFilterToolbarPlugin);
268         }
269         
270         return new Tine.widgets.grid.FilterToolbar(Ext.apply(config, {
271             app: this.app,
272             recordClass: this.recordClass,
273             filterModels: this.recordClass.getFilterModel().concat(this.getCustomfieldFilters()),
274             defaultFilter: 'query',
275             filters: this.defaultFilters || [],
276             plugins: plugins
277         }));
278     },
279     
280     /**
281      * returns add action / test
282      * 
283      * @return {Object} add action config
284      */
285     getAddAction: function () {
286         return {
287             requiredGrant: 'addGrant',
288             actionType: 'add',
289             text: this.app.i18n._('Upload'),
290             handler: this.onFilesSelect,
291             disabled: true,
292             scope: this,
293             plugins: [{
294                 ptype: 'ux.browseplugin',
295                 multiple: true,
296                 enableFileDrop: false,
297                 disable: true
298             }],
299             iconCls: this.app.appName + 'IconCls'
300         };
301     },
302     
303     /**
304      * init actions with actionToolbar, contextMenu and actionUpdater
305      * @private
306      */
307     initActions: function() {
308         this.action_upload = new Ext.Action(this.getAddAction());
309         
310         this.action_editFile = new Ext.Action({
311             requiredGrant: 'editGrant',
312             allowMultiple: false,
313             text: this.app.i18n._('Edit Properties'),
314             handler: this.onEditFile,
315             iconCls: 'action_edit_file',
316             disabled: false,
317             actionType: 'edit',
318             scope: this
319         });
320         this.action_createFolder = new Ext.Action({
321             requiredGrant: 'addGrant',
322             actionType: 'reply',
323             allowMultiple: true,
324             text: this.app.i18n._('Create Folder'),
325             handler: this.onCreateFolder,
326             iconCls: 'action_create_folder',
327             disabled: true,
328             scope: this
329         });
330         
331         this.action_goUpFolder = new Ext.Action({
332 //            requiredGrant: 'readGrant',
333             allowMultiple: true,
334             actionType: 'goUpFolder',
335             text: this.app.i18n._('Folder Up'),
336             handler: this.onLoadParentFolder,
337             iconCls: 'action_filemanager_folder_up',
338             disabled: true,
339             scope: this
340         });
341         
342         this.action_download = new Ext.Action({
343             requiredGrant: 'readGrant',
344             allowMultiple: false,
345             actionType: 'download',
346             text: this.app.i18n._('Save locally'),
347             handler: this.onDownload,
348             iconCls: 'action_filemanager_save_all',
349             disabled: true,
350             scope: this
351         });
352         
353         this.action_deleteRecord = new Ext.Action({
354             requiredGrant: 'deleteGrant',
355             allowMultiple: true,
356             singularText: this.app.i18n._('Delete'),
357             pluralText: this.app.i18n._('Delete'),
358             translationObject: this.i18nDeleteActionText ? this.app.i18n : Tine.Tinebase.translation,
359             text: this.app.i18n._('Delete'),
360             handler: this.onDeleteRecords,
361             disabled: true,
362             iconCls: 'action_delete',
363             scope: this
364         });
365         
366         this.contextMenu = Tine.Filemanager.GridContextMenu.getMenu({
367             nodeName: Tine.Filemanager.Model.Node.getRecordName(),
368             actions: ['delete', 'rename', 'download', 'resume', 'pause', 'edit'],
369             scope: this,
370             backend: 'Filemanager',
371             backendModel: 'Node'
372         });
373         
374         this.folderContextMenu = Tine.Filemanager.GridContextMenu.getMenu({
375             nodeName: this.app.i18n._(this.app.getMainScreen().getWestPanel().getContainerTreePanel().containerName),
376             actions: ['delete', 'rename'],
377             scope: this,
378             backend: 'Filemanager',
379             backendModel: 'Node'
380         });
381         
382         this.actionUpdater.addActions(this.contextMenu.items);
383         this.actionUpdater.addActions(this.folderContextMenu.items);
384         
385         this.actionUpdater.addActions([
386            this.action_createFolder,
387            this.action_goUpFolder,
388            this.action_download,
389            this.action_deleteRecord,
390            this.action_editFile
391        ]);
392     },
393     
394     /**
395      * get the right contextMenu
396      */
397     getContextMenu: function(grid, row, e) {
398         var r = this.store.getAt(row),
399             type = r ? r.get('type') : null;
400             
401         return type === 'folder' ? this.folderContextMenu : this.contextMenu;
402     },
403     
404     /**
405      * get action toolbar
406      * 
407      * @return {Ext.Toolbar}
408      */
409     getActionToolbar: function() {
410         if (! this.actionToolbar) {
411             this.actionToolbar = new Ext.Toolbar({
412                 defaults: {height: 55},
413                 items: [{
414                     xtype: 'buttongroup',
415                     columns: 8,
416                     defaults: {minWidth: 60},
417                     items: [
418                         this.splitAddButton ? 
419                         Ext.apply(new Ext.SplitButton(this.action_upload), {
420                             scale: 'medium',
421                             rowspan: 2,
422                             iconAlign: 'top',
423                             arrowAlign:'right',
424                             menu: new Ext.menu.Menu({
425                                 items: [],
426                                 plugins: [{
427                                     ptype: 'ux.itemregistry',
428                                     key:   'Tine.widgets.grid.GridPanel.addButton'
429                                 }]
430                             })
431                         }) : 
432                         Ext.apply(new Ext.Button(this.action_upload), {
433                             scale: 'medium',
434                             rowspan: 2,
435                             iconAlign: 'top'
436                         }),
437                         
438                         Ext.apply(new Ext.Button(this.action_editFile), {
439                             scale: 'medium',
440                             rowspan: 2,
441                             iconAlign: 'top'
442                         }),
443                         Ext.apply(new Ext.Button(this.action_deleteRecord), {
444                             scale: 'medium',
445                             rowspan: 2,
446                             iconAlign: 'top'
447                         }),
448                         Ext.apply(new Ext.Button(this.action_createFolder), {
449                             scale: 'medium',
450                             rowspan: 2,
451                             iconAlign: 'top'
452                         }),
453                         Ext.apply(new Ext.Button(this.action_goUpFolder), {
454                             scale: 'medium',
455                             rowspan: 2,
456                             iconAlign: 'top'
457                         }),
458                         Ext.apply(new Ext.Button(this.action_download), {
459                             scale: 'medium',
460                             rowspan: 2,
461                             iconAlign: 'top'
462                         })
463                  ]
464                 }, this.getActionToolbarItems()]
465             });
466             
467             if (this.filterToolbar && typeof this.filterToolbar.getQuickFilterField == 'function') {
468                 this.actionToolbar.add('->', this.filterToolbar.getQuickFilterField());
469             }
470         }
471         
472         return this.actionToolbar;
473     },
474     
475     /**
476      * opens the edit dialog
477      */
478     onEditFile: function() {
479         var sel = this.getGrid().getSelectionModel().getSelections();
480
481         if(sel.length == 1) {
482             var record = new Tine.Filemanager.Model.Node(sel[0].data);
483             var window = Tine.Filemanager.NodeEditDialog.openWindow({record: record});
484         }
485         
486         window.on('saveAndClose', function() {
487             this.getGrid().store.reload();
488         }, this);
489     },
490     
491     /**
492      * create folder in current position
493      * 
494      * @param {Ext.Component} button
495      * @param {Ext.EventObject} event
496      */
497     onCreateFolder: function(button, event) {
498         var app = this.app,
499             nodeName = Tine.Filemanager.Model.Node.getContainerName();
500         
501         Ext.MessageBox.prompt(_('New Folder'), _('Please enter the name of the new folder:'), function(_btn, _text) {
502             var currentFolderNode = app.getMainScreen().getCenterPanel().currentFolderNode;
503             if(currentFolderNode && _btn == 'ok') {
504                 if (! _text) {
505                     Ext.Msg.alert(String.format(_('No {0} added'), nodeName), String.format(_('You have to supply a {0} name!'), nodeName));
506                     return;
507                 }
508                 
509                 var filename = currentFolderNode.attributes.path + '/' + _text;
510                 Tine.Filemanager.fileRecordBackend.createFolder(filename);
511                 
512             }
513         }, this);  
514     },
515     
516     /**
517      * delete selected files / folders
518      * 
519      * @param {Ext.Component} button
520      * @param {Ext.EventObject} event
521      */
522     onDeleteRecords: function(button, event) {
523         var app = this.app,
524             nodeName = '',
525             sm = app.getMainScreen().getCenterPanel().selectionModel,
526             nodes = sm.getSelections();
527         
528         if(nodes && nodes.length) {
529             for(var i=0; i<nodes.length; i++) {
530                 var currNodeData = nodes[i].data;
531                 
532                 if(typeof currNodeData.name == 'object') {
533                     nodeName += currNodeData.name.name + '<br />';
534                 }
535                 else {
536                     nodeName += currNodeData.name + '<br />';
537                 }
538             }
539         }
540         
541         this.conflictConfirmWin = Tine.widgets.dialog.FileListDialog.openWindow({
542             modal: true,
543             allowCancel: false,
544             height: 180,
545             width: 300,
546             title: app.i18n._('Do you really want to delete the following files?'),
547             text: nodeName,
548             scope: this,
549             handler: function(button){
550                 if (nodes && button == 'yes') {
551                     this.store.remove(nodes);
552                     this.pagingToolbar.refresh.disable();
553                     Tine.Filemanager.fileRecordBackend.deleteItems(nodes);
554                 }
555                 
556                 for(var i=0; i<nodes.length; i++) {
557                     var node = nodes[i];
558                     
559                     if(node.fileRecord) {
560                         var upload = Tine.Tinebase.uploadManager.getUpload(node.fileRecord.get('uploadKey'));
561                         upload.setPaused(true);
562                         Tine.Tinebase.uploadManager.unregisterUpload(upload.id);
563                     }
564                     
565                 }
566             }
567         }, this);
568     },
569     
570     /**
571      * go up one folder
572      * 
573      * @param {Ext.Component} button
574      * @param {Ext.EventObject} event
575      */
576     onLoadParentFolder: function(button, event) {
577         var app = this.app,
578             currentFolderNode = app.getMainScreen().getCenterPanel().currentFolderNode;
579         
580         if(currentFolderNode && currentFolderNode.parentNode) {
581             app.getMainScreen().getCenterPanel().currentFolderNode = currentFolderNode.parentNode;
582             currentFolderNode.parentNode.select();
583         }
584     },
585     
586     /**
587      * grid row doubleclick handler
588      * 
589      * @param {Tine.Filemanager.NodeGridPanel} grid
590      * @param {} row record
591      * @param {Ext.EventObjet} e
592      */
593     onRowDblClick: function(grid, row, e) {
594         var app = this.app;
595         var rowRecord = grid.getStore().getAt(row);
596         
597         if(rowRecord.data.type == 'file') {
598             var downloadPath = rowRecord.data.path;
599             var downloader = new Ext.ux.file.Download({
600                 params: {
601                     method: 'Filemanager.downloadFile',
602                     requestType: 'HTTP',
603                     id: '',
604                     path: downloadPath
605                 }
606             }).start();
607         }
608         
609         else if (rowRecord.data.type == 'folder'){
610             var treePanel = app.getMainScreen().getWestPanel().getContainerTreePanel();
611             
612             var currentFolderNode;
613             if(rowRecord.data.path == '/personal/system') {
614                 currentFolderNode = treePanel.getNodeById('personal');
615             }
616             else if(rowRecord.data.path == '/shared') {
617                 currentFolderNode = treePanel.getNodeById('shared');
618             }
619             else if(rowRecord.data.path == '/personal') {
620                 currentFolderNode = treePanel.getNodeById('otherUsers');
621             }
622             else {
623                 currentFolderNode = treePanel.getNodeById(rowRecord.id);
624             }
625             if(currentFolderNode) {
626                 currentFolderNode.select();
627                 currentFolderNode.expand();
628                 app.getMainScreen().getCenterPanel().currentFolderNode = currentFolderNode;
629             } else {
630                 // get ftb path filter
631                 this.filterToolbar.filterStore.each(function(filter) {
632                     var field = filter.get('field');
633                     if (field === 'path') {
634                         filter.set('value', '');
635                         filter.set('value', rowRecord.data);
636                         filter.formFields.value.setValue(rowRecord.get('path'));
637                         
638                         this.filterToolbar.onFiltertrigger();
639                         return false;
640                     }
641                 }, this);
642             }
643         }
644     }, 
645         
646     /**
647      * on upload failure
648      * 
649      * @private
650      */
651     onUploadFail: function () {
652         Ext.MessageBox.alert(
653             _('Upload Failed'), 
654             _('Could not upload file. Filesize could be too big. Please notify your Administrator. Max upload size: ') 
655             + Tine.Tinebase.common.byteRenderer(Tine.Tinebase.registry.get('maxFileUploadSize')) 
656         ).setIcon(Ext.MessageBox.ERROR);
657         
658         var app = Tine.Tinebase.appMgr.get('Filemanager'),
659             grid = app.getMainScreen().getCenterPanel();
660         grid.pagingToolbar.refresh.enable();
661     },
662     
663     /**
664      * on remove handler
665      * 
666      * @param {} button
667      * @param {} event
668      */
669     onRemove: function (button, event) {
670         var selectedRows = this.selectionModel.getSelections();
671         for (var i = 0; i < selectedRows.length; i += 1) {
672             this.store.remove(selectedRows[i]);
673             var upload = Tine.Tinebase.uploadManager.getUpload(selectedRows[i].get('uploadKey'));
674             upload.setPaused(true);
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      * copies uploaded temporary file to target location
697      * 
698      * @param upload  {Ext.ux.file.Upload}
699      * @param file  {Ext.ux.file.Upload.file} 
700      */
701     onUploadComplete: function(upload, file) {
702         var app = Tine.Tinebase.appMgr.get('Filemanager'),
703             grid = app.getMainScreen().getCenterPanel();
704         
705         // check if we are responsible for the upload
706         if (upload.fmDirector != grid) return;
707         
708         // $filename, $type, $tempFileId, $forceOverwrite
709         Ext.Ajax.request({
710             timeout: 10*60*1000, // Overriding Ajax timeout - important!
711             params: {
712                 method: 'Filemanager.createNode',
713                 filename: upload.id,
714                 type: 'file',
715                 tempFileId: file.get('id'),
716                 forceOverwrite: true
717             },
718             success: grid.onNodeCreated.createDelegate(this, [upload], true), 
719             failure: grid.onNodeCreated.createDelegate(this, [upload], true)
720         });
721         
722     },
723     
724     /**
725      * TODO: move to Upload class or elsewhere??
726      * updating fileRecord after creating node
727      * 
728      * @param response
729      * @param request
730      * @param upload
731      */
732     onNodeCreated: function(response, request, upload) {
733         var record = Ext.util.JSON.decode(response.responseText);
734                 
735         var fileRecord = upload.fileRecord;
736         fileRecord.beginEdit();
737         fileRecord.set('contenttype', record.contenttype);
738         fileRecord.set('created_by', Tine.Tinebase.registry.get('currentAccount'));
739         fileRecord.set('creation_time', record.creation_time);
740         fileRecord.set('revision', record.revision);
741         fileRecord.set('last_modified_by', record.last_modified_by);
742         fileRecord.set('last_modified_time', record.last_modified_time);
743         fileRecord.set('name', record.name);
744         fileRecord.set('path', record.path);
745         fileRecord.set('status', 'complete');
746         fileRecord.set('progress', 100);
747         fileRecord.commit(false);
748        
749         upload.fireEvent('update', 'uploadfinished', upload, fileRecord);
750         
751         var app = Tine.Tinebase.appMgr.get('Filemanager'),
752             grid = app.getMainScreen().getCenterPanel();
753         
754         var allRecordsComplete = true;
755         var storeItems = grid.getStore().getRange();
756         for(var i=0; i<storeItems.length; i++) {
757             if(storeItems[i].get('status') && storeItems[i].get('status') !== 'complete') {
758                 allRecordsComplete = false;
759                 break;
760             }
761         }
762         
763         if(allRecordsComplete) {
764             grid.pagingToolbar.refresh.enable();
765         }
766     },
767     
768     /**
769      * upload new file and add to store
770      * 
771      * @param {ux.BrowsePlugin} fileSelector
772      * @param {} e
773      */
774     onFilesSelect: function (fileSelector, event) {
775         var app = Tine.Tinebase.appMgr.get('Filemanager'),
776             grid = app.getMainScreen().getCenterPanel(),
777             targetNode = grid.currentFolderNode,
778             gridStore = grid.store,
779             rowIndex = false,
780             targetFolderPath = grid.currentFolderNode.attributes.path,
781             addToGrid = true,
782             dropAllowed = false,
783             nodeRecord = null;
784         
785         if(event && event.getTarget()) {
786             rowIndex = grid.getView().findRowIndex(event.getTarget());
787         }
788         
789         
790         if(targetNode.attributes) {
791             nodeRecord = targetNode.attributes.nodeRecord;
792         }
793         
794         if(rowIndex !== false && rowIndex > -1) {
795             var newTargetNode = gridStore.getAt(rowIndex);
796             if(newTargetNode && newTargetNode.data.type == 'folder') {
797                 targetFolderPath = newTargetNode.data.path;
798                 addToGrid = false;
799                 nodeRecord = new Tine.Filemanager.Model.Node(newTargetNode.data);
800             }
801         }
802         
803         if(!nodeRecord.isDropFilesAllowed()) {
804             Ext.MessageBox.alert(
805                     _('Upload Failed'), 
806                     app.i18n._('Putting files in this folder is not allowed!')
807             ).setIcon(Ext.MessageBox.ERROR);
808             
809             return;
810         }    
811         
812         var files = fileSelector.getFileList();
813         
814         if(files.length > 0) {
815             grid.pagingToolbar.refresh.disable();
816         }
817         
818         var filePathsArray = [], uploadKeyArray = [];
819         
820         Ext.each(files, function (file) {
821             var fileRecord = Tine.Filemanager.Model.Node.createFromFile(file),
822                 filePath = targetFolderPath + '/' + fileRecord.get('name');
823             
824             fileRecord.set('path', filePath);
825             var existingRecordIdx = gridStore.find('name', fileRecord.get('name'));
826             if(existingRecordIdx < 0) {
827                 gridStore.add(fileRecord);
828             }
829             
830             var upload = new Ext.ux.file.Upload({
831                 fmDirector: grid,
832                 file: file,
833                 fileSelector: fileSelector,
834                 id: filePath
835             });
836             
837             var uploadKey = Tine.Tinebase.uploadManager.queueUpload(upload);
838             
839             filePathsArray.push(filePath);
840             uploadKeyArray.push(uploadKey);
841             
842         }, this);
843         
844         var params = {
845                 filenames: filePathsArray,
846                 type: "file",
847                 tempFileIds: [],
848                 forceOverwrite: false
849         };
850         Tine.Filemanager.fileRecordBackend.createNodes(params, uploadKeyArray, true);
851     },
852     
853     /**
854      * download file
855      * 
856      * @param {} button
857      * @param {} event
858      */
859     onDownload: function(button, event) {
860         
861         var app = Tine.Tinebase.appMgr.get('Filemanager'),
862             grid = app.getMainScreen().getCenterPanel(),
863             selectedRows = grid.selectionModel.getSelections();
864         
865         var fileRow = selectedRows[0];
866                
867         var downloadPath = fileRow.data.path;
868         var downloader = new Ext.ux.file.Download({
869             params: {
870                 method: 'Filemanager.downloadFile',
871                 requestType: 'HTTP',
872                 id: '',
873                 path: downloadPath
874             }
875         }).start();
876     },
877     
878     /**
879      * grid on load handler
880      * 
881      * @param grid
882      * @param records
883      * @param options
884      */
885     onLoad: function(store, records, options){
886         var app = Tine.Tinebase.appMgr.get('Filemanager'),
887             grid = app.getMainScreen().getCenterPanel();
888         
889         for(var i=records.length-1; i>=0; i--) {
890             var record = records[i];
891             if(record.get('type') == 'file' && (!record.get('size') || record.get('size') == 0)) {
892                 var upload = Tine.Tinebase.uploadManager.getUpload(record.get('path'));
893                 
894                 if(upload) {
895                       if(upload.fileRecord && record.get('name') == upload.fileRecord.get('name')) {
896                           grid.updateNodeRecord(record, upload.fileRecord);
897                           record.afterEdit();
898                     }
899                 }
900             }
901         }
902     },
903     
904     /**
905      * update grid nodeRecord with fileRecord data
906      * 
907      * @param nodeRecord
908      * @param fileRecord
909      */
910     updateNodeRecord: function(nodeRecord, fileRecord) {
911         for(var field in fileRecord.fields) {
912             nodeRecord.set(field, fileRecord.get(field));
913         }
914         nodeRecord.fileRecord = fileRecord;
915     },
916     
917     /**
918      * upload update handler
919      * 
920      * @param change {String} kind of change
921      * @param upload {Ext.ux.file.Upload} upload
922      * @param fileRecord {file} fileRecord
923      * 
924      */
925     onUpdate: function(change, upload, fileRecord) {
926         var app = Tine.Tinebase.appMgr.get('Filemanager'),
927             grid = app.getMainScreen().getCenterPanel(),
928             rowsToUpdate = grid.getStore().query('name', fileRecord.get('name'));
929         
930         if(change == 'uploadstart') {
931             Tine.Tinebase.uploadManager.onUploadStart();
932         }
933         else if(change == 'uploadfailure') {
934             grid.onUploadFail();
935         }
936         
937         if(rowsToUpdate.get(0)) {
938             if(change == 'uploadcomplete') {
939                 grid.onUploadComplete(upload, fileRecord);
940             }
941             else if(change == 'uploadfinished') {
942                 rowsToUpdate.get(0).set('size', fileRecord.get('size'));
943                 rowsToUpdate.get(0).set('contenttype', fileRecord.get('contenttype'));
944             }
945             rowsToUpdate.get(0).afterEdit();
946             rowsToUpdate.get(0).commit(false);
947         }
948     },
949     
950     /**
951      * init grid drop target
952      * 
953      * @TODO DRY cleanup
954      */
955     initDropTarget: function(){
956         var ddrow = new Ext.dd.DropTarget(this.getEl(), {
957             ddGroup : 'fileDDGroup',  
958             
959             notifyDrop : function(dragSource, e, data){
960                 
961                 if(data.node && data.node.attributes && !data.node.attributes.nodeRecord.isDragable()) {
962                     return false;
963                 }
964                 
965                 var app = Tine.Tinebase.appMgr.get(Tine.Filemanager.fileRecordBackend.appName),
966                     grid = app.getMainScreen().getCenterPanel(),
967                     treePanel = app.getMainScreen().getWestPanel().getContainerTreePanel(),
968                     dropIndex = grid.getView().findRowIndex(e.target),
969                     target = grid.getStore().getAt(dropIndex),
970                     nodes = data.selections ? data.selections : [data.node];
971                 
972                 if((!target || target.data.type === 'file') && grid.currentFolderNode) {
973                     target = grid.currentFolderNode;
974                 }
975                 
976                 if(!target) {
977                     return false;
978                 }
979                 
980                 for(var i=0; i<nodes.length; i++) {
981                     if(nodes[i].id == target.id) {
982                         return false;
983                     }
984                 }
985                 
986                 var targetNode = treePanel.getNodeById(target.id);
987                 if(targetNode && targetNode.isAncestor(nodes[0])) {
988                     return false;
989                 }
990                 
991                 Tine.Filemanager.fileRecordBackend.copyNodes(nodes, target, !e.ctrlKey);
992                 return true;
993             },
994             
995             notifyOver : function( dragSource, e, data ) {
996                 if(data.node && data.node.attributes && !data.node.attributes.nodeRecord.isDragable()) {
997                     return false;
998                 }
999                 
1000                 var app = Tine.Tinebase.appMgr.get(Tine.Filemanager.fileRecordBackend.appName),
1001                     grid = app.getMainScreen().getCenterPanel(),
1002                     dropIndex = grid.getView().findRowIndex(e.target),
1003                     treePanel = app.getMainScreen().getWestPanel().getContainerTreePanel(),
1004                     target= grid.getStore().getAt(dropIndex),
1005                     nodes = data.selections ? data.selections : [data.node];
1006                 
1007                 if((!target || (target.data && target.data.type === 'file')) && grid.currentFolderNode) {
1008                     target = grid.currentFolderNode;
1009                 }
1010                 
1011                 if(!target) {
1012                     return false;
1013                 }
1014                 
1015                 for(var i=0; i<nodes.length; i++) {
1016                     if(nodes[i].id == target.id) {
1017                         return false;
1018                     }
1019                 }
1020                 
1021                 var targetNode = treePanel.getNodeById(target.id);
1022                 if(targetNode && targetNode.isAncestor(nodes[0])) {
1023                     return false;
1024                 }
1025                 
1026                 return this.dropAllowed;
1027             }
1028         });
1029     }
1030 });