Merge branch 'pu/2013.03/modelconfig-hr'
[tine20] / tine20 / Tinebase / js / widgets / form / RecordPickerComboBox.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) 2007-2008 Metaways Infosystems GmbH (http://www.metaways.de)
7  */
8
9 /*global Ext, Tine*/
10
11 Ext.ns('Tine.Tinebase.widgets.form');
12
13 /**
14  * @namespace   Tine.Tinebase.widgets.form
15  * @author      Cornelius Weiss <c.weiss@metaways.de>
16  * @class       Tine.Tinebase.widgets.form.RecordPickerComboBox
17  * @extends     Ext.form.ComboBox
18  * 
19  * <p>Abstract base class for recordPickers like account/group pickers </p>
20  * 
21  * Usage:
22  * <pre><code>
23 var resourcePicker = new Tine.Tinebase.widgets.form.RecordPickerComboBox({
24     recordClass: Tine.Calendar.Model.Resource
25 });
26    </code></pre>
27  */
28 Tine.Tinebase.widgets.form.RecordPickerComboBox = Ext.extend(Ext.ux.form.ClearableComboBox, {
29     /**
30      * @cfg {bool} blurOnSelect
31      * blur this combo when record got selected, useful to be used in editor grids (defaults to false)
32      */
33     blurOnSelect: false,
34     
35     /**
36      * @cfg {Tine.Tinebase.data.Record} recordClass
37      * model of record to be picked (required) 
38      */
39     recordClass: null,
40     
41     /**
42      * @cfg {Tine.Tinebase.data.RecordProxy} recordProxy
43      * record backend 
44      */
45     recordProxy: null,
46     
47     /**
48      * @property app
49      * @type Tine.Tinebase.Application
50      */
51     app: null,
52     
53     /**
54      * @type Tine.Tinebase.data.Record selectedRecord
55      * @property selectedRecord 
56      * The last record which was selected
57      */
58     selectedRecord: null,
59     
60     /**
61      * sort by field
62      * 
63      * @type String 
64      */
65     sortBy: null,
66     
67     /**
68      * @type string
69      * @property lastStoreTransactionId
70      */
71     lastStoreTransactionId: null,
72     
73     /**
74      * if set to false, it is not possible to add the same record handled in this.editDialog
75      * this.editDialog must also be set
76      * 
77      * @cfg {Boolean} allowLinkingItself
78      */
79     allowLinkingItself: null,
80     
81     /**
82      * the editDialog, the form is nested in. Just needed if this.allowLinkingItself is set to false
83      * 
84      * @type Tine.widgets.dialog.EditDialog editDialog
85      */
86     editDialog: null,
87     
88     /**
89      * always use additional filter
90      * 
91      * @type {Array}
92      */
93     additionalFilters: null,
94     
95     triggerAction: 'all',
96     pageSize: 10,
97     minChars: 3,
98     forceSelection: true,
99     
100     /**
101      * additional filters to use for each query
102      * @type {Array}
103      */
104     additionalFilters: null,
105     
106     initComponent: function () {
107         this.app = Tine.Tinebase.appMgr.get(this.recordClass.getMeta('appName'));
108         this.displayField = this.recordClass.getMeta('titleProperty');
109         this.valueField = this.recordClass.getMeta('idProperty');
110         this.disableClearer = ! this.allowBlank;
111         
112         this.loadingText = _('Searching...');
113         
114         this.store = new Tine.Tinebase.data.RecordStore(Ext.copyTo({
115             readOnly: true,
116             proxy: this.recordProxy || undefined
117         }, this, 'totalProperty,root,recordClass'));
118         
119         this.on('beforequery', this.onBeforeQuery, this);
120         this.store.on('beforeloadrecords', this.onStoreBeforeLoadRecords, this);
121         this.initTemplate();
122         
123         Tine.Tinebase.widgets.form.RecordPickerComboBox.superclass.initComponent.call(this);
124     },
125     
126     /**
127      * respect record.getTitle method
128      */
129     initTemplate: function() {
130         if (! this.tpl) {
131             this.tpl = new Ext.XTemplate('<tpl for="."><div class="x-combo-list-item">{[this.getTitle(values.' + this.recordClass.getMeta('idProperty') + ')]}</div></tpl>', {
132                 getTitle: (function(id) {
133                     var record = this.getStore().getById(id),
134                         title = record ? record.getTitle() : '&nbsp';
135                     
136                     return Ext.util.Format.htmlEncode(title);
137                 }).createDelegate(this)
138             });
139         }
140     },
141     
142     
143     /**
144      * prepare paging and sort
145      * 
146      * @param {Ext.data.Store} store
147      * @param {Object} options
148      */
149     onBeforeLoad: function (store, options) {
150         Tine.Tinebase.widgets.form.RecordPickerComboBox.superclass.onBeforeLoad.call(this, store, options);
151         
152         this.lastStoreTransactionId = options.transactionId = Ext.id();
153         
154         var paging = {
155             // TODO do we need to set start & limit here?
156             start: options.params.start,
157             limit: options.params.limit,
158             // if sort is not set, use display field as default sort
159             sort: (this.sortBy) ? this.sortBy : this.displayField,
160             dir: 'ASC'
161         };
162         
163         Ext.applyIf(options.params, paging);
164         
165         // TODO is this needed?
166         options.params.paging = paging;
167     },
168     
169     /**
170      * onStoreBeforeLoadRecords
171      * 
172      * @param {Object} o
173      * @param {Object} options
174      * @param {Boolean} success
175      * @param {Ext.data.Store} store
176      */
177     onStoreBeforeLoadRecords: function(o, options, success, store) {
178         if (! this.lastStoreTransactionId || options.transactionId && this.lastStoreTransactionId !== options.transactionId) {
179             Tine.log.debug('Tine.Tinebase.widgets.form.RecordPickerComboBox::onStoreBeforeLoadRecords cancelling old transaction request.');
180             return false;
181         }
182     },
183     
184     /**
185      * use beforequery to set query filter
186      * 
187      * @param {Object} qevent
188      */
189     onBeforeQuery: function (qevent) {
190         var filter = [
191             {field: 'query', operator: 'contains', value: qevent.query }
192         ];
193         if (this.additionalFilters !== null && this.additionalFilters.length > 0) {
194             for (var i = 0; i < this.additionalFilters.length; i++) {
195                 filter.push(this.additionalFilters[i]);
196             }
197         }
198         this.store.baseParams.filter = filter;
199     },
200     
201     /**
202      * relay contextmenu events
203      * 
204      * @param {Ext.Container} ct
205      * @param {Number} position
206      * @private
207      */
208     onRender : function(ct, position){
209         Tine.Tinebase.widgets.form.RecordPickerComboBox.superclass.onRender.call(this, ct, position);
210         
211         var c = this.getEl();
212  
213         this.mon(c, {
214             scope: this,
215             contextmenu: Ext.emptyFn
216         });
217  
218         this.relayEvents(c, ['contextmenu']);
219     },
220     
221     /**
222      * store a copy of the selected record
223      * 
224      * @param {Tine.Tinebase.data.Record} record
225      * @param {Number} index
226      */
227     onSelect: function (record, index) {
228         this.selectedRecord = record;
229         return Tine.Tinebase.widgets.form.RecordPickerComboBox.superclass.onSelect.call(this, record, index);
230     },
231     
232     /**
233      * on keypressed("enter") event to add record
234      * 
235      * @param {Tine.Addressbook.SearchCombo} combo
236      * @param {Event} event
237      */ 
238     onSpecialkey: function (combo, event) {
239         if (event.getKey() === event.ENTER) {
240             var id = combo.getValue();
241             var record = this.store.getById(id);
242             this.onSelect(record);
243         }
244     },
245     
246     /**
247      * set value and prefill store if needed
248      * 
249      * @param {mixed} value
250      */
251     setValue: function (value) {
252         if (value) {
253             
254             // value is a record
255             if (typeof(value.get) === 'function') {
256                 if (this.store.indexOf(value) < 0) {
257                     this.store.addSorted(value);
258                 }
259                 value = value.get(this.valueField);
260             }
261             
262             // value is a js object
263             else if (Ext.isObject(value)) {
264                 var record = this.recordProxy ? this.recordProxy.recordReader({responseText: Ext.encode(value)}) : new this.recordClass(value)
265                 if (! this.store.getById(value.id)) {
266                     this.store.addSorted(record);
267                 }
268                 value = value[this.valueField] || '';
269             }
270             
271             // value is the current id
272             else if (Ext.isPrimitive(value) && value == this.getValue()) {
273                 return this.setValue(this.selectedRecord);
274             }
275         }
276         
277         var r = this.findRecord(this.valueField, value),
278             text = value;
279         
280         if (r){
281             text = r.getTitle();
282             this.selectedRecord = r;
283             if (this.allowLinkingItself === false) {
284                 // check if editDialog exists
285                 if (this.editDialog && this.editDialog.record && r.getId() == this.editDialog.record.getId()) {
286                     Ext.MessageBox.show({
287                         title: _('Failure'),
288                         msg: _('You tried to link a record with itself. This is not allowed!'),
289                         buttons: Ext.MessageBox.OK,
290                         icon: Ext.MessageBox.ERROR  
291                     });
292                     return;
293                 }
294             }
295             
296         } else if (Ext.isDefined(this.valueNotFoundText)){
297             text = this.valueNotFoundText;
298         }
299         this.lastSelectionText = text;
300         if (this.hiddenField){
301             this.hiddenField.value = Ext.value(value, '');
302         }
303         Tine.Tinebase.widgets.form.RecordPickerComboBox.superclass.setValue.call(this, text);
304         this.value = value;
305         return this;
306     }
307 });
308 Ext.reg('tinerecordpickercombobox', Tine.Tinebase.widgets.form.RecordPickerComboBox);