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