0012950: More attachment methods for mail
[tine20] / tine20 / Filemanager / js / FilePicker.js
1 /*
2  * Tine 2.0
3  *
4  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
5  * @author      Michael Spahn <m.spahn@metaways.de>
6  * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
7  */
8 Ext.ns('Tine.Filemanager');
9
10 /**
11  * FilePicker component.
12  *
13  * Standalone file picker for selecting a node or a folder from filemanager.
14  * The filepicker does require the filemanager to be enabled!
15  *
16  * The filepicker offers two events:
17  *  - nodeSelected
18  *  - invalidNodeSelected
19  */
20 Tine.Filemanager.FilePicker = Ext.extend(Ext.Container, {
21     /**
22      * Filemanager app
23      * @private
24      */
25     app: null,
26
27     /**
28      * Layout.
29      * @private
30      */
31     layout: 'fit',
32
33     /**
34      * NodeTreePanel instance
35      * @private
36      */
37     treePanel: null,
38
39     /**
40      * NodeGridPanel instance
41      * @private
42      */
43     gridPanel: null,
44
45     /**
46      * Selected nodes
47      * @private
48      */
49     selection: [],
50
51     /**
52      * Last clicked node
53      * @private
54      */
55     lastClickedNode: null,
56
57     /**
58      * Allow to select one or more node
59      */
60     singleSelect: true,
61
62     /**
63      * A constraint allows to alter the selection behaviour of the picker, for example only allow to select files.
64      *
65      * By default, file and folder are allowed to be selected, the concrete implementation needs to define it's purpose
66      *
67      * Valids constraints:
68      *  - file
69      *  - folder
70      *  - null (take all)
71      */
72     constraint: null,
73
74     /**
75      * Constructor.
76      */
77     initComponent: function () {
78         var model = Tine.Filemanager.Model.Node;
79         this.app = Tine.Tinebase.appMgr.get(model.getMeta('appName'));
80
81         this.treePanel = this.getTreePanel();
82         this.gridPanel = this.getGridPanel();
83
84         this.addEvents(
85             /**
86              * Fires when a file was selected which fulfills all constraints
87              *
88              * @param nodeData selected node data
89              */
90             'nodeSelected',
91             /**
92              * Fires if a node is selected which does not fulfill the provided constraints
93              */
94             'invalidNodeSelected'
95         );
96
97         this.items = [{
98             layout: 'border',
99             border: false,
100             frame: false,
101             items: [{
102                 layout: 'fit',
103                 region: 'west',
104                 frame: false,
105                 width: 200,
106                 border: false,
107                 split: true,
108                 collapsible: true,
109                 header: false,
110                 collapseMode: 'mini',
111                 items: [
112                     this.treePanel
113                 ]
114             }, {
115                 layout: 'fit',
116                 split: true,
117                 frame: false,
118                 border: false,
119                 region: 'center',
120                 width: 300,
121                 items: [
122                     this.gridPanel
123                 ]
124             }]
125         }];
126
127         Tine.Filemanager.FilePicker.superclass.initComponent.call(this);
128     },
129
130     /**
131      * Updates selected element and triggers an event
132      */
133     updateSelection: function (nodes) {
134         // If selection doesn't fullfil constraint, we don't throw a selectionChange event
135         if (!this.checkConstraint(nodes)) {
136             this.fireEvent('invalidNodeSelected');
137             return false;
138         }
139
140         //  Clear previous selection
141         this.selection = [];
142
143         var me = this;
144         Ext.each(nodes, function (node) {
145             me.selection.push(node.data || node);
146         });
147
148         this.fireEvent('nodeSelected', this.selection);
149     },
150
151     /**
152      * @returns {Tine.Filemanager.NodeTreePanel}
153      */
154     getTreePanel: function () {
155         if (this.treePanel) {
156             return this.treePanel;
157         }
158
159         var me = this;
160         var treePanel = new Tine.Filemanager.NodeTreePanel({
161             height: 200,
162             width: 200,
163             readOnly: true,
164             filterMode: 'filterToolbar',
165             // fixme: NodeTreePanel fetches grid via app registry
166             onSelectionChange: Tine.widgets.container.TreePanel.prototype.onSelectionChange
167         });
168
169         treePanel.getSelectionModel().on('selectionchange', function (selectionModel) {
170             var treeNode = selectionModel.getSelectedNode();
171             me.updateSelection([
172                 treeNode.attributes
173             ]);
174         });
175
176         return treePanel;
177     },
178
179     /**
180      * @returns {*}
181      */
182     getGridPanel: function () {
183         if (this.gridPanel) {
184             return this.gridPanel;
185         }
186
187         var me = this;
188
189         var gridPanel = new Tine.Filemanager.NodeGridPanel({
190             app: me.app,
191             height: 200,
192             width: 200,
193             border: false,
194             frame: false,
195             readOnly: true,
196             enableDD: false,
197             enableDrag: false,
198             treePanel: this.getTreePanel(),
199             hasQuickSearchFilterToolbarPlugin: false,
200             stateIdSuffix: '-FilePicker',
201             plugins: [this.getTreePanel().getFilterPlugin()]
202         });
203
204         gridPanel.getGrid().reconfigure(gridPanel.getStore(), this.getColumnModel());
205         gridPanel.getGrid().getSelectionModel().singleSelect = this.singleSelect;
206         gridPanel.getGrid().getSelectionModel().on('rowselect', function (selModel) {
207             var record = selModel.getSelections();
208             me.updateSelection(record);
209         });
210
211         // Hide filter toolbar
212         gridPanel.filterToolbar.hide();
213
214         return gridPanel;
215     },
216
217     /**
218      * Check if selection fits current constraint
219      * @returns {boolean}
220      */
221     checkConstraint: function (nodes) {
222         var me = this;
223         var valid = true;
224
225         Ext.each(nodes, function (node) {
226             if (!me.checkNodeConstraint(node.data || node)) {
227                 valid = false;
228                 return false;
229             }
230         });
231
232         return valid;
233     },
234
235     /**
236      * Checks if a single node matches the constraints
237      *
238      * @param node
239      * @returns {boolean}
240      */
241     checkNodeConstraint: function (node) {
242         // Minimum information to proceed here
243         if (!node.path || !node.id) {
244             return false;
245         }
246
247         // If no constraints apply, skip here
248         if (this.constraint === null) {
249             return true;
250         }
251
252         switch (this.constraint) {
253             case 'file':
254                 if (node.type !== 'file') {
255                     return false;
256                 }
257                 break;
258             case 'folder':
259                 if (node.type !== 'folder') {
260                     return false;
261                 }
262                 break;
263         }
264
265         return true;
266     },
267
268     /**
269      * Customized column model for the grid
270      * @returns {*}
271      */
272     getColumnModel: function () {
273         var columns = [{
274             id: 'name',
275             header: this.app.i18n._("Name"),
276             width: 70,
277             sortable: true,
278             dataIndex: 'name',
279             renderer: Ext.ux.PercentRendererWithName
280         }, {
281             id: 'size',
282             header: this.app.i18n._("Size"),
283             width: 40,
284             sortable: true,
285             dataIndex: 'size',
286             renderer: Tine.Tinebase.common.byteRenderer.createDelegate(this, [2, true], 3)
287         }, {
288             id: 'contenttype',
289             header: this.app.i18n._("Contenttype"),
290             width: 50,
291             sortable: true,
292             dataIndex: 'contenttype',
293             renderer: function (value, metadata, record) {
294
295                 var app = Tine.Tinebase.appMgr.get('Filemanager');
296                 if (record.data.type == 'folder') {
297                     return app.i18n._("Folder");
298                 }
299                 else {
300                     return value;
301                 }
302             }
303         }];
304
305         return new Ext.grid.ColumnModel({
306             defaults: {
307                 sortable: true,
308                 resizable: true
309             },
310             columns: columns
311         });
312     }
313 });