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)
10 Ext.namespace('Tine.Tinebase');
13 * Tinebase Application Starter
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>
20 Tine.Tinebase.ApplicationStarter = {
23 * the applictions the user has access to
26 userApplications: null,
44 * initializes the starter
47 // Wait until appmgr is initialized
48 if (! Tine.Tinebase.hasOwnProperty('appMgr')) {
49 this.init.defer(100, this);
53 Tine.log.info('ApplicationStarter::init');
55 if (! this.userApplications || this.userApplications.length == 0) {
56 this.userApplications = Tine.Tinebase.registry.get('userApplications');
57 this.createStructure(true);
64 * @param {Object} fieldDefinition
67 getField: function(fieldDefinition, key) {
68 // default type is auto
69 var field = {name: key};
71 if (fieldDefinition.type) {
72 // add pre defined type
73 field.type = this.types[fieldDefinition.type];
74 switch (fieldDefinition.type) {
76 field.dateFormat = Date.patterns.ISO8601Long;
79 field.dateFormat = Date.patterns.ISO8601Long;
82 field.dateFormat = Date.patterns.ISO8601Time;
86 fieldDefinition.config.modelName = fieldDefinition.config.modelName.replace(/_/, '');
87 field.type = fieldDefinition.config.appName + '.' + fieldDefinition.config.modelName;
90 // allow overwriting date pattern in model
91 if (fieldDefinition.hasOwnProperty('dateFormat')) {
92 field.dateFormat = fieldDefinition.dateFormat;
95 if (fieldDefinition.hasOwnProperty('label')) {
96 field.label = fieldDefinition.label;
100 // TODO: create field registry, add fields here
104 * returns the grid renderer
105 * @param {Object} config
106 * @param {String} field
109 getGridRenderer: function(config, field, appName, modelName) {
110 var gridRenderer = null;
111 if (config && field) {
112 switch (config.type) {
114 if (Tine.Tinebase.common.hasRight('view', config.config.appName, config.config.modelName.toLowerCase())) {
115 gridRenderer = function(value, row, record) {
116 var foreignRecordClass = Tine[config.config.appName].Model[config.config.modelName];
117 var titleProperty = foreignRecordClass.getMeta('titleProperty');
118 return record.get(field) ? Ext.util.Format.htmlEncode(record.get(field)[titleProperty]) : '';
126 if (config.hasOwnProperty('specialType')) {
127 switch (config.specialType) {
129 gridRenderer = function(value, cell, record) {
130 return Tine.Tinebase.common.byteRenderer(value, cell, record, 2, true);
134 gridRenderer = function(value, cell, record) {
135 return Tine.Tinebase.common.byteRenderer(value, cell, record, 2, false);
139 gridRenderer = Tine.Tinebase.common.minutesRenderer;
142 gridRenderer = Tine.Tinebase.common.secondsRenderer;
145 gridRenderer = Ext.util.Format.usMoney;
148 gridRenderer = Ext.util.Format.euMoney;
151 gridRenderer = function(value, cell, record) {
152 return Tine.Tinebase.common.percentRenderer(value, config.type);
156 gridRenderer = Ext.util.Format.htmlEncode;
161 gridRenderer = Tine.Tinebase.common.usernameRenderer;
164 gridRenderer = Tine.Tinebase.widgets.keyfield.Renderer.get(appName, config.name);
167 gridRenderer = Tine.Tinebase.common.dateRenderer;
170 gridRenderer = Tine.Tinebase.common.dateTimeRenderer;
173 gridRenderer = Tine.Tinebase.common.timeRenderer;
176 gridRenderer = Tine.Tinebase.common.tagsRenderer;
179 gridRenderer = Tine.Tinebase.common.containerRenderer;
182 gridRenderer = Tine.Tinebase.common.booleanRenderer;
185 var cc = config.config;
186 gridRenderer = new Tine.widgets.relation.GridRenderer({
189 foreignApp: cc.appName,
190 foreignModel: cc.modelName
194 gridRenderer = Ext.util.Format.htmlEncode;
201 * used in getFilter for mapping types to filter
205 filterMap: function(type, fieldconfig, filter, filterconfig, appName, modelName, modelConfig) {
211 filter.valueType = 'user';
214 filter.valueType = 'bool'
215 filter.defaultValue = false;
218 filterconfig.options.modelName = filterconfig.options.modelName.replace(/_/, '');
219 var foreignApp = filterconfig.options.appName;
220 var foreignModel = filterconfig.options.modelName;
222 // create generic foreign id filter
223 var filterclass = Ext.extend(Tine.widgets.grid.ForeignRecordFilter, {
224 foreignRecordClass: foreignApp + '.' + foreignModel,
225 linkType: 'foreignId',
226 ownField: fieldconfig.key,
229 // register foreign id field as appName.modelName.fieldKey
230 var fc = appName + '.' + modelName + '.' + fieldconfig.key;
231 Tine.widgets.grid.FilterToolbar.FILTERS[fc] = filterclass;
232 filter = {filtertype: fc};
235 filter = {filtertype: 'tinebase.tag', app: appName};
238 var applicationName = filterconfig.appName ? filterconfig.appName : appName;
239 var modelName = filterconfig.modelName ? filterconfig.modelName : modelName;
241 filtertype: 'tine.widget.container.filtermodel',
242 app: applicationName,
243 recordClass: applicationName + '.' + modelName,
244 field: fieldconfig.key,
245 label: fieldconfig.label,
250 filter.filtertype = 'tine.widget.keyfield.filter';
251 filter.app = {name: appName};
252 filter.keyfieldName = fieldconfig.name;
255 filter.valueType = 'date';
258 filter.valueType = 'date';
262 filter.valueType = 'number';
270 * @param {String} fieldKey
271 * @param {Object} filterconfig
272 * @param {Object} fieldconfig
275 getFilter: function(fieldKey, filterconfig, modelConfig) {
276 // take field label if no filterlabel is defined
277 // TODO Refactor: tag and tags see ticket 0008944
278 // TODO Remove this ugly hack!
279 if (fieldKey == 'tag') {
282 var fieldconfig = modelConfig.fields[fieldKey];
283 var appName = modelConfig.appName;
284 var modelName = modelConfig.modelName;
286 var app = Tine.Tinebase.appMgr.get(appName);
288 Tine.log.error('Application ' + appName + ' not found!');
292 // check right on foreign app
293 if (fieldconfig && (fieldconfig.type == 'record' || fieldconfig.type == 'records')) {
294 var opt = fieldconfig.config;
296 if (opt && (! Tine.Tinebase.common.hasRight('view', opt.appName, opt.modelName.toLowerCase()))) {
301 var fieldTypeKey = (fieldconfig && fieldconfig.type) ? fieldconfig.type : (filterconfig && filterconfig.type) ? filterconfig.type : 'default',
302 label = (filterconfig && filterconfig.hasOwnProperty('label')) ? filterconfig.label : (fieldconfig && fieldconfig.hasOwnProperty('label')) ? fieldconfig.label : null,
303 globalI18n = ((filterconfig && filterconfig.hasOwnProperty('useGlobalTranslation')) || (fieldconfig && fieldconfig.hasOwnProperty('useGlobalTranslation')));
310 label: globalI18n ? i18n._(label) : app.i18n._(label),
315 if (filterconfig.hasOwnProperty('options') && (filterconfig.options.hasOwnProperty('jsFilterType') || filterconfig.options.hasOwnProperty('jsFilterValueType'))) {
316 Tine.log.error('jsFilterType and jsFilterValueType are deprecated. Use jsConfig.<property> instead.');
318 // if js filter is defined in filterconfig.options, take this and return
319 if (filterconfig.hasOwnProperty('jsConfig')) {
320 Ext.apply(filter, filterconfig.jsConfig);
325 filter = this.filterMap(fieldTypeKey, fieldconfig, filter, filterconfig, appName, modelName, modelConfig);
327 var keys = filterconfig.filter.split('_'),
328 filterkey = keys[0].toLowerCase() + '.' + keys[2].toLowerCase();
329 filterkey = filterkey.replace(/filter/g, '');
331 if (Tine.widgets.grid.FilterToolbar.FILTERS[filterkey]) {
332 filter = {filtertype: filterkey};
333 } else { // set to null if no filter could be found
343 * if application starter should be used, here the js contents are (pre-)created
345 createStructure: function(initial) {
346 var start = new Date();
347 Ext.each(this.userApplications, function(app) {
349 var appName = app.name;
350 Tine.log.info('ApplicationStarter::createStructure for app ' + appName);
351 Ext.namespace('Tine.' + appName);
353 var models = Tine[appName].registry ? Tine[appName].registry.get('models') : null;
357 Tine[appName].isAuto = true;
358 var contentTypes = [];
360 // create translation
361 Tine[appName].i18n = new Locale.Gettext();
362 Tine[appName].i18n.textdomain(appName);
364 // iterate models of this app
365 Ext.iterate(models, function(modelName, modelConfig) {
366 var containerProperty = modelConfig.hasOwnProperty('containerProperty') ? modelConfig.containerProperty : null;
368 modelName = modelName.replace(/_/, '');
370 Ext.namespace('Tine.' + appName, 'Tine.' + appName + '.Model');
372 var modelArrayName = modelName + 'Array',
375 Tine.log.info('ApplicationStarter::createStructure for model ' + modelName);
377 if (modelConfig.createModule) {
378 contentTypes.push(modelConfig);
381 // iterate record fields
382 Ext.each(modelConfig.fieldKeys, function(key) {
383 // add field to model array
384 modelArray.push(this.getField(modelConfig.fields[key], key));
386 if (modelConfig.fields[key].label) {
387 // register grid renderer
390 var renderer = this.getGridRenderer(modelConfig.fields[key], key, appName, modelName);
396 if (Ext.isFunction(renderer)) {
397 if (! Tine.widgets.grid.RendererManager.has(appName, modelName, key)) {
398 Tine.widgets.grid.RendererManager.register(appName, modelName, key, renderer);
400 } else if (Ext.isObject(renderer)) {
401 if (! Tine.widgets.grid.RendererManager.has(appName, modelName, key)) {
402 Tine.widgets.grid.RendererManager.register(appName, modelName, key, renderer.render, null, renderer);
410 // iterate virtual record fields
411 if (modelConfig.virtualFields && modelConfig.virtualFields.length) {
412 Ext.each(modelConfig.virtualFields, function(field) {
413 modelArray.push(this.getField(field, field.key));
417 // collect the filterModel
418 var filterModel = [];
419 Ext.iterate(modelConfig.filterModel, function(key, filter) {
420 var f = this.getFilter(key, filter, modelConfig);
423 Tine.widgets.grid.FilterRegistry.register(appName, modelName, f);
427 // TODO: registry looses info if gridpanel resides in an editDialog
428 // delete filterModel as all filters are in the filter registry now
429 // delete modelConfig.filterModel;
431 Tine[appName].Model[modelArrayName] = modelArray;
434 if (! Tine[appName].Model.hasOwnProperty(modelName)) {
435 Tine[appName].Model[modelName] = Tine.Tinebase.data.Record.create(Tine[appName].Model[modelArrayName],
436 Ext.copyTo({modelConfiguration: modelConfig}, modelConfig,
437 'idProperty,defaultFilter,appName,modelName,recordName,recordsName,titleProperty,containerProperty,containerName,containersName,group,copyOmitFields')
439 Tine[appName].Model[modelName].getFilterModel = function() {
444 Ext.namespace('Tine.' + appName);
446 // create recordProxy
447 var recordProxyName = modelName.toLowerCase() + 'Backend';
448 if (! Tine[appName].hasOwnProperty(recordProxyName)) {
449 Tine[appName][recordProxyName] = new Tine.Tinebase.data.RecordProxy({
451 modelName: modelName,
452 recordClass: Tine[appName].Model[modelName]
455 // if default data is empty, it will be resolved to an array
456 if (Ext.isArray(modelConfig.defaultData)) {
457 modelConfig.defaultData = {};
460 // overwrite function
461 Tine[appName].Model[modelName].getDefaultData = function() {
463 var dd = Ext.decode(Ext.encode(modelConfig.defaultData));
466 // find container by selection or use defaultContainer by registry
467 if (modelConfig.containerProperty) {
468 if (! dd.hasOwnProperty(modelConfig.containerProperty)) {
469 var app = Tine.Tinebase.appMgr.get(appName),
470 registry = app.getRegistry(),
471 ctp = app.getMainScreen().getWestPanel().getContainerTreePanel();
473 var container = (ctp ? ctp.getDefaultContainer() : null) || (registry ? registry.get("default" + modelName + "Container") : null);
476 dd[modelConfig.containerProperty] = container;
483 // create filter panel
484 var filterPanelName = modelName + 'FilterPanel';
485 if (! Tine[appName].hasOwnProperty(filterPanelName)) {
486 Tine[appName][filterPanelName] = function(c) {
488 Tine[appName][filterPanelName].superclass.constructor.call(this);
490 Ext.extend(Tine[appName][filterPanelName], Tine.widgets.persistentfilter.PickerPanel);
492 // create container tree panel, if needed
493 if (containerProperty) {
494 var containerTreePanelName = modelName + 'TreePanel';
495 if (! Tine[appName].hasOwnProperty(containerTreePanelName)) {
496 Tine[appName][containerTreePanelName] = Ext.extend(Tine.widgets.container.TreePanel, {
497 filterMode: 'filterToolbar',
498 recordClass: Tine[appName].Model[modelName]
503 // create main screen
504 if(! Tine[appName].hasOwnProperty('MainScreen')) {
505 Tine[appName].MainScreen = Ext.extend(Tine.widgets.MainScreen, {
507 contentTypes: contentTypes,
508 activeContentType: modelName
512 // create editDialog openWindow function only if edit dialog exists
513 var editDialogName = modelName + 'EditDialog';
514 if (! Tine[appName].hasOwnProperty(editDialogName)) {
515 Tine[appName][editDialogName] = Ext.extend(Tine.widgets.dialog.EditDialog, {
516 displayNotes: Tine[appName].Model[modelName].hasField('notes')
521 if (Tine[appName].hasOwnProperty(editDialogName)) {
522 var edp = Tine[appName][editDialogName].prototype;
523 if (containerProperty) {
524 edp.showContainerSelector = true;
527 modelConfig: Ext.encode(modelConfig),
528 modelName: modelName,
529 recordClass: Tine[appName].Model[modelName],
530 recordProxy: Tine[appName][recordProxyName],
532 windowNamePrefix: modelName + 'EditWindow_'
534 if (! Ext.isFunction(Tine[appName][editDialogName].openWindow)) {
535 Tine[appName][editDialogName].openWindow = function (cfg) {
536 var id = cfg.recordId ? cfg.recordId : ( (cfg.record && cfg.record.id) ? cfg.record.id : 0 );
537 var window = Tine.WindowFactory.getWindow({
538 width: edp.windowWidth ? edp.windowWidth : 600,
539 height: edp.windowHeight ? edp.windowHeight : 230,
540 name: edp.windowNamePrefix + id,
541 contentPanelConstructor: 'Tine.' + appName + '.' + editDialogName,
542 contentPanelConstructorConfig: cfg
549 var gridPanelName = modelName + 'GridPanel',
551 modelConfig: modelConfig,
553 recordProxy: Tine[appName][recordProxyName],
554 recordClass: Tine[appName].Model[modelName]
557 if (! Tine[appName].hasOwnProperty(gridPanelName)) {
558 Tine[appName][gridPanelName] = Ext.extend(Tine.widgets.grid.GridPanel, gpConfig);
560 Ext.apply(Tine[appName][gridPanelName].prototype, gpConfig);
563 // add model to global add splitbutton if set
564 if (modelConfig.hasOwnProperty('splitButton') && modelConfig.splitButton == true) {
565 var iconCls = appName + modelName;
566 if (! Ext.util.CSS.getRule('.' + iconCls)) {
567 iconCls = 'ApplicationIconCls';
569 Ext.ux.ItemRegistry.registerItem('Tine.widgets.grid.GridPanel.addButton', {
570 text: Tine[appName].i18n._('New ' + modelName),
572 scope: Tine.Tinebase.appMgr.get(appName),
573 handler: (function() {
574 var ms = this.getMainScreen(),
575 cp = ms.getCenterPanel(modelName);
577 cp.onEditInNewWindow.call(cp, {});
578 }).createDelegate(Tine.Tinebase.appMgr.get(appName))
586 var stop = new Date();