9cb8753d5b9c1d54823086ac397876ca4e0d651d
[tine20] / tine20 / Tinebase / js / widgets / container / FilterModel.js
1 /*
2  * Tine 2.0
3  * 
4  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
5  * @author      Cornelius Weiss <c.weiss@metaways.de>
6  * @copyright   Copyright (c) 2010 Metaways Infosystems GmbH (http://www.metaways.de)
7  * 
8  * TODO         extend Tine.widgets.grid.PickerFilter
9  */
10 Ext.ns('Tine.widgets.container');
11
12 /**
13  * @namespace   Tine.widgets.container
14  * @class       Tine.widgets.container.FilterModel
15  * @extends     Tine.widgets.grid.FilterModel
16  * 
17  * @author      Cornelius Weiss <c.weiss@metaways.de>
18  */
19 Tine.widgets.container.FilterModel = Ext.extend(Tine.widgets.grid.FilterModel, {
20     /**
21      * @cfg {Tine.Tinebase.Application} app
22      */
23     app: null,
24     
25     /**
26      * If this component is called from another app than the container belongs to, set this to this app
27      * 
28      * @cfg {Tine.Tinebase.Application} callingApp
29      */
30     callingApp: null,
31     
32     /**
33      * @cfg {Tine.Tinebase.data.Record} recordClass
34      * record definition class  (required)
35      */
36     recordClass: null,
37     
38     /**
39      * @cfg {Array} operators allowed operators
40      */
41     operators: ['equals', 'in', 'not', 'notin'],
42     
43     /**
44      * @cfg {String} field container field (defaults to container_id)
45      */
46     field: 'container_id',
47     
48     /**
49      * the label of the filter (autoset from containing recordClass, if none is given)
50      * 
51      * @type {String}
52      */
53     label: null,
54     
55     /**
56      * @cfg {String} defaultOperator default operator, one of <tt>{@link #operators} (defaults to equals)
57      */
58     defaultOperator: 'equals',
59     
60     /**
61      * @cfg {String} defaultValue default value (defaults to all)
62      */
63     defaultValue: {path: '/'},
64     
65     /**
66      * @private
67      */
68     initComponent: function() {
69         // get recordClass if string is given
70         if (Ext.isString(this.recordClass)) {
71             var split = this.recordClass.split('.');
72             this.recordClass = Tine[split[0]].Model[split[1]];
73         }
74         
75         // get application if string is given
76         if (Ext.isString(this.app)) {
77             this.app = Tine.Tinebase.appMgr.get(this.app);
78         }
79         
80         // get calling application if string is given
81         if (Ext.isString(this.callingApp)) {
82             this.callingApp = Tine.Tinebase.appMgr.get(this.callingApp);
83         }
84         
85         Tine.widgets.container.FilterModel.superclass.initComponent.call(this);
86         
87         this.containerName  = this.app.i18n.n_hidden(this.recordClass.getMeta('containerName'), this.recordClass.getMeta('containersName'), 1);
88         this.containersName = this.app.i18n._hidden(this.recordClass.getMeta('containersName'));
89
90         this.label = this.label ? this.callingApp.i18n._(this.label) : this.containerName;
91     },
92     
93     /**
94      * returns valueType of given filter
95      * 
96      * @param {Record} filter
97      * @return {String}
98      */
99     getValueType: function(filter) {
100         var operator = filter.get('operator') ? filter.get('operator') : this.defaultOperator;
101         
102         var valueType = 'selectionComboBox';
103         switch (operator) {
104             case 'notin':
105             case 'in':
106                 valueType = 'FilterModelMultipleValueField';
107                 break;
108         }
109         
110         return valueType;
111     },
112     
113     /**
114      * called on operator change of a filter row
115      * @private
116      */
117     onOperatorChange: function(filter, newOperator) {
118         this.supr().onOperatorChange.call(this, filter, newOperator);
119         
120         var valueType = this.getValueType(filter);
121         
122         for (var valueField in filter.valueFields) {
123             filter.valueFields[valueField][valueField == valueType ? 'show' : 'hide']();
124         };
125         
126         filter.formFields.value = filter.valueFields[valueType];
127         filter.formFields.value.setWidth(filter.formFields.value.width);
128         filter.formFields.value.wrap.setWidth(filter.formFields.value.width);
129         filter.formFields.value.syncSize();
130     },
131     
132     /**
133      * value renderer
134      * 
135      * @param {Ext.data.Record} filter line
136      * @param {Ext.Element} element to render to 
137      */
138     valueRenderer: function(filter, el) {
139         var valueType = this.getValueType(filter);
140         
141         filter.valueFields = {};
142         filter.valueFields.selectionComboBox = new Tine.widgets.container.SelectionComboBox({
143             hidden: valueType != 'selectionComboBox',
144             app: this.app,
145             filter: filter,
146             width: this.filterValueWidth,
147             listWidth: 200,
148             //id: 'tw-ftb-frow-valuefield-' + filter.id,
149             value: filter.data.value ? filter.data.value : this.defaultValue,
150             renderTo: el,
151             allowNodeSelect: true,
152             recordClass: this.recordClass,
153             appName: this.recordClass.getMeta('appName'),
154             containerName: this.containerName,
155             containersName: this.containersName,
156             getValue: function() {
157                 return this.selectedContainer ? this.selectedContainer.path : null;
158             },
159             setValue: function(value) {
160                 if (this.filter.get('operator') == 'specialNode' || this.filter.get('operator') == 'personalNode' ) {
161                     var operatorText = this.filter.data.operator === 'personalNode' ? i18n._('is personal of') : i18n._('is equal to');
162                     
163                     // use equals for node 'My containers'
164                     if (value.path && value.path === Tine.Tinebase.container.getMyNodePath()) {
165                         operatorText = i18n._('is equal to')
166                     }
167                     this.filter.formFields.operator.setRawValue(operatorText);
168                 }
169             
170                 return Tine.widgets.container.SelectionComboBox.prototype.setValue.call(this, value);
171             },
172             listeners: {
173                 scope: this, 
174                 select: this.onFiltertrigger,
175                 specialkey: function(field, e){
176                      if(e.getKey() == e.ENTER){
177                          this.onFiltertrigger();
178                      }
179                 }
180             }
181         });
182         
183         filter.valueFields.FilterModelMultipleValueField = new Tine.widgets.container.FilterModelMultipleValueField({
184             hidden: valueType != 'FilterModelMultipleValueField',
185             app: this.app,
186             recordClass: this.recordClass,
187             containerName: this.containerName,
188             containersName: this.containersName,
189             filter: filter,
190             width: this.filterValueWidth,
191             //id: 'tw-ftb-frow-valuefield-' + filter.id,
192             value: filter.data.value ? filter.data.value : this.defaultValue,
193             renderTo: el,
194             listeners: {
195                 scope: this, 
196                 select: this.onFiltertrigger,
197                 specialkey: function(field, e){
198                      if(e.getKey() == e.ENTER){
199                          this.onFiltertrigger();
200                      }
201                 }
202             }
203         });
204         
205         return filter.valueFields[valueType];
206     }
207 });
208
209 Tine.widgets.grid.FilterToolbar.FILTERS['tine.widget.container.filtermodel'] = Tine.widgets.container.FilterModel;
210
211
212 /**
213  * @namespace   Tine.widgets.container
214  * @class       Tine.widgets.container.FilterModelMultipleValueField
215  * @extends     Ext.ux.form.LayerCombo
216  * 
217  * @author      Cornelius Weiss <c.weiss@metaways.de>
218  */
219 Tine.widgets.container.FilterModelMultipleValueField = Ext.extend(Ext.ux.form.LayerCombo, {
220     /**
221      * @cfg {string} containerName
222      * name of container (singular)
223      */
224     containerName: 'container',
225     
226     /**
227      * @cfg {string} containerName
228      * name of container (plural)
229      */
230     containersName: 'containers',
231     
232     /**
233      * @cfg {Tine.Tinebase.data.Record} recordClass
234      * record definition class  (required)
235      */
236     recordClass: null,
237     
238     hideButtons: false,
239     layerAlign : 'tr-br?',
240     minLayerWidth: 400,
241     layerHeight: 300,
242     
243     lazyInit: true,
244     
245     formConfig: {
246         labelAlign: 'left',
247         labelWidth: 30
248     },
249     
250     initComponent: function() {
251         this.containerName = this.containerName == 'container' && this.recordClass ? this.recordClass.getContainerName() : this.containerName;
252         this.containersName = this.containersName == 'containers' && this.recordClass ? this.recordClass.getContainersName() : this.containersName;
253
254         this.on('beforecollapse', this.onBeforeCollapse, this);
255         this.store = new Ext.data.SimpleStore({
256             fields: this.recordClass
257         });
258         
259         this.supr().initComponent.call(this);
260     },
261     
262     /**
263      * @return Ext.grid.ColumnModel
264      */
265     getColumnModel: function() {
266         return new Ext.grid.ColumnModel({
267             defaults: {
268                 sortable: false
269             },
270             columns:  [
271                 {id: 'name', header: String.format(i18n._('Selected {0}'), this.containersName), dataIndex: 'name'}
272             ]
273         });
274     },
275     
276     /**
277      * 
278      * @return {Array} of containerData
279      */
280     getFormValue: function() {
281         var container = [];
282         
283         this.store.each(function(containerRecord) {
284             container.push(containerRecord.data);
285         }, this);
286         
287         return container;
288     },
289     
290     getItems: function() {
291         var containerSelectionCombo = this.containerSelectionCombo = new Tine.widgets.container.SelectionComboBox({
292             allowNodeSelect: true,
293             allowBlank: true,
294             blurOnSelect: true,
295             recordClass: this.recordClass,
296             appName: this.recordClass.getMeta('appName'),
297             containerName: this.containerName,
298             containersName: this.containersName,
299             listeners: {
300                 scope: this, 
301                 select: this.onContainerSelect
302             }
303         });
304                 
305         this.containerGridPanel = new Tine.widgets.grid.PickerGridPanel({
306             height: this.layerHeight || 'auto',
307             recordClass: Tine.Tinebase.Model.Container,
308             store: this.store,
309             autoExpandColumn: 'name',
310             getColumnModel: this.getColumnModel.createDelegate(this),
311             initActionsAndToolbars: function() {
312                 Tine.widgets.grid.PickerGridPanel.prototype.initActionsAndToolbars.call(this);
313                 
314                 this.tbar = new Ext.Toolbar({
315                     layout: 'fit',
316                     items: [
317                         containerSelectionCombo
318                     ]
319                 });
320             }
321         });
322         var items = [this.containerGridPanel];
323         
324         return items;
325     },
326     
327     /**
328      * cancel collapse if ctx menu or container selection is shown
329      * 
330      * @return Boolean
331      */
332     onBeforeCollapse: function() {
333         var contextMenuVisible = this.containerGridPanel.contextMenu && ! this.containerGridPanel.contextMenu.hidden,
334             containerSelectionVisible = this.containerSelectionCombo.dlg && ! this.containerSelectionCombo.dlg.win.isDestroyed;
335             
336         return ! (contextMenuVisible || this.containerSelectionCombo.isExpanded() || containerSelectionVisible);
337     },
338     
339     onContainerSelect: function(field, containerData) {
340         var existingRecord = this.store.getById(containerData.id);
341         if (! existingRecord) {
342             var data = (containerData.data) ? containerData.data : containerData;
343             this.store.add(new Tine.Tinebase.Model.Container(data));
344             
345         } else {
346             var idx = this.store.indexOf(existingRecord);
347             var row = this.containerGridPanel.getView().getRow(idx);
348             Ext.fly(row).highlight();
349         }
350         
351         this.containerSelectionCombo.clearValue();
352     },
353     
354     /**
355      * @param {Array/Object} value
356      * @return {Ext.form.Field} this
357      */
358     setValue: function(value) {
359         this.value = value;
360         value = Ext.isArray(value) ? value : [value];
361
362         this.currentValue = [];
363         
364         this.store.removeAll();
365         var containerNames = [];
366         Ext.each(value, function(containerData) {
367             // ignore broken value
368             if (! containerData) return;
369
370             // ignore server name for node 'My containers'
371             if (containerData.path && containerData.path === Tine.Tinebase.container.getMyNodePath()) {
372                 containerData.name = null;
373             }
374             containerData.name = containerData.name || Tine.Tinebase.container.path2name(containerData.path, this.containerName, this.containersName);
375             containerData.id = containerData.id ||containerData.path;
376             
377             this.store.add(new Tine.Tinebase.Model.Container(containerData));
378             this.currentValue.push(containerData);
379             containerNames.push(containerData.name);
380         }, this);
381         
382         this.setRawValue(containerNames.join(', '));
383         return this;
384     },
385     
386     /**
387      * sets values to innerForm
388      */
389     setFormValue: function(value) {
390         // nothing to do
391     }
392 });
393