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                 minWidth: 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.') 
698         ).setIcon(Ext.MessageBox.ERROR);
699         
700         var app = Tine.Tinebase.appMgr.get('Filemanager'),
701             grid = app.getMainScreen().getCenterPanel();
702         grid.pagingToolbar.refresh.enable();
703     },
704     
705     /**
706      * on remove handler
707      * 
708      * @param {} button
709      * @param {} event
710      */
711     onRemove: function (button, event) {
712         var selectedRows = this.selectionModel.getSelections();
713         for (var i = 0; i < selectedRows.length; i += 1) {
714             this.store.remove(selectedRows[i]);
715             var upload = Tine.Tinebase.uploadManager.getUpload(selectedRows[i].get('uploadKey'));
716             upload.setPaused(true);
717         }
718     },
719     
720     /**
721      * populate grid store
722      * 
723      * @param {} record
724      */
725     loadRecord: function (record) {
726         if (record && record.get(this.filesProperty)) {
727             var files = record.get(this.filesProperty);
728             for (var i = 0; i < files.length; i += 1) {
729                 var file = new Ext.ux.file.Upload.file(files[i]);
730                 file.set('status', 'complete');
731                 file.set('nodeRecord', new Tine.Filemanager.Model.Node(file.data));
732                 this.store.add(file);
733             }
734         }
735     },
736     
737     /**
738      * copies uploaded temporary file to target location
739      * 
740      * @param upload  {Ext.ux.file.Upload}
741      * @param file  {Ext.ux.file.Upload.file} 
742      */
743     onUploadComplete: function(upload, file) {
744         var app = Tine.Tinebase.appMgr.get('Filemanager'),
745             grid = app.getMainScreen().getCenterPanel();
746         
747         // check if we are responsible for the upload
748         if (upload.fmDirector != grid) return;
749         
750         // $filename, $type, $tempFileId, $forceOverwrite
751         Ext.Ajax.request({
752             timeout: 10*60*1000, // Overriding Ajax timeout - important!
753             params: {
754                 method: 'Filemanager.createNode',
755                 filename: upload.id,
756                 type: 'file',
757                 tempFileId: file.get('id'),
758                 forceOverwrite: true
759             },
760             success: grid.onNodeCreated.createDelegate(this, [upload], true), 
761             failure: grid.onNodeCreated.createDelegate(this, [upload], true)
762         });
763         
764     },
765     
766     /**
767      * TODO: move to Upload class or elsewhere??
768      * updating fileRecord after creating node
769      * 
770      * @param response
771      * @param request
772      * @param upload
773      */
774     onNodeCreated: function(response, request, upload) {
775         var record = Ext.util.JSON.decode(response.responseText);
776                 
777         var fileRecord = upload.fileRecord;
778         fileRecord.beginEdit();
779         fileRecord.set('contenttype', record.contenttype);
780         fileRecord.set('created_by', Tine.Tinebase.registry.get('currentAccount'));
781         fileRecord.set('creation_time', record.creation_time);
782         fileRecord.set('revision', record.revision);
783         fileRecord.set('last_modified_by', record.last_modified_by);
784         fileRecord.set('last_modified_time', record.last_modified_time);
785         fileRecord.set('name', record.name);
786         fileRecord.set('path', record.path);
787         fileRecord.set('status', 'complete');
788         fileRecord.set('progress', 100);
789         fileRecord.commit(false);
790        
791         upload.fireEvent('update', 'uploadfinished', upload, fileRecord);
792         
793         var app = Tine.Tinebase.appMgr.get('Filemanager'),
794             grid = app.getMainScreen().getCenterPanel();
795         
796         var allRecordsComplete = true;
797         var storeItems = grid.getStore().getRange();
798         for(var i=0; i<storeItems.length; i++) {
799             if(storeItems[i].get('status') && storeItems[i].get('status') !== 'complete') {
800                 allRecordsComplete = false;
801                 break;
802             }
803         }
804         
805         if(allRecordsComplete) {
806             grid.pagingToolbar.refresh.enable();
807         }
808     },
809     
810     /**
811      * upload new file and add to store
812      * 
813      * @param {ux.BrowsePlugin} fileSelector
814      * @param {} e
815      */
816     onFilesSelect: function (fileSelector, event) {
817         var app = Tine.Tinebase.appMgr.get('Filemanager'),
818             grid = app.getMainScreen().getCenterPanel(),
819             targetNode = grid.currentFolderNode,
820             gridStore = grid.store,
821             rowIndex = false,
822             targetFolderPath = grid.currentFolderNode.attributes.path,
823             addToGrid = true,
824             dropAllowed = false,
825             nodeRecord = null;
826         
827         if(event && event.getTarget()) {
828             rowIndex = grid.getView().findRowIndex(event.getTarget());
829         }
830         
831         
832         if(targetNode.attributes) {
833             nodeRecord = targetNode.attributes.nodeRecord;
834         }
835         
836         if(rowIndex !== false && rowIndex > -1) {
837             var newTargetNode = gridStore.getAt(rowIndex);
838             if(newTargetNode && newTargetNode.data.type == 'folder') {
839                 targetFolderPath = newTargetNode.data.path;
840                 addToGrid = false;
841                 nodeRecord = new Tine.Filemanager.Model.Node(newTargetNode.data);
842             }
843         }
844         
845         if(!nodeRecord.isDropFilesAllowed()) {
846             Ext.MessageBox.alert(
847                     _('Upload Failed'), 
848                     app.i18n._('Putting files in this folder is not allowed!')
849             ).setIcon(Ext.MessageBox.ERROR);
850             
851             return;
852         }    
853         
854         var files = fileSelector.getFileList();
855         
856         if(files.length > 0) {
857             grid.pagingToolbar.refresh.disable();
858         }
859         
860         var filePathsArray = [], uploadKeyArray = [];
861         
862         Ext.each(files, function (file) {
863             var fileRecord = Tine.Filemanager.Model.Node.createFromFile(file),
864                 filePath = targetFolderPath + '/' + fileRecord.get('name');
865             
866             fileRecord.set('path', filePath);
867             var existingRecordIdx = gridStore.find('name', fileRecord.get('name'));
868             if(existingRecordIdx < 0) {
869                 gridStore.add(fileRecord);
870             }
871             
872             var upload = new Ext.ux.file.Upload({
873                 fmDirector: grid,
874                 file: file,
875                 fileSelector: fileSelector,
876                 id: filePath
877             });
878             
879             var uploadKey = Tine.Tinebase.uploadManager.queueUpload(upload);
880             
881             filePathsArray.push(filePath);
882             uploadKeyArray.push(uploadKey);
883             
884         }, this);
885         
886         var params = {
887                 filenames: filePathsArray,
888                 type: "file",
889                 tempFileIds: [],
890                 forceOverwrite: false
891         };
892         Tine.Filemanager.fileRecordBackend.createNodes(params, uploadKeyArray, true);
893     },
894     
895     /**
896      * download file
897      * 
898      * @param {} button
899      * @param {} event
900      */
901     onDownload: function(button, event) {
902         
903         var app = Tine.Tinebase.appMgr.get('Filemanager'),
904             grid = app.getMainScreen().getCenterPanel(),
905             selectedRows = grid.selectionModel.getSelections();
906         
907         var fileRow = selectedRows[0];
908                
909         var downloadPath = fileRow.data.path;
910         var downloader = new Ext.ux.file.Download({
911             params: {
912                 method: 'Filemanager.downloadFile',
913                 requestType: 'HTTP',
914                 id: '',
915                 path: downloadPath
916             }
917         }).start();
918     },
919     
920     /**
921      * grid on load handler
922      * 
923      * @param grid
924      * @param records
925      * @param options
926      */
927     onLoad: function(store, records, options){
928         var app = Tine.Tinebase.appMgr.get('Filemanager'),
929             grid = app.getMainScreen().getCenterPanel();
930         
931         for(var i=records.length-1; i>=0; i--) {
932             var record = records[i];
933             if(record.get('type') == 'file' && (!record.get('size') || record.get('size') == 0)) {
934                 var upload = Tine.Tinebase.uploadManager.getUpload(record.get('path'));
935                 
936                 if(upload) {
937                       if(upload.fileRecord && record.get('name') == upload.fileRecord.get('name')) {
938                           grid.updateNodeRecord(record, upload.fileRecord);
939                           record.afterEdit();
940                     }
941                 }
942             }
943         }
944     },
945     
946     /**
947      * update grid nodeRecord with fileRecord data
948      * 
949      * @param nodeRecord
950      * @param fileRecord
951      */
952     updateNodeRecord: function(nodeRecord, fileRecord) {
953         for(var field in fileRecord.fields) {
954             nodeRecord.set(field, fileRecord.get(field));
955         }
956         nodeRecord.fileRecord = fileRecord;
957     },
958     
959     /**
960      * upload update handler
961      * 
962      * @param change {String} kind of change
963      * @param upload {Ext.ux.file.Upload} upload
964      * @param fileRecord {file} fileRecord
965      * 
966      */
967     onUpdate: function(change, upload, fileRecord) {
968         var app = Tine.Tinebase.appMgr.get('Filemanager'),
969             grid = app.getMainScreen().getCenterPanel(),
970             rowsToUpdate = grid.getStore().query('name', fileRecord.get('name'));
971         
972         if(change == 'uploadstart') {
973             Tine.Tinebase.uploadManager.onUploadStart();
974         }
975         else if(change == 'uploadfailure') {
976             grid.onUploadFail();
977         }
978         
979         if(rowsToUpdate.get(0)) {
980             if(change == 'uploadcomplete') {
981                 grid.onUploadComplete(upload, fileRecord);
982             }
983             else if(change == 'uploadfinished') {
984                 rowsToUpdate.get(0).set('size', fileRecord.get('size'));
985                 rowsToUpdate.get(0).set('contenttype', fileRecord.get('contenttype'));
986             }
987             rowsToUpdate.get(0).afterEdit();
988             rowsToUpdate.get(0).commit(false);
989         }
990     },
991     
992     /**
993      * init grid drop target
994      * 
995      * @TODO DRY cleanup
996      */
997     initDropTarget: function(){
998         var ddrow = new Ext.dd.DropTarget(this.getEl(), {
999             ddGroup : 'fileDDGroup',  
1000             
1001             notifyDrop : function(dragSource, e, data){
1002                 
1003                 if(data.node && data.node.attributes && !data.node.attributes.nodeRecord.isDragable()) {
1004                     return false;
1005                 }
1006                 
1007                 var app = Tine.Tinebase.appMgr.get(Tine.Filemanager.fileRecordBackend.appName),
1008                     grid = app.getMainScreen().getCenterPanel(),
1009                     treePanel = app.getMainScreen().getWestPanel().getContainerTreePanel(),
1010                     dropIndex = grid.getView().findRowIndex(e.target),
1011                     target = grid.getStore().getAt(dropIndex),
1012                     nodes = data.selections ? data.selections : [data.node];
1013                 
1014                 if((!target || target.data.type === 'file') && grid.currentFolderNode) {
1015                     target = grid.currentFolderNode;
1016                 }
1017                 
1018                 if(!target) {
1019                     return false;
1020                 }
1021                 
1022                 for(var i=0; i<nodes.length; i++) {
1023                     if(nodes[i].id == target.id) {
1024                         return false;
1025                     }
1026                 }
1027                 
1028                 var targetNode = treePanel.getNodeById(target.id);
1029                 if(targetNode && targetNode.isAncestor(nodes[0])) {
1030                     return false;
1031                 }
1032                 
1033                 Tine.Filemanager.fileRecordBackend.copyNodes(nodes, target, !e.ctrlKey);
1034                 return true;
1035             },
1036             
1037             notifyOver : function( dragSource, e, data ) {
1038                 if(data.node && data.node.attributes && !data.node.attributes.nodeRecord.isDragable()) {
1039                     return false;
1040                 }
1041                 
1042                 var app = Tine.Tinebase.appMgr.get(Tine.Filemanager.fileRecordBackend.appName),
1043                     grid = app.getMainScreen().getCenterPanel(),
1044                     dropIndex = grid.getView().findRowIndex(e.target),
1045                     treePanel = app.getMainScreen().getWestPanel().getContainerTreePanel(),
1046                     target= grid.getStore().getAt(dropIndex),
1047                     nodes = data.selections ? data.selections : [data.node];
1048                 
1049                 if((!target || (target.data && target.data.type === 'file')) && grid.currentFolderNode) {
1050                     target = grid.currentFolderNode;
1051                 }
1052                 
1053                 if(!target) {
1054                     return false;
1055                 }
1056                 
1057                 for(var i=0; i<nodes.length; i++) {
1058                     if(nodes[i].id == target.id) {
1059                         return false;
1060                     }
1061                 }
1062                 
1063                 var targetNode = treePanel.getNodeById(target.id);
1064                 if(targetNode && targetNode.isAncestor(nodes[0])) {
1065                     return false;
1066                 }
1067                 
1068                 return this.dropAllowed;
1069             }
1070         });
1071     }
1072 });