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