0012932: file picker dialog in fileuploadgrid
[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                 collapseMode: 'mini',
110                 items: [
111                     this.treePanel
112                 ]
113             }, {
114                 layout: 'fit',
115                 split: true,
116                 frame: false,
117                 region: 'center',
118                 width: 300,
119                 items: [
120                     this.gridPanel
121                 ]
122             }]
123         }];
124
125         Tine.Filemanager.FilePicker.superclass.initComponent.call(this);
126     },
127
128     /**
129      * Updates selected element and triggers an event
130      */
131     updateSelection: function (nodes) {
132         // If selection doesn't fullfil constraint, we don't throw a selectionChange event
133         if (!this.checkConstraint(nodes)) {
134             this.fireEvent('invalidNodeSelected');
135             return false;
136         }
137
138         //  Clear previous selection
139         this.selection = [];
140
141         var me = this;
142         Ext.each(nodes, function (node) {
143             me.selection.push(node.data || node);
144         });
145
146         this.fireEvent('nodeSelected', this.selection);
147     },
148
149     /**
150      * @returns {Tine.Filemanager.NodeTreePanel}
151      */
152     getTreePanel: function () {
153         if (this.treePanel) {
154             return this.treePanel;
155         }
156
157         var me = this;
158         var treePanel = new Tine.Filemanager.NodeTreePanel({
159             height: 200,
160             width: 200,
161             readOnly: true,
162             filterMode: 'filterToolbar'
163         });
164
165         treePanel.getSelectionModel().on('selectionchange', function (selectionModel) {
166             var treeNode = selectionModel.getSelectedNode();
167             me.updateSelection([
168                 treeNode.attributes
169             ]);
170         });
171
172         return treePanel;
173     },
174
175     /**
176      * @returns {*}
177      */
178     getGridPanel: function () {
179         if (this.gridPanel) {
180             return this.gridPanel;
181         }
182
183         var me = this;
184
185         var gridPanel = new Tine.Filemanager.NodeGridPanel({
186             app: me.app,
187             height: 200,
188             width: 200,
189             readOnly: true,
190             enableDD: false,
191             enableDrag: false,
192             treePanel: this.getTreePanel(),
193             hasQuickSearchFilterToolbarPlugin: false,
194             stateIdPrefix: '-FilePicker',
195             plugins: [this.getTreePanel().getFilterPlugin()]
196         });
197
198         gridPanel.getGrid().reconfigure(gridPanel.getStore(), this.getColumnModel());
199         gridPanel.getGrid().getSelectionModel().singleSelect = this.singleSelect;
200         gridPanel.getGrid().getSelectionModel().on('rowselect', function (selModel) {
201             var record = selModel.getSelections();
202             me.updateSelection(record);
203         });
204
205         return gridPanel;
206     },
207
208     /**
209      * Check if selection fits current constraint
210      * @returns {boolean}
211      */
212     checkConstraint: function (nodes) {
213         var me = this;
214         var valid = true;
215
216         Ext.each(nodes, function (node) {
217             if (!me.checkNodeConstraint(node.data || node)) {
218                 valid = false;
219                 return false;
220             }
221         });
222
223         return valid;
224     },
225
226     /**
227      * Checks if a single node matches the constraints
228      *
229      * @param node
230      * @returns {boolean}
231      */
232     checkNodeConstraint: function (node) {
233         // Minimum information to proceed here
234         if (!node.path || !node.id) {
235             return false;
236         }
237
238         // If no constraints apply, skip here
239         if (this.constraint === null) {
240             return true;
241         }
242
243         switch (this.constraint) {
244             case 'file':
245                 if (node.type !== 'file') {
246                     return false;
247                 }
248                 break;
249             case 'folder':
250                 if (node.type !== 'folder') {
251                     return false;
252                 }
253                 break;
254         }
255
256         return true;
257     },
258
259     /**
260      * Customized column model for the grid
261      * @returns {*}
262      */
263     getColumnModel: function () {
264         var columns = [{
265             id: 'name',
266             header: this.app.i18n._("Name"),
267             width: 70,
268             sortable: true,
269             dataIndex: 'name',
270             renderer: Ext.ux.PercentRendererWithName
271         }, {
272             id: 'size',
273             header: this.app.i18n._("Size"),
274             width: 40,
275             sortable: true,
276             dataIndex: 'size',
277             renderer: Tine.Tinebase.common.byteRenderer.createDelegate(this, [2, true], 3)
278         }, {
279             id: 'contenttype',
280             header: this.app.i18n._("Contenttype"),
281             width: 50,
282             sortable: true,
283             dataIndex: 'contenttype',
284             renderer: function (value, metadata, record) {
285
286                 var app = Tine.Tinebase.appMgr.get('Filemanager');
287                 if (record.data.type == 'folder') {
288                     return app.i18n._("Folder");
289                 }
290                 else {
291                     return value;
292                 }
293             }
294         }];
295
296         return new Ext.grid.ColumnModel({
297             defaults: {
298                 sortable: true,
299                 resizable: true
300             },
301             columns: columns
302         });
303     }
304 });