allow empty logger config
[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-2013 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         'text':     'string',
38         'boolean':  'bool',
39         'integer':  'int',
40         'float':    'float'
41     },
42     
43     /**
44      * initializes the starter
45      */
46     init: function() {
47         // Wait until appmgr is initialized
48         if (! Tine.Tinebase.hasOwnProperty('appMgr')) {
49             this.init.defer(100, this);
50             return;
51         }
52         
53         if (! this.userApplications || this.userApplications.length == 0) {
54             this.userApplications = Tine.Tinebase.registry.get('userApplications');
55             this.createStructure(true);
56         }
57     },
58     
59     /**
60      * returns the field
61      * 
62      * @param {Object} fieldDefinition
63      * @return {Object}
64      */
65     getField: function(fieldDefinition, key) {
66         // default type is auto
67         var field = {name: key};
68         
69         if (fieldDefinition.type) {
70             // add pre defined type
71             field.type = this.types[fieldDefinition.type];
72             switch (fieldDefinition.type) {
73                 case 'datetime':
74                     field.dateFormat = Date.patterns.ISO8601Long;
75                     break;
76                 case 'date':
77                     field.dateFormat = Date.patterns.ISO8601Long;
78                     break;
79                 case 'time':
80                     field.dateFormat = Date.patterns.ISO8601Time;
81                     break;
82                 case 'record':
83                 case 'records':
84                     fieldDefinition.config.modelName = fieldDefinition.config.modelName.replace(/_/, '');
85                     field.type = fieldDefinition.config.appName + '.' + fieldDefinition.config.modelName;
86                     break;
87             }
88             // allow overwriting date pattern in model
89             if (fieldDefinition.hasOwnProperty('dateFormat')) {
90                 field.dateFormat = fieldDefinition.dateFormat;
91             }
92         }
93         
94         // TODO: create field registry, add fields here
95         return field;
96     },
97     /**
98      * returns the grid renderer
99      * @param {Object} config
100      * @param {String} field
101      * @return {Function}
102      */
103     getGridRenderer: function(config, field, appName, modelName) {
104         var gridRenderer = null;
105         if (config && field) {
106             switch (config.type) {
107                 case 'record':
108                     gridRenderer = function(value, row, record) {
109                         var foreignRecordClass = Tine[config.config.appName].Model[config.config.modelName];
110                         var titleProperty = foreignRecordClass.getMeta('titleProperty');
111                         return record.get(field) ? Ext.util.Format.htmlEncode(record.get(field)[titleProperty]) : '';
112                     };
113                     break;
114                 case 'integer':
115                     if (config.hasOwnProperty('specialType')) {
116                         switch (config.specialType) {
117                             case 'bytes1000':
118                                 gridRenderer = function(a,b,c) {
119                                     return Tine.Tinebase.common.byteRenderer(a, b, c, 2, true);
120                                 };
121                                 break;
122                             case 'bytes':
123                                 gridRenderer = function(a,b,c) {
124                                     return Tine.Tinebase.common.byteRenderer(a, b, c, 2, false);
125                                 };
126                                 break;
127                             case 'minutes':
128                                 gridRenderer = Tine.Tinebase.common.minutesRenderer;
129                                 break;
130                             case 'seconds':
131                                 gridRenderer = Tine.Tinebase.common.secondsRenderer;
132                                 break;
133                             case 'usMoney':
134                                 gridRenderer = Ext.util.Format.usMoney;
135                                 break;
136                             case 'euMoney':
137                                 gridRenderer = Ext.util.Format.euMoney;
138                                 break;
139                             default:
140                                 gridRenderer = Ext.util.Format.htmlEncode;
141                         }
142                     }
143                     break;
144                 case 'user':
145                     gridRenderer = Tine.Tinebase.common.usernameRenderer;
146                     break;
147                 case 'keyfield': 
148                     gridRenderer = Tine.Tinebase.widgets.keyfield.Renderer.get(appName, config.name);
149                     break;
150                 case 'date':
151                     gridRenderer = Tine.Tinebase.common.dateRenderer;
152                     break;
153                 case 'datetime':
154                     gridRenderer = Tine.Tinebase.common.dateTimeRenderer;
155                     break;
156                 case 'time':
157                     gridRenderer = Tine.Tinebase.common.timeRenderer;
158                     break;
159                 case 'tag':
160                     gridRenderer = Tine.Tinebase.common.tagsRenderer;
161                     break;
162                 case 'container':
163                     gridRenderer = Tine.Tinebase.common.containerRenderer;
164                     break;
165                 case 'boolean':
166                     gridRenderer = Tine.Tinebase.common.booleanRenderer;
167                     break;
168                 default:
169                     gridRenderer = Ext.util.Format.htmlEncode;
170                  }
171            }
172         return gridRenderer;
173     },
174
175     /**
176      * used in getFilter for mapping types to filter
177      * 
178      * @type 
179      */
180     filterMap: function(type, fieldconfig, filter, filterconfig, appName, modelName, modelConfig) {
181         switch (type) {
182             case 'string':
183             case 'text':
184             case 'user':
185                 break;
186             case 'boolean': 
187                 filter.valueType = 'bool'
188                 filter.defaultValue = false;
189                 break;
190             case 'record':
191                 filterconfig.options.modelName = filterconfig.options.modelName.replace(/_/, '');
192                 var foreignApp = filterconfig.options.appName;
193                 var foreignModel = filterconfig.options.modelName;
194                 
195                 // create generic foreign id filter
196                 var filterclass = Ext.extend(Tine.widgets.grid.ForeignRecordFilter, {
197                     foreignRecordClass: foreignApp + '.' + foreignModel,
198                     linkType: 'foreignId',
199                     ownField: fieldconfig.key,
200                     label: filter.label
201                 });
202                 // register foreign id field as appName.modelName.fieldKey
203                 var fc = appName + '.' + modelName + '.' + fieldconfig.key;
204                 Tine.widgets.grid.FilterToolbar.FILTERS[fc] = filterclass;
205                 filter = {filtertype: fc};
206                 break;
207             case 'tag': 
208                 filter = {filtertype: 'tinebase.tag', app: appName};
209                 break;
210             case 'container':
211                 var applicationName = filterconfig.appName ? filterconfig.appName : appName;
212                 var modelName = filterconfig.modelName ? filterconfig.modelName : modelName;
213                 filter = {
214                     filtertype: 'tine.widget.container.filtermodel', 
215                     app: applicationName, 
216                     recordClass: applicationName + '.' + modelName,
217                     field: fieldconfig.key,
218                     label: fieldconfig.label,
219                     callingApp: appName
220                 };
221                 break;
222             case 'keyfield':
223                 filter.filtertype = 'tine.widget.keyfield.filter';
224                 filter.app = {name: appName};
225                 filter.keyfieldName = fieldconfig.name;
226                 break;
227             case 'date':
228                 filter.valueType = 'date';
229                 break;
230             case 'datetime':
231                 filter.valueType = 'date';
232                 break;
233             case 'integer':
234                 filter.valueType = 'number';
235         }
236         return filter;
237     },
238     
239     /**
240      * returns filter
241      * 
242      * @param {String} fieldKey
243      * @param {Object} filterconfig
244      * @param {Object} fieldconfig
245      * @return {Object}
246      */
247     getFilter: function(fieldKey, filterconfig, modelConfig) {
248         // take field label if no filterlabel is defined
249         var fieldconfig = modelConfig.fields[fieldKey];
250         var appName = modelConfig.appName;
251         var modelName = modelConfig.modelName;
252         
253         var app = Tine.Tinebase.appMgr.get(appName),
254             fieldTypeKey = (fieldconfig && fieldconfig.type) ? fieldconfig.type : (filterconfig && filterconfig.type) ? filterconfig.type : 'default',
255             label = (filterconfig && filterconfig.hasOwnProperty('label')) ? filterconfig.label : (fieldconfig && fieldconfig.hasOwnProperty('label')) ? fieldconfig.label : null,
256             globalI18n = ((filterconfig && filterconfig.hasOwnProperty('useGlobalTranslation')) || (fieldconfig && fieldconfig.hasOwnProperty('useGlobalTranslation')));
257         
258         if (! label) {
259             return null;
260         }
261         // prepare filter
262         var filter = {
263             label: globalI18n ? _(label) : app.i18n._(label),
264             field: fieldKey
265         };
266         
267         if (filterconfig) {
268             if (filterconfig.hasOwnProperty('options') && (filterconfig.options.hasOwnProperty('jsFilterType') || filterconfig.options.hasOwnProperty('jsFilterValueType'))) {
269                 Tine.log.err('jsFilterType and jsFilterValueType are deprecated. Use jsConfig.<property> instead.');
270             }
271             // if js filter is defined in filterconfig.options, take this and return
272             if (filterconfig.hasOwnProperty('jsConfig')) {
273                 Ext.apply(filter, filterconfig.jsConfig);
274                 return filter;
275             } 
276             
277             try {
278                 filter = this.filterMap(fieldTypeKey, fieldconfig, filter, filterconfig, appName, modelName, modelConfig);
279             } catch (e) {
280                 var keys = filterconfig.filter.split('_'),
281                     filterkey = keys[0].toLowerCase() + '.' + keys[2].toLowerCase();
282                     filterkey = filterkey.replace(/filter/g, '');
283     
284                 if (Tine.widgets.grid.FilterToolbar.FILTERS[filterkey]) {
285                     filter = {filtertype: filterkey};
286                 } else { // set to null if no filter could be found
287                     filter = null;
288                 }
289             }
290         }
291         return filter;
292     },
293     
294     /**
295      * if application starter should be used, here the js contents are (pre-)created
296      */
297     createStructure: function(initial) {
298         var start = new Date();
299         Ext.each(this.userApplications, function(app) {
300             var appName = app.name;
301             Ext.namespace('Tine.' + appName);
302             
303             var models = Tine[appName].registry ? Tine[appName].registry.get('models') : null;
304             
305             if (models) {
306                 
307                 Tine[appName].isAuto = true;
308                 var contentTypes = [];
309                 
310                 // create translation
311                 Tine[appName].i18n = new Locale.Gettext();
312                 Tine[appName].i18n.textdomain(appName);
313                 
314                 // iterate models of this app
315                 Ext.iterate(models, function(modelName, modelConfig) {
316                     var containerProperty = modelConfig.hasOwnProperty('containerProperty') ? modelConfig.containerProperty : null;
317                     
318                     modelName = modelName.replace(/_/, '');
319                     
320                     Ext.namespace('Tine.' + appName, 'Tine.' + appName + '.Model');
321                     
322                     var modelArrayName = modelName + 'Array',
323                         modelArray = [];
324
325                     if (modelConfig.createModule) {
326                         contentTypes.push(modelConfig);
327                     }
328                     
329                     // iterate record fields
330                     Ext.each(modelConfig.fieldKeys, function(key) {
331                         // add field to model array
332                         modelArray.push(this.getField(modelConfig.fields[key], key));
333                         
334                         if (modelConfig.fields[key].label) {
335                             // register grid renderer
336                             if (initial) {
337                                 var renderer = this.getGridRenderer(modelConfig.fields[key], key, appName, modelName);
338                                 if (renderer) {
339                                     if (! Tine.widgets.grid.RendererManager.has(appName, modelName, key)) {
340                                         Tine.widgets.grid.RendererManager.register(appName, modelName, key, renderer);
341                                     }
342                                 }
343                             }
344                         }
345                         
346                     }, this);
347                     
348                     // iterate virtual record fields
349                     if (modelConfig.virtualFields && modelConfig.virtualFields.length) {
350                         Ext.each(modelConfig.virtualFields, function(field) {
351                             modelArray.push(this.getField(field, field.key));
352                         }, this);
353                     }
354                     
355                     // collect the filterModel
356                     var filterModel = [];
357                     Ext.iterate(modelConfig.filterModel, function(key, filter) {
358                         var f = this.getFilter(key, filter, modelConfig);
359                         
360                         if (f) {
361                             Tine.widgets.grid.FilterRegistry.register(appName, modelName, f);
362                             filterModel.push(f);
363                         }
364                     }, this);
365                     
366                     // TODO: registry looses info if gridpanel resides in an editDialog
367                     // delete filterModel as all filters are in the filter registry now
368                     // delete modelConfig.filterModel;
369                     
370                     Tine[appName].Model[modelArrayName] = modelArray;
371                     
372                     // create model
373                     if (! Tine[appName].Model.hasOwnProperty(modelName)) {
374                         Tine[appName].Model[modelName] = Tine.Tinebase.data.Record.create(Tine[appName].Model[modelArrayName], 
375                             Ext.copyTo({}, modelConfig, 
376                                'defaultFilter,appName,modelName,recordName,recordsName,titleProperty,containerProperty,containerName,containersName,group')
377                         );
378                         Tine[appName].Model[modelName].getFilterModel = function() {
379                             return filterModel;
380                         }
381                     }
382                     
383                     Ext.namespace('Tine.' + appName);
384                     
385                     // create recordProxy
386                     var recordProxyName = modelName.toLowerCase() + 'Backend';
387                     if (! Tine[appName].hasOwnProperty(recordProxyName)) {
388                         Tine[appName][recordProxyName] = new Tine.Tinebase.data.RecordProxy({
389                             appName: appName,
390                             modelName: modelName,
391                             recordClass: Tine[appName].Model[modelName]
392                         });
393                     }
394                     
395                     // overwrite function
396                     Tine[appName].Model[modelName].getDefaultData = function() {
397                         if (! dd) {
398                             var dd = Ext.decode(Ext.encode(modelConfig.defaultData));
399                         }
400                         
401                         // find container by selection or use defaultContainer by registry
402                         if (modelConfig.containerProperty) {
403                             if (! dd.hasOwnProperty(modelConfig.containerProperty)) {
404                                 var app = Tine.Tinebase.appMgr.get(appName),
405                                     registry = app.getRegistry(),
406                                     ctp = app.getMainScreen().getWestPanel().getContainerTreePanel();
407                                     
408                                 var container = (ctp ? ctp.getDefaultContainer() : null) || (registry ? registry.get("default" + modelName + "Container") : null);
409                                 
410                                 if (container) {
411                                     dd[modelConfig.containerProperty] = container;
412                                 }
413                             }
414                         }
415                         return dd;
416                     };
417                     
418                     // create filter panel
419                     var filterPanelName = modelName + 'FilterPanel';
420                     if (! Tine[appName].hasOwnProperty(filterPanelName)) {
421                         Tine[appName][filterPanelName] = function(c) {
422                             Ext.apply(this, c);
423                             Tine[appName][filterPanelName].superclass.constructor.call(this);
424                         };
425                         Ext.extend(Tine[appName][filterPanelName], Tine.widgets.persistentfilter.PickerPanel);
426                     }
427                     // create container tree panel, if needed
428                     if (containerProperty) {
429                         var containerTreePanelName = modelName + 'TreePanel';
430                         if (! Tine[appName].hasOwnProperty(containerTreePanelName)) {
431                             Tine[appName][containerTreePanelName] = Ext.extend(Tine.widgets.container.TreePanel, {
432                                 filterMode: 'filterToolbar',
433                                 recordClass: Tine[appName].Model[modelName]
434                             });
435                         }
436                     }
437                     
438                     // create main screen
439                     if(! Tine[appName].hasOwnProperty('MainScreen')) {
440                         Tine[appName].MainScreen = Ext.extend(Tine.widgets.MainScreen, {
441                             app: appName,
442                             contentTypes: contentTypes,
443                             activeContentType: modelName
444                         });
445                     }
446                     
447                     // create editDialog openWindow function only if edit dialog exists
448                     var editDialogName = modelName + 'EditDialog';
449                     
450                     if (Tine[appName].hasOwnProperty(editDialogName)) {
451                         var edp = Tine[appName][editDialogName].prototype;
452                         if (containerProperty) {
453                             edp.showContainerSelector = true;
454                         }
455                         Ext.apply(edp, {
456                             modelConfig:      Ext.encode(modelConfig),
457                             modelName:        modelName,
458                             recordClass:      Tine[appName].Model[modelName],
459                             recordProxy:      Tine[appName][recordProxyName],
460                             appName:          appName,
461                             windowNamePrefix: modelName + 'EditWindow_'
462                         });
463                         if (! Ext.isFunction(Tine[appName][editDialogName].openWindow)) {
464                             Tine[appName][editDialogName].openWindow  = function (cfg) {
465                                 var id = (cfg.record && cfg.record.id) ? cfg.record.id : 0;
466                                 var window = Tine.WindowFactory.getWindow({
467                                     width: edp.windowWidth ? edp.windowWidth : 600,
468                                     height: edp.windowHeight ? edp.windowHeight : 230,
469                                     name: edp.windowNamePrefix + id,
470                                     contentPanelConstructor: 'Tine.' + appName + '.' + editDialogName,
471                                     contentPanelConstructorConfig: cfg
472                                 });
473                                 return window;
474                             };
475                         }
476                     }
477                     // create Gridpanel
478                     var gridPanelName = modelName + 'GridPanel', 
479                         gpConfig = {
480                             modelConfig: modelConfig,
481                             app: Tine[appName], 
482                             recordProxy: Tine[appName][recordProxyName],
483                             recordClass: Tine[appName].Model[modelName]
484                         };
485                         
486                     if (! Tine[appName].hasOwnProperty(gridPanelName)) {
487                         Tine[appName][gridPanelName] = Ext.extend(Tine.widgets.grid.GridPanel, gpConfig);
488                     } else {
489                         Ext.apply(Tine[appName][gridPanelName].prototype, gpConfig);
490                     }
491                 }, this);
492             }
493         }, this);
494         
495         var stop = new Date();
496     }
497 }