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