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