Merge branch 'pu/inventory'
[tine20] / tine20 / Tinebase / js / ApplicationStarter.js
1 /*
2  * Tine 2.0
3  * 
4  * @package     Tinebase
5  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
6  * @author      Alexander Stintzing <a.stintzing@metaways.de>
7  * @copyright   Copyright (c) 2012 Metaways Infosystems GmbH (http://www.metaways.de)
8  *
9  */
10 Ext.namespace('Tine.Tinebase');
11
12 /**
13  * Tinebase Application Starter
14  * 
15  * @namespace   Tine.Tinebase
16  * @function    Tine.MailAccounting.MailAggregateGridPanel
17  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
18  * @author      Alexander Stintzing <a.stintzing@metaways.de>
19  */
20 Tine.Tinebase.ApplicationStarter = {
21     
22     /**
23      * the applictions the user has access to
24      * @type 
25      */
26     userApplications: null,
27     
28     /**
29      * type mapping
30      * @type {Object}
31      */
32     types: {
33         'date':     'date',
34         'datetime': 'date',
35         'time':     'date',
36         'string':   'string',
37         'boolean':  'bool',
38         'integer':  'int',
39         'float':    'float'
40     },
41     
42     /**
43      * maps php filters to value types of js filter
44      * @type {Object}
45      */
46     filters: {
47         'Tinebase_Model_Filter_Id': null,
48         'Tinebase_Model_Filter_Text': 'string',
49         'Tinebase_Model_Filter_Date': 'date',
50         'Tinebase_Model_Filter_DateTime': 'date',
51         'Tinebase_Model_Filter_Bool': 'bool',
52         'Tinebase_Model_Filter_ForeignId': 'foreign',
53         'Tinebase_Model_Filter_Int': 'number',
54         'Tinebase_Model_Filter_User': 'user'
55     },
56     
57     /**
58      * initializes the starter
59      */
60     init: function() {
61         // Wait until appmgr is initialized
62         if (!Tine.Tinebase.hasOwnProperty('appMgr')) {
63             this.init.defer(100, this);
64             return;
65         }
66         if(!this.userApplications) {
67             this.userApplications = Tine.Tinebase.registry.get('userApplications');
68             this.createStructure(true)
69         }
70     },
71     
72     /**
73      * returns the field
74      * @param {Object} fieldconfig
75      * @return {Object}
76      */
77     getField: function(fieldconfig, key) {     
78         // default type is auto
79         var field = {name: key};
80         
81         if (fieldconfig.type) {
82             // add pre defined type
83             field.type = this.types[fieldconfig.type];
84             switch (fieldconfig.type) {
85                 case 'datetime':
86                     field.dateFormat = Date.patterns.ISO8601Long;
87                     break;
88                 case 'date':
89                     field.dateFormat = Date.patterns.ISO8601Long;
90                     break;
91                 case 'time':
92                     field.dateFormat = Date.patterns.ISO8601Time;
93                 case 'foreign':
94                     field.type = fieldconfig.options.app + '.' + fieldconfig.options.model;
95                     break;
96             }
97             // allow overwriting date pattern in model
98             if (fieldconfig.hasOwnProperty('dateFormat')) {
99                 field.dateFormat = fieldconfig.dateFormat;
100             }
101         }
102         
103         // TODO: create field registry, add fields here
104         return field;
105     },
106     /**
107      * returns the grid renderer
108      * @param {Object} config
109      * @param {String} field
110      * @return {Function}
111      */
112     getGridRenderer: function(config, field, appName, modelName) {
113         var gridRenderer = null;
114         if(config && field) {
115             switch (config.type) {
116                 case 'foreign':
117                     gridRenderer = function(value, row, record) {
118                         var foreignRecordClass = Tine[config.options.app].Model[config.options.model];
119                         var titleProperty = foreignRecordClass.getMeta('titleProperty');
120                         return record.get(field) ? Ext.util.Format.htmlEncode(record.get(field)[titleProperty]) : '';
121                     }
122                     break;
123                 case 'integer':
124                     if(config.hasOwnProperty('specialType')) {
125                         var useDecimal = (config.specialType == 'bytes1000');
126                         gridRenderer = function(a,b,c) {
127                             return Tine.Tinebase.common.byteRenderer(a, b, c, 2, useDecimal);
128                         }
129                     }
130                     break;
131                 case 'user':
132                     gridRenderer = Tine.Tinebase.common.usernameRenderer;
133                     break;
134                 case 'keyfield': 
135                     gridRenderer = Tine.Tinebase.widgets.keyfield.Renderer.get(appName, config.name);
136                     break;
137                 case 'date':
138                     gridRenderer = Tine.Tinebase.common.dateRenderer;
139                     break;
140                 case 'datetime':
141                     gridRenderer = Tine.Tinebase.common.dateTimeRenderer;
142                     break;
143                  case 'time':
144                     gridRenderer = Tine.Tinebase.common.timeRenderer;
145                     break;
146                 case 'tag':
147                     gridRenderer = Tine.Tinebase.common.tagsRenderer;
148                     break;
149                 case 'container':
150                     gridRenderer = Tine.Tinebase.common.containerRenderer;
151                     break;
152                 case 'boolean':
153                     gridRenderer = Tine.Tinebase.common.booleanRenderer;
154                     break;
155                  default:
156                     gridRenderer = function(value) {
157                         return Ext.util.Format.htmlEncode(value);
158                     }
159                 }
160            }
161         return gridRenderer;
162     },
163
164     /**
165      * returns filter
166      * @param {String} key
167      * @param {Object} filterconfig
168      * @param {Object} fieldconfig
169      * @return {Object}
170      */
171     getFilter: function(key, filterconfig, fieldconfig, appName, modelName) {
172         // take field label if no filterlabel is defined
173         var label = (filterconfig && filterconfig.label) ? filterconfig.label : (fieldconfig && fieldconfig.label) ? fieldconfig.label : null;
174         var app = Tine.Tinebase.appMgr.get(appName);
175         
176         if (! label && (key != 'query')) {
177             return null;
178         }
179         // prepare filter
180         var filter = {
181             label: app.i18n._(label),
182             field: key
183         };
184
185         if (filterconfig) {
186             // if js filter is defined in filterconfig.options, take this and return
187             if(filterconfig.hasOwnProperty('options') && (filterconfig.options.hasOwnProperty('jsFilterType') || filterconfig.options.hasOwnProperty('jsFilterValueType'))) {
188                 if(filterconfig.options.jsFilterType) {
189                     filter.filtertype = filterconfig.options.jsFilterType;
190                 }
191                 if(filterconfig.options.jsFilterValueType) {
192                     filter.valueType = filterconfig.options.jsFilterValueType;
193                 }
194                 return filter;
195             } 
196             
197             switch (filterconfig.filter) {
198                 case 'Tinebase_Model_Filter_Bool':
199                     filter.valueType = this.filters[filterconfig.filter];
200                     filter.defaultValue = false;
201                     break;
202                 case 'Tinebase_Model_Filter_ForeignId':
203                     // create generic foreign id filter
204                     var filterclass = Ext.extend(Tine.widgets.grid.ForeignRecordFilter, {
205                         foreignRecordClass: fieldconfig.options.app + '.' + fieldconfig.options.model,
206                         linkType: 'foreignId',
207                         ownField: key
208                     });
209                     var a = appName.toLowerCase();
210                     var b = fieldconfig.options.model.toLowerCase();
211                     var fc = a + '.' + b; 
212                     Tine.widgets.grid.FilterToolbar.FILTERS[fc] = filterclass;
213                     filter = {filtertype: fc};
214                     break;
215                 case 'Tinebase_Model_Filter_Tag':
216                     filter = {filtertype: 'tinebase.tag', app: appName};
217                     break;
218                 case 'Tinebase_Model_Filter_Container':
219                     filter = {filtertype: 'tine.widget.container.filtermodel', app: appName, recordClass: appName + '.' + modelName};
220                     break;
221                 case 'Tinebase_Model_Filter_Query':
222                     filter = Ext.apply(filter, {label: _('Quick search'), operators: ['contains']});
223                     break;
224                 default:
225                     if (this.filters[filterconfig.filter]) {  // use pre-defined default filter (this.filters)
226                         filter.valueType = this.filters[filterconfig.filter];
227                     } else if (fieldconfig && fieldconfig.hasOwnProperty('type') && fieldconfig.type == 'keyfield') {
228                         filter.filtertype = 'tine.widget.keyfield.filter';
229                         filter.app = {name: appName};
230                         filter.keyfieldName = fieldconfig.name;
231                     } else {    // try to find registered filter
232                         var keys = filterconfig.filter.split('_'),
233                             filterkey = keys[0].toLowerCase() + '.' + keys[2].toLowerCase();
234                             filterkey = filterkey.replace(/filter/g, '');
235             
236                         if(Tine.widgets.grid.FilterToolbar.FILTERS[filterkey]) {
237                             filter = {filtertype: filterkey};
238                         } else { // set to null if no filter could be found
239                             filter = null;
240                         }
241                     }
242                 }
243             }
244         return filter;
245     },
246     
247     /**
248      * if application starter should be used, here the js contents are (pre-)created
249      */
250     createStructure: function(initial) {
251         var start = new Date();
252         Ext.each(this.userApplications, function(app) {
253             var appName = app.name;
254             Ext.namespace('Tine.' + appName);
255             if(Tine[appName].registry && Tine[appName].registry.get('models')) {
256                 Tine[appName].isAuto = true;
257                 var models = Tine[appName].registry.get('models');
258                 var contentTypes = [];
259
260                 // create translation
261                 Tine[appName].i18n = new Locale.Gettext();
262                 Tine[appName].i18n.textdomain(appName);
263                 
264                 // iterate models of this app
265                 Ext.iterate(models, function(model, config) {
266                     Ext.namespace('Tine.' + appName, 'Tine.' + appName + '.Model');
267                     var modelArrayName = model + 'Array';
268                     var rA = [];
269                     contentTypes.push(config);
270                     
271                     var defaultData = {},
272                         filterModel = [];
273                     // iterate record fields
274                     Ext.each(config.keys, function(key) {
275                         // add field to model array
276                         rA.push(this.getField(config.fields[key], key));
277                         // create default data
278                         defaultData[key] = config.fields[key].standard ? config.fields[key].standard : null;
279                         // if field config has label, create grid renderer
280                         if(config.fields[key].label) {
281                             // register grid renderer
282                             if(initial) {
283                                 var renderer = this.getGridRenderer(config.fields[key], key, appName, model);
284                                 if(renderer) {
285                                     if(! Tine.widgets.grid.RendererManager.has(appName, model, key)) {
286                                         Tine.widgets.grid.RendererManager.register(appName, model, key, renderer);
287                                     }
288                                 }
289                             }
290                         }
291                     }, this);
292                     
293                     if(config.hasOwnProperty('meta')) {
294                         // relations
295                         if(config.meta.hasRelations) {
296                             rA.push('relations');
297                         }
298                         // tags
299                         if(config.meta.hasTags) {
300                             rA.push('tags');
301                         }
302                         // customfields
303                         if(config.meta.hasCustomFields || config.meta.hasCustomfields) {
304                             rA.push('customfields');
305                         }
306                         // notes
307                         if(config.meta.hasNotes) {
308                             rA.push('notes');
309                         }
310                         Ext.iterate(config.filter, function(key, filter) {
311                             // create Filter Model
312                             var f = this.getFilter(key, filter, config.fields[key], appName, model);
313                             if (f) {
314                                 filterModel.push(f);
315                             }
316                         }, this);
317                     }
318                     // add generic fields if modlog is active
319                     Tine[appName].Model[modelArrayName] = (config.meta && config.meta.useModlog) ? Tine.Tinebase.Model.genericFields.concat(rA) : rA;
320                     
321                     // create model
322                     if(! Tine[appName].Model.hasOwnProperty(model)) {
323                         Tine[appName].Model[model] = Tine.Tinebase.data.Record.create(Tine[appName].Model[modelArrayName], Ext.apply(config.meta ? config.meta : {}, {
324                             appName: appName,
325                             modelName: model
326                         }));
327                     }
328                     Ext.namespace('Tine.' + appName);
329                     // create recordProxy
330                     var recordProxyName = model.toLowerCase() + 'Backend';
331                     if(! Tine[appName].hasOwnProperty(recordProxyName)) {
332                         Tine[appName][recordProxyName] = new Tine.Tinebase.data.RecordProxy({
333                             appName: appName,
334                             modelName: model,
335                             recordClass: Tine[appName].Model[model]
336                         });
337                     }
338
339                     // create container tree panel, if needed
340                     var containerTreePanelName = model + 'TreePanel';
341                     if(! Tine[appName].hasOwnProperty(containerTreePanelName)) {
342                         Tine[appName][containerTreePanelName] = Ext.extend(Tine.widgets.container.TreePanel, {
343                             filterMode: 'filterToolbar',
344                             recordClass: Tine[appName].Model[model]
345                         });
346                     }
347                     
348                     // create default data
349                     if(config.meta) {
350                         // get default container if needed
351                         if(config.meta.containerProperty) {
352                             var defaultContainer = Tine[appName].registry.get('default' + model + 'Container');
353                             if (defaultContainer) {
354                                 defaultData[config.meta.containerProperty] = defaultContainer;
355                             }
356                         }
357                         
358                         // add notes if needed
359                         if(config.meta.hasNotes) {
360                             defaultData['notes'] = [];
361                         }
362                         // add tags if needed
363                         if(config.meta.hasTags) {
364                             defaultData['tags'] = [];
365                         }
366                         // add customfields if needed
367                         if(config.meta.hasCustomFields) {
368                             defaultData['customfields'] = {};
369                         }
370                         // overwrite function
371                         Tine[appName].Model[model].getDefaultData = function() {
372                             if(!dd) var dd = Ext.decode(Ext.encode(defaultData));
373                             return dd;
374                         };
375                         Tine[appName].Model[model].getDefaultData();
376                     }
377                     
378                     // create the filter model
379                     if (!Ext.isFunction(Tine[appName].Model[model].getFilterModel)) {
380                         Tine[appName].Model[model].getFilterModel = function() {
381                             if(!pF) var pF = Ext.decode(Ext.encode(filterModel));
382                             return pF;
383                         };
384                         Tine[appName].Model[model].getFilterModel();
385                     }
386
387                     // create filter panel
388                     var filterPanelName = model + 'FilterPanel';
389                     if (! Tine[appName].hasOwnProperty(filterPanelName)) {
390                         Tine[appName][filterPanelName] = function(config) {
391                             Ext.apply(this, config);
392                             Tine[appName][filterPanelName].superclass.constructor.call(this);
393                         };
394                         Ext.extend(Tine[appName][filterPanelName], Tine.widgets.persistentfilter.PickerPanel);
395                     }
396
397                     // create main screen
398                     if(! Tine[appName].hasOwnProperty('MainScreen')) {
399                         Tine[appName].MainScreen = Ext.extend(Tine.widgets.MainScreen, {
400                             app: appName,
401                             contentTypes: contentTypes,
402                             activeContentType: model
403                         });
404                     }
405                     
406                     var editDialogName = model + 'EditDialog';
407                     // create editDialog openWindow function only if edit dialog exists
408                     if(Tine[appName].hasOwnProperty(editDialogName)) {
409                         if(config.meta.containerProperty) {
410                             Tine[appName][editDialogName].prototype.showContainerSelector = true;
411                         }
412                         if(!Ext.isFunction(Tine[appName][editDialogName].openWindow)) {
413                             Tine[appName][editDialogName].openWindow  = function (cfg) {
414                                 var id = (cfg.record && cfg.record.id) ? cfg.record.id : 0;
415                                 var window = Tine.WindowFactory.getWindow({
416                                     width: Tine[appName][editDialogName].prototype.windowWidth ? Tine[appName][editDialogName].prototype.windowWidth : 600,
417                                     height: Tine[appName][editDialogName].prototype.windowHeight ? Tine[appName][editDialogName].prototype.windowHeight : 230,
418                                     name: Tine[appName][editDialogName].prototype.windowNamePrefix + id,
419                                     contentPanelConstructor: 'Tine.' + appName + '.' + editDialogName,
420                                     contentPanelConstructorConfig: cfg
421                                 });
422                                 return window;
423                             };
424                         }
425                     }
426                     // create Gridpanel
427                     var gridPanelName = model + 'GridPanel';
428                     if (! Tine[appName].hasOwnProperty(gridPanelName)) {
429                         Tine[appName][gridPanelName] = Ext.extend(Tine.widgets.grid.GridPanel, {
430                             modelConfig: config,
431                             app: Tine[appName], 
432                             recordProxy: Tine[appName][recordProxyName],
433                             recordClass: Tine[appName].Model[model]
434                         });
435                     } else {
436                          Ext.apply(Tine[appName][gridPanelName].prototype, {
437                             modelConfig: config,
438                             app: Tine[appName], 
439                             recordProxy: Tine[appName][recordProxyName],
440                             recordClass: Tine[appName].Model[model]
441                         });
442                     }
443                 }, this);
444             }
445         }, this);
446         
447         var stop = new Date();
448     }
449 }