ContainerOwner strategy always add container owner if exists
[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.action_publish = new Ext.Action({
367             allowMultiple: false,
368             singularText: this.app.i18n._('Publish'),
369             folderText: this.app.i18n._('Publish'),
370             translationObject: this.i18nDeleteActionText ? this.app.i18n : Tine.Tinebase.translation,
371             text: this.app.i18n._('Publish'),
372             handler: this.onPublishFile,
373             disabled: true,
374             iconCls: 'action_publish',
375             scope: this,
376             actionUpdater: function(action, grants, records) {
377                 if (records.length != 1) {
378                     action.disable();
379                 } else {
380                     action.enable();
381                 }
382             }
383         });
384         
385         this.contextMenu = Tine.Filemanager.GridContextMenu.getMenu({
386             nodeName: Tine.Filemanager.Model.Node.getRecordName(),
387             actions: ['delete', 'rename', 'download', 'resume', 'pause', 'edit', 'publish'],
388             scope: this,
389             backend: 'Filemanager',
390             backendModel: 'Node'
391         });
392         
393         this.folderContextMenu = Tine.Filemanager.GridContextMenu.getMenu({
394             nodeName: this.app.i18n._(this.app.getMainScreen().getWestPanel().getContainerTreePanel().containerName),
395             actions: ['delete', 'rename', 'edit', 'publish'],
396             scope: this,
397             backend: 'Filemanager',
398             backendModel: 'Node'
399         });
400         
401         this.actionUpdater.addActions(this.contextMenu.items);
402         this.actionUpdater.addActions(this.folderContextMenu.items);
403         
404         this.actionUpdater.addActions([
405            this.action_createFolder,
406            this.action_goUpFolder,
407            this.action_download,
408            this.action_deleteRecord,
409            this.action_editFile,
410            this.action_publish
411        ]);
412     },
413     
414     onPublishFile: function() {
415         var selections = this.selectionModel.getSelections();
416         
417         if (selections.length != 1) {
418             return;
419         }
420         
421         var date = new Date();
422         date.setDate(date.getDate() + 30);
423         
424         var record = new Tine.Filemanager.Model.DownloadLink({node_id: selections[0].id, expiry_time: date});
425         Tine.Filemanager.downloadLinkRecordBackend.saveRecord(record, {success: function(record) {
426             
427             // TODO: add mail-button
428             Ext.MessageBox.show({
429                 title: selections[0].data.type == 'folder' ? this.app.i18n._('Folder has been published successfully') : this.app.i18n._('File has been published successfully'), 
430                 msg: String.format(this.app.i18n._("Url: {0}") + '<br />' + this.app.i18n._("Valid Until: {1}"), record.get('url'), record.get('expiry_time')), 
431                 width:900,
432                 buttons: Ext.Msg.OK,
433                 icon: Ext.MessageBox.INFO,
434                 scope: this
435             });
436         }, failure: Tine.Tinebase.ExceptionHandler.handleRequestException, scope: this});
437     },
438     
439     /**
440      * get the right contextMenu
441      */
442     getContextMenu: function(grid, row, e) {
443         var r = this.store.getAt(row),
444             type = r ? r.get('type') : null;
445             
446         return type === 'folder' ? this.folderContextMenu : this.contextMenu;
447     },
448     
449     /**
450      * get action toolbar
451      * 
452      * @return {Ext.Toolbar}
453      */
454     getActionToolbar: function() {
455         if (! this.actionToolbar) {
456             this.actionToolbar = new Ext.Toolbar({
457                 defaults: {height: 55},
458                 items: [{
459                     xtype: 'buttongroup',
460                     columns: 8,
461                     defaults: {minWidth: 60},
462                     items: [
463                         this.splitAddButton ? 
464                         Ext.apply(new Ext.SplitButton(this.action_upload), {
465                             scale: 'medium',
466                             rowspan: 2,
467                             iconAlign: 'top',
468                             arrowAlign:'right',
469                             menu: new Ext.menu.Menu({
470                                 items: [],
471                                 plugins: [{
472                                     ptype: 'ux.itemregistry',
473                                     key:   'Tine.widgets.grid.GridPanel.addButton'
474                                 }]
475                             })
476                         }) : 
477                         Ext.apply(new Ext.Button(this.action_upload), {
478                             scale: 'medium',
479                             rowspan: 2,
480                             iconAlign: 'top'
481                         }),
482                         
483                         Ext.apply(new Ext.Button(this.action_editFile), {
484                             scale: 'medium',
485                             rowspan: 2,
486                             iconAlign: 'top'
487                         }),
488                         Ext.apply(new Ext.Button(this.action_deleteRecord), {
489                             scale: 'medium',
490                             rowspan: 2,
491                             iconAlign: 'top'
492                         }),
493                         Ext.apply(new Ext.Button(this.action_createFolder), {
494                             scale: 'medium',
495                             rowspan: 2,
496                             iconAlign: 'top'
497                         }),
498                         Ext.apply(new Ext.Button(this.action_goUpFolder), {
499                             scale: 'medium',
500                             rowspan: 2,
501                             iconAlign: 'top'
502                         }),
503                         Ext.apply(new Ext.Button(this.action_download), {
504                             scale: 'medium',
505                             rowspan: 2,
506                             iconAlign: 'top'
507                         }),
508                         Ext.apply(new Ext.Button(this.action_publish), {
509                             scale: 'medium',
510                             rowspan: 2,
511                             iconAlign: 'top'
512                         })
513                  ]
514                 }, this.getActionToolbarItems()]
515             });
516             
517             if (this.filterToolbar && typeof this.filterToolbar.getQuickFilterField == 'function') {
518                 this.actionToolbar.add('->', this.filterToolbar.getQuickFilterField());
519             }
520         }
521         
522         return this.actionToolbar;
523     },
524     
525     /**
526      * opens the edit dialog
527      */
528     onEditFile: function() {
529         var sel = this.getGrid().getSelectionModel().getSelections();
530
531         if(sel.length == 1) {
532             var record = new Tine.Filemanager.Model.Node(sel[0].data);
533             var window = Tine.Filemanager.NodeEditDialog.openWindow({record: record});
534         }
535         
536         window.on('saveAndClose', function() {
537             this.getGrid().store.reload();
538         }, this);
539     },
540     
541     /**
542      * create folder in current position
543      * 
544      * @param {Ext.Component} button
545      * @param {Ext.EventObject} event
546      */
547     onCreateFolder: function(button, event) {
548         var app = this.app,
549             nodeName = Tine.Filemanager.Model.Node.getContainerName();
550         
551         Ext.MessageBox.prompt(_('New Folder'), _('Please enter the name of the new folder:'), function(_btn, _text) {
552             var currentFolderNode = app.getMainScreen().getCenterPanel().currentFolderNode;
553             if(currentFolderNode && _btn == 'ok') {
554                 if (! _text) {
555                     Ext.Msg.alert(String.format(_('No {0} added'), nodeName), String.format(_('You have to supply a {0} name!'), nodeName));
556                     return;
557                 }
558                 
559                 var filename = currentFolderNode.attributes.path + '/' + _text;
560                 Tine.Filemanager.fileRecordBackend.createFolder(filename);
561                 
562             }
563         }, this);  
564     },
565     
566     /**
567      * delete selected files / folders
568      * 
569      * @param {Ext.Component} button
570      * @param {Ext.EventObject} event
571      */
572     onDeleteRecords: function(button, event) {
573         var app = this.app,
574             nodeName = '',
575             sm = app.getMainScreen().getCenterPanel().selectionModel,
576             nodes = sm.getSelections();
577         
578         if(nodes && nodes.length) {
579             for(var i=0; i<nodes.length; i++) {
580                 var currNodeData = nodes[i].data;
581                 
582                 if(typeof currNodeData.name == 'object') {
583                     nodeName += currNodeData.name.name + '<br />';
584                 }
585                 else {
586                     nodeName += currNodeData.name + '<br />';
587                 }
588             }
589         }
590         
591         this.conflictConfirmWin = Tine.widgets.dialog.FileListDialog.openWindow({
592             modal: true,
593             allowCancel: false,
594             height: 180,
595             width: 300,
596             title: app.i18n._('Do you really want to delete the following files?'),
597             text: nodeName,
598             scope: this,
599             handler: function(button){
600                 if (nodes && button == 'yes') {
601                     this.store.remove(nodes);
602                     this.pagingToolbar.refresh.disable();
603                     Tine.Filemanager.fileRecordBackend.deleteItems(nodes);
604                 }
605                 
606                 for(var i=0; i<nodes.length; i++) {
607                     var node = nodes[i];
608                     
609                     if(node.fileRecord) {
610                         var upload = Tine.Tinebase.uploadManager.getUpload(node.fileRecord.get('uploadKey'));
611                         upload.setPaused(true);
612                         Tine.Tinebase.uploadManager.unregisterUpload(upload.id);
613                     }
614                     
615                 }
616             }
617         }, this);
618     },
619     
620     /**
621      * go up one folder
622      * 
623      * @param {Ext.Component} button
624      * @param {Ext.EventObject} event
625      */
626     onLoadParentFolder: function(button, event) {
627         var app = this.app,
628             currentFolderNode = app.getMainScreen().getCenterPanel().currentFolderNode;
629         
630         if(currentFolderNode && currentFolderNode.parentNode) {
631             app.getMainScreen().getCenterPanel().currentFolderNode = currentFolderNode.parentNode;
632             currentFolderNode.parentNode.select();
633         }
634     },
635     
636     /**
637      * grid row doubleclick handler
638      * 
639      * @param {Tine.Filemanager.NodeGridPanel} grid
640      * @param {} row record
641      * @param {Ext.EventObjet} e
642      */
643     onRowDblClick: function(grid, row, e) {
644         var app = this.app;
645         var rowRecord = grid.getStore().getAt(row);
646         
647         if(rowRecord.data.type == 'file') {
648             var downloadPath = rowRecord.data.path;
649             var downloader = new Ext.ux.file.Download({
650                 params: {
651                     method: 'Filemanager.downloadFile',
652                     requestType: 'HTTP',
653                     id: '',
654                     path: downloadPath
655                 }
656             }).start();
657         }
658         
659         else if (rowRecord.data.type == 'folder'){
660             var treePanel = app.getMainScreen().getWestPanel().getContainerTreePanel();
661             
662             var currentFolderNode;
663             if(rowRecord.data.path == '/personal/system') {
664                 currentFolderNode = treePanel.getNodeById('personal');
665             }
666             else if(rowRecord.data.path == '/shared') {
667                 currentFolderNode = treePanel.getNodeById('shared');
668             }
669             else if(rowRecord.data.path == '/personal') {
670                 currentFolderNode = treePanel.getNodeById('otherUsers');
671             }
672             else {
673                 currentFolderNode = treePanel.getNodeById(rowRecord.id);
674             }
675             if(currentFolderNode) {
676                 currentFolderNode.select();
677                 currentFolderNode.expand();
678                 app.getMainScreen().getCenterPanel().currentFolderNode = currentFolderNode;
679             } else {
680                 // get ftb path filter
681                 this.filterToolbar.filterStore.each(function(filter) {
682                     var field = filter.get('field');
683                     if (field === 'path') {
684                         filter.set('value', '');
685                         filter.set('value', rowRecord.data);
686                         filter.formFields.value.setValue(rowRecord.get('path'));
687                         
688                         this.filterToolbar.onFiltertrigger();
689                         return false;
690                     }
691                 }, this);
692             }
693         }
694     }, 
695         
696     /**
697      * on upload failure
698      * 
699      * @private
700      */
701     onUploadFail: function () {
702         Ext.MessageBox.alert(
703             _('Upload Failed'), 
704             _('Could not upload file. Filesize could be too big. Please notify your Administrator. Max upload size: ') 
705             + Tine.Tinebase.common.byteRenderer(Tine.Tinebase.registry.get('maxFileUploadSize')) 
706         ).setIcon(Ext.MessageBox.ERROR);
707         
708         var app = Tine.Tinebase.appMgr.get('Filemanager'),
709             grid = app.getMainScreen().getCenterPanel();
710         grid.pagingToolbar.refresh.enable();
711     },
712     
713     /**
714      * on remove handler
715      * 
716      * @param {} button
717      * @param {} event
718      */
719     onRemove: function (button, event) {
720         var selectedRows = this.selectionModel.getSelections();
721         for (var i = 0; i < selectedRows.length; i += 1) {
722             this.store.remove(selectedRows[i]);
723             var upload = Tine.Tinebase.uploadManager.getUpload(selectedRows[i].get('uploadKey'));
724             upload.setPaused(true);
725         }
726     },
727     
728     /**
729      * populate grid store
730      * 
731      * @param {} record
732      */
733     loadRecord: function (record) {
734         if (record && record.get(this.filesProperty)) {
735             var files = record.get(this.filesProperty);
736             for (var i = 0; i < files.length; i += 1) {
737                 var file = new Ext.ux.file.Upload.file(files[i]);
738                 file.set('status', 'complete');
739                 file.set('nodeRecord', new Tine.Filemanager.Model.Node(file.data));
740                 this.store.add(file);
741             }
742         }
743     },
744     
745     /**
746      * copies uploaded temporary file to target location
747      * 
748      * @param upload  {Ext.ux.file.Upload}
749      * @param file  {Ext.ux.file.Upload.file} 
750      */
751     onUploadComplete: function(upload, file) {
752         var app = Tine.Tinebase.appMgr.get('Filemanager'),
753             grid = app.getMainScreen().getCenterPanel();
754         
755         // check if we are responsible for the upload
756         if (upload.fmDirector != grid) return;
757         
758         // $filename, $type, $tempFileId, $forceOverwrite
759         Ext.Ajax.request({
760             timeout: 10*60*1000, // Overriding Ajax timeout - important!
761             params: {
762                 method: 'Filemanager.createNode',
763                 filename: upload.id,
764                 type: 'file',
765                 tempFileId: file.get('id'),
766                 forceOverwrite: true
767             },
768             success: grid.onNodeCreated.createDelegate(this, [upload], true), 
769             failure: grid.onNodeCreated.createDelegate(this, [upload], true)
770         });
771         
772     },
773     
774     /**
775      * TODO: move to Upload class or elsewhere??
776      * updating fileRecord after creating node
777      * 
778      * @param response
779      * @param request
780      * @param upload
781      */
782     onNodeCreated: function(response, request, upload) {
783         var record = Ext.util.JSON.decode(response.responseText);
784                 
785         var fileRecord = upload.fileRecord;
786         fileRecord.beginEdit();
787         fileRecord.set('contenttype', record.contenttype);
788         fileRecord.set('created_by', Tine.Tinebase.registry.get('currentAccount'));
789         fileRecord.set('creation_time', record.creation_time);
790         fileRecord.set('revision', record.revision);
791         fileRecord.set('last_modified_by', record.last_modified_by);
792         fileRecord.set('last_modified_time', record.last_modified_time);
793         fileRecord.set('name', record.name);
794         fileRecord.set('path', record.path);
795         fileRecord.set('status', 'complete');
796         fileRecord.set('progress', 100);
797         fileRecord.commit(false);
798        
799         upload.fireEvent('update', 'uploadfinished', upload, fileRecord);
800         
801         var app = Tine.Tinebase.appMgr.get('Filemanager'),
802             grid = app.getMainScreen().getCenterPanel();
803         
804         var allRecordsComplete = true;
805         var storeItems = grid.getStore().getRange();
806         for(var i=0; i<storeItems.length; i++) {
807             if(storeItems[i].get('status') && storeItems[i].get('status') !== 'complete') {
808                 allRecordsComplete = false;
809                 break;
810             }
811         }
812         
813         if(allRecordsComplete) {
814             grid.pagingToolbar.refresh.enable();
815         }
816     },
817     
818     /**
819      * upload new file and add to store
820      * 
821      * @param {ux.BrowsePlugin} fileSelector
822      * @param {} e
823      */
824     onFilesSelect: function (fileSelector, event) {
825         var app = Tine.Tinebase.appMgr.get('Filemanager'),
826             grid = app.getMainScreen().getCenterPanel(),
827             targetNode = grid.currentFolderNode,
828             gridStore = grid.store,
829             rowIndex = false,
830             targetFolderPath = grid.currentFolderNode.attributes.path,
831             addToGrid = true,
832             dropAllowed = false,
833             nodeRecord = null;
834         
835         if(event && event.getTarget()) {
836             rowIndex = grid.getView().findRowIndex(event.getTarget());
837         }
838         
839         
840         if(targetNode.attributes) {
841             nodeRecord = targetNode.attributes.nodeRecord;
842         }
843         
844         if(rowIndex !== false && rowIndex > -1) {
845             var newTargetNode = gridStore.getAt(rowIndex);
846             if(newTargetNode && newTargetNode.data.type == 'folder') {
847                 targetFolderPath = newTargetNode.data.path;
848                 addToGrid = false;
849                 nodeRecord = new Tine.Filemanager.Model.Node(newTargetNode.data);
850             }
851         }
852         
853         if(!nodeRecord.isDropFilesAllowed()) {
854             Ext.MessageBox.alert(
855                     _('Upload Failed'), 
856                     app.i18n._('Putting files in this folder is not allowed!')
857             ).setIcon(Ext.MessageBox.ERROR);
858             
859             return;
860         }    
861         
862         var files = fileSelector.getFileList();
863         
864         if(files.length > 0) {
865             grid.pagingToolbar.refresh.disable();
866         }
867         
868         var filePathsArray = [], uploadKeyArray = [];
869         
870         Ext.each(files, function (file) {
871             var fileRecord = Tine.Filemanager.Model.Node.createFromFile(file),
872                 filePath = targetFolderPath + '/' + fileRecord.get('name');
873             
874             fileRecord.set('path', filePath);
875             var existingRecordIdx = gridStore.find('name', fileRecord.get('name'));
876             if(existingRecordIdx < 0) {
877                 gridStore.add(fileRecord);
878             }
879             
880             var upload = new Ext.ux.file.Upload({
881                 fmDirector: grid,
882                 file: file,
883                 fileSelector: fileSelector,
884                 id: filePath
885             });
886             
887             var uploadKey = Tine.Tinebase.uploadManager.queueUpload(upload);
888             
889             filePathsArray.push(filePath);
890             uploadKeyArray.push(uploadKey);
891             
892         }, this);
893         
894         var params = {
895                 filenames: filePathsArray,
896                 type: "file",
897                 tempFileIds: [],
898                 forceOverwrite: false
899         };
900         Tine.Filemanager.fileRecordBackend.createNodes(params, uploadKeyArray, true);
901     },
902     
903     /**
904      * download file
905      * 
906      * @param {} button
907      * @param {} event
908      */
909     onDownload: function(button, event) {
910         
911         var app = Tine.Tinebase.appMgr.get('Filemanager'),
912             grid = app.getMainScreen().getCenterPanel(),
913             selectedRows = grid.selectionModel.getSelections();
914         
915         var fileRow = selectedRows[0];
916                
917         var downloadPath = fileRow.data.path;
918         var downloader = new Ext.ux.file.Download({
919             params: {
920                 method: 'Filemanager.downloadFile',
921                 requestType: 'HTTP',
922                 id: '',
923                 path: downloadPath
924             }
925         }).start();
926     },
927     
928     /**
929      * grid on load handler
930      * 
931      * @param grid
932      * @param records
933      * @param options
934      */
935     onLoad: function(store, records, options){
936         var app = Tine.Tinebase.appMgr.get('Filemanager'),
937             grid = app.getMainScreen().getCenterPanel();
938         
939         for(var i=records.length-1; i>=0; i--) {
940             var record = records[i];
941             if(record.get('type') == 'file' && (!record.get('size') || record.get('size') == 0)) {
942                 var upload = Tine.Tinebase.uploadManager.getUpload(record.get('path'));
943                 
944                 if(upload) {
945                       if(upload.fileRecord && record.get('name') == upload.fileRecord.get('name')) {
946                           grid.updateNodeRecord(record, upload.fileRecord);
947                           record.afterEdit();
948                     }
949                 }
950             }
951         }
952     },
953     
954     /**
955      * update grid nodeRecord with fileRecord data
956      * 
957      * @param nodeRecord
958      * @param fileRecord
959      */
960     updateNodeRecord: function(nodeRecord, fileRecord) {
961         for(var field in fileRecord.fields) {
962             nodeRecord.set(field, fileRecord.get(field));
963         }
964         nodeRecord.fileRecord = fileRecord;
965     },
966     
967     /**
968      * upload update handler
969      * 
970      * @param change {String} kind of change
971      * @param upload {Ext.ux.file.Upload} upload
972      * @param fileRecord {file} fileRecord
973      * 
974      */
975     onUpdate: function(change, upload, fileRecord) {
976         var app = Tine.Tinebase.appMgr.get('Filemanager'),
977             grid = app.getMainScreen().getCenterPanel(),
978             rowsToUpdate = grid.getStore().query('name', fileRecord.get('name'));
979         
980         if(change == 'uploadstart') {
981             Tine.Tinebase.uploadManager.onUploadStart();
982         }
983         else if(change == 'uploadfailure') {
984             grid.onUploadFail();
985         }
986         
987         if(rowsToUpdate.get(0)) {
988             if(change == 'uploadcomplete') {
989                 grid.onUploadComplete(upload, fileRecord);
990             }
991             else if(change == 'uploadfinished') {
992                 rowsToUpdate.get(0).set('size', fileRecord.get('size'));
993                 rowsToUpdate.get(0).set('contenttype', fileRecord.get('contenttype'));
994             }
995             rowsToUpdate.get(0).afterEdit();
996             rowsToUpdate.get(0).commit(false);
997         }
998     },
999     
1000     /**
1001      * init grid drop target
1002      * 
1003      * @TODO DRY cleanup
1004      */
1005     initDropTarget: function(){
1006         var ddrow = new Ext.dd.DropTarget(this.getEl(), {
1007             ddGroup : 'fileDDGroup',  
1008             
1009             notifyDrop : function(dragSource, e, data){
1010                 
1011                 if(data.node && data.node.attributes && !data.node.attributes.nodeRecord.isDragable()) {
1012                     return false;
1013                 }
1014                 
1015                 var app = Tine.Tinebase.appMgr.get(Tine.Filemanager.fileRecordBackend.appName),
1016                     grid = app.getMainScreen().getCenterPanel(),
1017                     treePanel = app.getMainScreen().getWestPanel().getContainerTreePanel(),
1018                     dropIndex = grid.getView().findRowIndex(e.target),
1019                     target = grid.getStore().getAt(dropIndex),
1020                     nodes = data.selections ? data.selections : [data.node];
1021                 
1022                 if((!target || target.data.type === 'file') && grid.currentFolderNode) {
1023                     target = grid.currentFolderNode;
1024                 }
1025                 
1026                 if(!target) {
1027                     return false;
1028                 }
1029                 
1030                 for(var i=0; i<nodes.length; i++) {
1031                     if(nodes[i].id == target.id) {
1032                         return false;
1033                     }
1034                 }
1035                 
1036                 var targetNode = treePanel.getNodeById(target.id);
1037                 if(targetNode && targetNode.isAncestor(nodes[0])) {
1038                     return false;
1039                 }
1040                 
1041                 Tine.Filemanager.fileRecordBackend.copyNodes(nodes, target, !e.ctrlKey);
1042                 return true;
1043             },
1044             
1045             notifyOver : function( dragSource, e, data ) {
1046                 if(data.node && data.node.attributes && !data.node.attributes.nodeRecord.isDragable()) {
1047                     return false;
1048                 }
1049                 
1050                 var app = Tine.Tinebase.appMgr.get(Tine.Filemanager.fileRecordBackend.appName),
1051                     grid = app.getMainScreen().getCenterPanel(),
1052                     dropIndex = grid.getView().findRowIndex(e.target),
1053                     treePanel = app.getMainScreen().getWestPanel().getContainerTreePanel(),
1054                     target= grid.getStore().getAt(dropIndex),
1055                     nodes = data.selections ? data.selections : [data.node];
1056                 
1057                 if((!target || (target.data && target.data.type === 'file')) && grid.currentFolderNode) {
1058                     target = grid.currentFolderNode;
1059                 }
1060                 
1061                 if(!target) {
1062                     return false;
1063                 }
1064                 
1065                 for(var i=0; i<nodes.length; i++) {
1066                     if(nodes[i].id == target.id) {
1067                         return false;
1068                     }
1069                 }
1070                 
1071                 var targetNode = treePanel.getNodeById(target.id);
1072                 if(targetNode && targetNode.isAncestor(nodes[0])) {
1073                     return false;
1074                 }
1075                 
1076                 return this.dropAllowed;
1077             }
1078         });
1079     }
1080 });