ASSIGNED - # 1688: refactor javascript
authorPhilipp Schüle <p.schuele@metaways.de>
Tue, 29 Sep 2009 15:29:19 +0000 (15:29 +0000)
committerPhilipp Schüle <p.schuele@metaways.de>
Tue, 29 Sep 2009 15:29:19 +0000 (15:29 +0000)
http://www.tine20.org/bugtracker/view.php?id=1688

- created crm js files according to new app layout (TODO: fill the templates, adopt frontend json)

git-svn-id: file:///www/metaways/svn.tine20.org/repo/trunk@10653 8d11cdf3-9d2f-0410-bbb5-e098f187f06b

docs/ext-doc.xml
tine20/Crm/Frontend/Http.php
tine20/Crm/js/Crm.js
tine20/Crm/js/LeadEditDialog.js
tine20/Crm/js/LeadGridPanel.js [new file with mode: 0644]
tine20/Crm/js/Models.js [new file with mode: 0644]

index 004ac6a..1e938d7 100644 (file)
         <source src="../tine20/Calendar/js/PagingToolbar.js"/> \r
         <source src="../tine20/Calendar/js/ParallelEventsRegistry.js"/> \r
         <source src="../tine20/Calendar/js/RrulePanel.js"/> -->\r
-        <!--<source src="../tine20/Crm/js/Crm.js"/>\r
+        <source src="../tine20/Crm/js/Crm.js"/>\r
         <source src="../tine20/Crm/js/Models.js"/>\r
-        <source src="../tine20/Crm/js/LeadGridPanel.js"/>-->\r
+        <source src="../tine20/Crm/js/LeadGridPanel.js"/>\r
+        <source src="../tine20/Crm/js/LeadEditDialog.js"/>\r
         <source src="../tine20/ExampleApplication/js/ExampleRecordGridPanel.js"/>\r
         <source src="../tine20/ExampleApplication/js/ExampleRecordEditDialog.js"/>\r
         <source src="../tine20/Felamimail/js" match="*.js"/>\r
index be5c569..82ea7c5 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Crm
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Thomas Wadewitz <t.wadewitz@metaways.de>
- * @copyright   Copyright (c) 2007-2008 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)
  * @version     $Id: Http.php 5090 2008-10-24 10:30:05Z p.schuele@metaways.de $
  */
 
@@ -28,14 +28,16 @@ class Crm_Frontend_Http extends Tinebase_Frontend_Http_Abstract
     public function getJsFilesToInclude()
     {
         return array(
+            'Crm/js/Models.js',
             'Crm/js/Crm.js',
+            'Crm/js/LeadGridPanel.js',
             'Crm/js/LeadEditDialog.js',
-            'Crm/js/LeadState.js',
-            'Crm/js/LeadSource.js',
-            'Crm/js/LeadType.js',
-            'Crm/js/Product.js',
-            'Addressbook/js/SearchCombo.js',
-            'Crm/js/Contact.js',
+            //'Crm/js/LeadState.js',
+            //'Crm/js/LeadSource.js',
+            //'Crm/js/LeadType.js',
+            //'Crm/js/Product.js',
+            //'Addressbook/js/SearchCombo.js',
+            //'Crm/js/Contact.js',
         );
     }
     
index 1b31825..1f91a7d 100644 (file)
-/**\r
+/*\r
  * Tine 2.0\r
  * \r
  * @package     Crm\r
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3\r
- * @author      Thomas Wadewitz <t.wadewitz@metaways.de>\r
- *              redesign by Philipp Schuele <p.schuele@metaways.de>\r
- * @copyright   Copyright (c) 2007-2008 Metaways Infosystems GmbH (http://www.metaways.de)\r
+ * @author      Philipp Schuele <p.schuele@metaways.de>\r
+ * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)\r
  * @version     $Id$\r
  *\r
  */\r
  \r
 Ext.namespace('Tine.Crm');\r
 \r
-/*************************************** CRM TREE PANEL *****************************************/\r
-\r
-Tine.Crm = {\r
-       \r
-    /**\r
-     * entry point, required by tinebase\r
-     * creates and returnes app tree panel\r
-     */\r
-       getPanel: function()\r
-       {\r
-       var tree = new Tine.widgets.container.TreePanel({\r
-            id: 'crmTree',\r
-            iconCls: 'CrmIconCls',\r
-            title: 'CRM',\r
-            itemName: 'Leads',\r
-            containerName: 'Leads',\r
-            containersName: 'Leads',\r
-            appName: 'Crm',\r
-            border: false\r
-        });\r
-            \r
-        tree.on('click', function(node){               \r
-            Tine.Crm.Main.show(node);\r
-        }, this);\r
-            \r
-        tree.on('beforeexpand', function(panel) {\r
-            if(panel.getSelectionModel().getSelectedNode() === null) {\r
-                panel.expandPath('/root/all');\r
-                panel.selectPath('/root/all');\r
-            }\r
-            panel.fireEvent('click', panel.getSelectionModel().getSelectedNode());\r
-        }, this);\r
-        \r
-        return tree;\r
-       }\r
-};\r
-\r
-\r
-/*************************************** CRM GENERIC FNUCTIONS *****************************************/\r
 /**\r
- * split the relations array in contacts and tasks and switch related_record and relation objects\r
+ * @namespace Tine.Crm\r
+ * @class Tine.Crm.MainScreen\r
+ * @extends Tine.Tinebase.widgets.app.MainScreen\r
+ * MainScreen of the Crm Application <br>\r
  * \r
- * @param array _relations\r
- * @param boolean _splitAll if set, all different relation types are splitted into arrays\r
- * @return Object with arrays containing the different relation types\r
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3\r
+ * @author      Philipp Schuele <p.schuele@metaways.de>\r
+ * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)\r
+ * @version     $Id$\r
+ * @constructor\r
+ * Constructs mainscreen of the crm application\r
  */\r
-Tine.Crm.splitRelations = function(_relations, _splitAll) {\r
-    var result = null;\r
-            \r
-    if (_splitAll) {\r
-        result = {responsible: [], customer: [], partner: [], tasks: []};\r
-    } else {\r
-        result = {contacts: [], tasks: []};\r
-    }\r
-\r
-    if (!_relations) {\r
-        return result;\r
-    }\r
-    \r
-    for (var i=0; i < _relations.length; i++) {\r
-        var newLinkObject = _relations[i]['related_record'];\r
-        newLinkObject.relation = _relations[i];\r
-        newLinkObject.relation_type = _relations[i]['type'].toLowerCase();\r
+Tine.Crm.MainScreen = Tine.Tinebase.widgets.app.MainScreen;\r
 \r
-        if (!_splitAll && (newLinkObject.relation_type === 'responsible' \r
-          || newLinkObject.relation_type === 'customer' \r
-          || newLinkObject.relation_type === 'partner')) {\r
-            result.contacts.push(newLinkObject);\r
-        } else if (newLinkObject.relation_type === 'task') {                \r
-            result.tasks.push(newLinkObject);\r
-        } else {\r
-            switch(newLinkObject.relation_type) {\r
-                case 'responsible':\r
-                    result.responsible.push(newLinkObject);\r
-                    break;\r
-                case 'customer':\r
-                    result.customer.push(newLinkObject);\r
-                    break;\r
-                case 'partner':\r
-                    result.partner.push(newLinkObject);\r
-                    break;\r
-            }\r
-        }\r
-    }\r
+/**\r
+ * @namespace Tine.Crm\r
+ * @class Tine.Crm.TreePanel\r
+ * @extends Tine.widgets.container.TreePanel\r
+ * Left Crm Panel including Tree<br>\r
+ * \r
+ * TODO add d&d support to tree (change calendar)\r
+ * \r
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3\r
+ * @author      Philipp Schuele <p.schuele@metaways.de>\r
+ * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)\r
+ * @version     $Id$\r
+ */\r
+Tine.Crm.TreePanel = function(config) {\r
+    Ext.apply(this, config);\r
     \r
-    //console.log(result);\r
-       \r
-    return result;\r
+    this.id = 'CrmTreePanel';\r
+    this.recordClass = Tine.Crm.Model.Lead;\r
+    Tine.Crm.TreePanel.superclass.constructor.call(this);\r
 };\r
 \r
-/*************************************** CRM MAIN DIALOG *****************************************/\r
-\r
-Tine.Crm.Main = {\r
-\r
-       /**\r
-        * main screen actions\r
-        */\r
-    actions: {\r
-        addLead: null,\r
-        editLead: null,\r
-        deleteLead: null,\r
-        exportLead: null,\r
-        addTask: null\r
-    },\r
-\r
-    /**\r
-     * holds underlaying store\r
-     */\r
-    store: null,\r
-    \r
-       handlers: {\r
-               /**\r
-                * edit lead\r
-                */\r
-        handlerEdit: function(){\r
-            var _rowIndex = Ext.getCmp('gridCrm').getSelectionModel().getSelections();\r
-            var lead = _rowIndex[0];\r
-            Tine.Crm.LeadEditDialog.openWindow({\r
-                lead:lead,\r
-                listeners: {\r
-                    scope: this,\r
-                    'update': this.onRecordUpdate\r
-                }\r
-            });\r
-        },\r
-        \r
-        /**\r
-         * delete lead\r
-         */\r
-        handlerDelete: function(){\r
-            Ext.MessageBox.confirm('Confirm', Tine.Crm.Main.translation._('Are you sure you want to delete this lead?'), function(_button) {\r
-                if(_button == 'yes') {            \r
-                    var _rowIndexIds = Ext.getCmp('gridCrm').getSelectionModel().getSelections();            \r
-                    var toDelete_Ids = [];\r
-                \r
-                    for (var i = 0; i < _rowIndexIds.length; ++i) {\r
-                        toDelete_Ids.push(_rowIndexIds[i].data.id);\r
-                    }\r
-                    \r
-                    var leadIds = Ext.util.JSON.encode(toDelete_Ids);\r
-                \r
-                    Ext.Ajax.request({\r
-                        params: {\r
-                            method: 'Crm.deleteLeads',\r
-                            _leadIds: leadIds\r
-                        },\r
-                        text: Tine.Crm.Main.translation._('Deleting lead') + '...',\r
-                        success: function(_result, _request){\r
-                            Ext.getCmp('gridCrm').getStore().reload();\r
-                        },\r
-                        failure: function(result, request){\r
-                            Ext.MessageBox.alert('Failed', Tine.Crm.Main.translation._('Some error occured while trying to delete the lead.'));\r
-                        }\r
-                    });\r
-                }\r
-            });\r
-        },\r
-\r
-        /**\r
-         * onclick handler for exportBtn\r
-         */\r
-        exportLead: function(_button, _event) {\r
-            var _rowIndexIds = Ext.getCmp('gridCrm').getSelectionModel().getSelections();            \r
-            var toExportIds = [];\r
-        \r
-            for (var i = 0; i < _rowIndexIds.length; ++i) {\r
-                toExportIds.push(_rowIndexIds[i].data.id);\r
-            }\r
-            \r
-            var leadIds = Ext.util.JSON.encode(toExportIds);\r
-\r
-            Tine.Tinebase.common.openWindow('contactWindow', 'index.php?method=Crm.exportLead&_format=pdf&_leadIds=' + leadIds, 768, 1024);\r
-        },\r
-        \r
-        /**\r
-         * add task handler\r
-         * \r
-         * @todo    save the link via json request here?\r
-         */\r
-        handlerAddTask: function(){\r
-            Tine.Tasks.EditDialog.openWindow({\r
-                relatedApp: 'Crm'\r
-            });\r
-        }\r
-        \r
-    },        \r
-\r
-    /**\r
-     * showCrmToolbar function\r
-     */\r
-    showCrmToolbar: function() {\r
-        /**\r
-         * handlerToggleDetails function\r
-         */          \r
-        var handlerToggleDetails = function(toggle) {\r
-               //console.log(toggle.pressed);\r
-            var gridView         = Ext.getCmp('gridCrm').getView();\r
-            var gridColumnModel = Ext.getCmp('gridCrm').getColumnModel();\r
-            \r
-            if(toggle.pressed === true) {\r
-                \r
-                gridColumnModel.setRenderer(1, function(value, meta, record) {\r
-                    return '<b>' + value + '</b><br /><br />' + record.data.description;\r
-                } );                \r
-                \r
-                gridColumnModel.setRenderer(2, Tine.Crm.Main.renderer.detailedContact);\r
-                gridColumnModel.setRenderer(3, Tine.Crm.Main.renderer.detailedContact);\r
-                  \r
-               gridView.refresh();              \r
-               \r
-            } else {\r
-                \r
-                gridColumnModel.setRenderer(1, function(value, meta, record) {\r
-                    return value;\r
-                } );                \r
-                \r
-                gridColumnModel.setRenderer(2, Tine.Crm.Main.renderer.shortContact);\r
-                gridColumnModel.setRenderer(3, Tine.Crm.Main.renderer.shortContact);\r
-                  \r
-               gridView.refresh();                                \r
-            }\r
-        };\r
-        \r
-        // toolbar\r
-        var toolbar = new Ext.Toolbar({\r
-            id: 'crmToolbar',\r
-            split: false,\r
-            height: 26,\r
-            items: [\r
-                this.actions.addLead,\r
-                this.actions.editLead,\r
-                this.actions.deleteLead,\r
-                //actions.actionAddTask,\r
-                this.actions.exportLead,\r
-                '-',\r
-                new Ext.Button({\r
-                    text: this.translation._('Show details'),\r
-                    enableToggle: true,\r
-                    id: 'crmShowDetailsButton',\r
-                    iconCls: 'showDetailsAction',\r
-                    //cls: 'x-btn-icon',\r
-                    handler: handlerToggleDetails\r
-                }),                    \r
-                new Ext.Button({\r
-                    text: this.translation._('Show closed leads'),\r
-                    enableToggle: true,\r
-                    iconCls: 'showEndedLeadsAction',\r
-                    //cls: 'x-btn-icon',\r
-                    id: 'crmShowClosedLeadsButton',\r
-                    handler: function(toggle) {                        \r
-                        Ext.getCmp('gridCrm').getStore().reload();\r
-                    }                    \r
-                })\r
-            ]\r
-        });\r
-        \r
-        Tine.Tinebase.MainScreen.setActiveToolbar(toolbar);\r
-    },\r
-    \r
-    /**\r
-     * execuded when record updates\r
-     */\r
-    onRecordUpdate: function() {\r
-        this.reload();    \r
-    },\r
-    \r
-    /**\r
-     * creates the grid\r
-     * \r
-     * @todo addTask again?\r
-     * \r
-     */\r
-    initGridPanel: function() \r
-    { \r
-        //var dataStore = this.createDataStore();\r
-        // the filter toolbar\r
-        this.filterToolbar = new Tine.widgets.grid.FilterToolbar({\r
-            app: Tine.Tinebase.appMgr.get('Crm'),\r
-            id : 'crmLeadsFilterToolbar',\r
-            filterModels: [\r
-                {label: this.translation._('Lead'),        field: 'query',    operators: ['contains']},\r
-                {label: this.translation._('Lead name'),   field: 'lead_name' },\r
-                new Tine.Crm.LeadState.Filter({}),\r
-                {label: this.translation._('Probability'), field: 'probability', valueType: 'percentage'},\r
-                {label: this.translation._('Turnover'),    field: 'turnover', valueType: 'number', defaultOperator: 'greater'},\r
-                new Tine.widgets.tags.TagFilter({app: Tine.Tinebase.appMgr.get('Crm')})\r
-             ],\r
-             defaultFilter: 'query',\r
-             filters: []\r
-        });\r
-        \r
-        this.filterToolbar.on('change', function() {\r
-            this.store.load({});\r
-        }, this);\r
-        \r
-        var pagingToolbar = new Ext.PagingToolbar({ // inline paging toolbar\r
-            pageSize: 50,\r
-            store: this.store,\r
-            displayInfo: true,\r
-            displayMsg: this.translation._('Displaying leads {0} - {1} of {2}'),\r
-            emptyMsg: this.translation._('No leads found.')\r
-        });\r
-        pagingToolbar.on('beforechange', function() {\r
-            Ext.getCmp('gridCrm').getView().isPagingRefresh = true;\r
-        }, this);\r
-        \r
-        var ctxMenuGrid = new Ext.menu.Menu({\r
-               id:'ctxMenuGrid', \r
-               items: [\r
-                   this.actions.editLead,\r
-                   this.actions.deleteLead,\r
-                   this.actions.exportLead,\r
-                '-',\r
-                   this.actions.addLead\r
-                //this.actions.addTask\r
-               ]\r
-           });\r
-\r
-/*        var expander = new Ext.ux.grid.RowExpander({\r
-            enableCaching: false,\r
-            tpl : new Ext.Template(\r
-                '<b>Notes:</b> {description}</div></td>',\r
-                '<td class="x-grid3-col x-grid3-cell"><b>Activities:</b> </td>')\r
-        }); */\r
-        \r
-        var columnModel = new Ext.grid.ColumnModel([\r
-                       {resizable: true, header: this.translation._('Lead id'), id: 'id', dataIndex: 'id', width: 20, hidden: true},\r
-            {resizable: true, header: this.translation._('Lead name'), id: 'lead_name', dataIndex: 'lead_name', width: 200},\r
-            {resizable: true, header: this.translation._('Partner'), id: 'lead_partner', dataIndex: 'partner', width: 175, sortable: false, renderer: Tine.Crm.Main.renderer.shortContact},\r
-            {resizable: true, header: this.translation._('Customer'), id: 'lead_customer', dataIndex: 'customer', width: 175, sortable: false, renderer: Tine.Crm.Main.renderer.shortContact},\r
-            {resizable: true, header: this.translation._('Leadstate'), id: 'leadstate_id', dataIndex: 'leadstate_id', sortable: false, width: 100, renderer: Tine.Crm.LeadState.Renderer},\r
-            {resizable: true, header: this.translation._('Probability'), id: 'probability', dataIndex: 'probability', width: 50, renderer: Ext.util.Format.percentage },\r
-            {resizable: true, header: this.translation._('Turnover'), id: 'turnover', dataIndex: 'turnover', width: 100, renderer: Ext.util.Format.euMoney }\r
-        ]);\r
-        \r
-        columnModel.defaultSortable = true; // by default columns are sortable\r
-        \r
-        var rowSelectionModel = new Ext.grid.RowSelectionModel({multiSelect:true});\r
-        \r
-        rowSelectionModel.on('selectionchange', function(_selectionModel) {\r
-            // update toolbars\r
-            Tine.widgets.actionUpdater(_selectionModel, this.actions, 'container_id');\r
-        }, this);\r
-        \r
-        var gridPanel = new Ext.grid.GridPanel({\r
-            id: 'gridCrm',\r
-            store: this.store,\r
-            cm: columnModel,\r
-            tbar: pagingToolbar, \r
-            stripeRows: true,  \r
-            viewConfig: {\r
-                forceFit:true\r
-            },  \r
-            /* plugins: expander, */                    \r
-            autoSizeColumns: false,\r
-            selModel: rowSelectionModel,\r
-            enableColLock:false,\r
-            loadMask: true,\r
-            autoExpandColumn: 'lead_name',\r
-            border: false,\r
-            view: new Ext.grid.GridView({\r
-                autoFill: true,\r
-                forceFit:true,\r
-                ignoreAdd: true,\r
-                emptyText: this.translation._('No leads found.'),\r
-                onLoad: Ext.emptyFn,\r
-                listeners: {\r
-                    beforerefresh: function(v) {\r
-                        v.scrollTop = v.scroller.dom.scrollTop;\r
-                    },\r
-                    refresh: function(v) {\r
-                        // on paging-refreshes (prev/last...) we don't preserv the scroller state\r
-                        if (v.isPagingRefresh) {\r
-                            v.scrollToTop();\r
-                            v.isPagingRefresh = false;\r
-                        } else {\r
-                            v.scroller.dom.scrollTop = v.scrollTop;\r
-                        }\r
-                    }\r
-                }\r
-            })            \r
-        });\r
-        \r
-        gridPanel.on('rowcontextmenu', function(_grid, _rowIndex, _eventObject) {\r
-            _eventObject.stopEvent();\r
-            if(!_grid.getSelectionModel().isSelected(_rowIndex)) {\r
-                _grid.getSelectionModel().selectRow(_rowIndex);\r
-                //this.actions.actionDelete.setDisabled(false);\r
-            }\r
-            ctxMenuGrid.showAt(_eventObject.getXY());\r
-        });\r
-        \r
-        gridPanel.on('rowdblclick', function(_gridPanel, _rowIndexPar, ePar) {\r
-            var record = _gridPanel.getStore().getAt(_rowIndexPar);\r
-            Tine.Crm.LeadEditDialog.openWindow({\r
-                lead: record,\r
-                listeners: {\r
-                    scope: this,\r
-                    'update': this.onRecordUpdate\r
-                }\r
-            });           \r
-        }, this);\r
-       \r
-        this.gridPanel = new Ext.Panel({\r
-            layout: 'border',\r
-            border: false,\r
-            items: [{\r
-                region: 'center',\r
-                border: false,\r
-                layout: 'fit',              \r
-                tbar: this.filterToolbar,\r
-                items: gridPanel\r
-            }]\r
-        });\r
-        //Tine.Tinebase.MainScreen.setActiveContentPanel(gridPanel);\r
-    },    \r
-      \r
-    /**\r
-     * initComponent\r
-     */\r
-    initComponent: function()\r
-    {\r
-       // set translation\r
-        this.translation = new Locale.Gettext();\r
-        this.translation.textdomain('Crm');\r
-    \r
-        // set actions\r
-        this.actions.addLead = new Ext.Action({\r
-            requiredGrant: 'addGrant',\r
-            text: this.translation._('Add lead'),\r
-            tooltip: this.translation._('Add new lead'),\r
-            iconCls: 'actionAdd',\r
-            scope: this,\r
-            handler: function(){\r
-                Tine.Crm.LeadEditDialog.openWindow({\r
-                    listeners: {\r
-                        scope: this,\r
-                        'update': this.onRecordUpdate\r
-                    }\r
-                });                \r
-            }   \r
-        });\r
-        \r
-        this.actions.editLead = new Ext.Action({\r
-            requiredGrant: 'readGrant',\r
-            text: this.translation._('Edit lead'),\r
-            tooltip: this.translation._('Edit selected lead'),\r
-            disabled: true,\r
-            handler: this.handlers.handlerEdit,\r
-            iconCls: 'actionEdit',\r
-            scope: this\r
-        });\r
-        \r
-        this.actions.deleteLead = new Ext.Action({\r
-            requiredGrant: 'deleteGrant',\r
-            allowMultiple: true,\r
-            singularText: 'Delete lead',\r
-            pluralText: 'Delete leads',\r
-            translationObject: this.translation,\r
-            text: this.translation.ngettext('Delete lead', 'Delete leads', 1),\r
-            tooltip: this.translation._('Delete selected leads'),\r
-            disabled: true,\r
-            handler: this.handlers.handlerDelete,\r
-            iconCls: 'actionDelete',\r
-            scope: this\r
-        });\r
-        \r
-        this.actions.exportLead = new Ext.Action({\r
-            requiredGrant: 'readGrant',\r
-            allowMultiple: true,\r
-            text: this.translation._('Export as PDF'),\r
-            tooltip: this.translation._('Export selected lead as PDF'),\r
-            disabled: true,\r
-            handler: this.handlers.exportLead,\r
-            iconCls: 'action_exportAsPdf',\r
-            scope: this\r
-        });\r
-        \r
-        this.actions.addTask = new Ext.Action({\r
-            requiredGrant: 'readGrant',\r
-            text: this.translation._('Add task'),\r
-            tooltip: this.translation._('Add task for selected lead'),\r
-            handler: this.handlers.handlerAddTask,\r
-            iconCls: 'actionAddTask',\r
-            disabled: true,\r
-            scope: this\r
-        });\r
-        \r
-        // init grid store\r
-        this.initStore();\r
-        this.initGridPanel();\r
-    },\r
-\r
-    /**\r
-     * init the leads json grid store\r
-     */\r
-    initStore: function(){\r
-        this.store = new Ext.data.JsonStore({\r
-            id: 'id',\r
-            root: 'results',\r
-            totalProperty: 'totalcount',\r
-            fields: Tine.Crm.Model.Lead,\r
-            remoteSort: true,\r
-            baseParams: {\r
-                method: 'Crm.searchLeads'\r
-            },\r
-            sortInfo: {\r
-                field: 'lead_name',\r
-                dir: 'ASC'\r
-            }\r
-        });\r
-        \r
-        // register store\r
-        Ext.StoreMgr.add('LeadsGridStore', this.store);\r
-        \r
-        // prepare filter\r
-        this.store.on('beforeload', function(store, options){\r
-            if (!options.params) {\r
-                options.params = {};\r
-            }\r
-            \r
-            // fix nasty paging tb\r
-            Ext.applyIf(options.params, {\r
-                start: 0,\r
-                limit: 50,\r
-                sort: 'lead_name',\r
-                dir: 'ASC'\r
-            });\r
-            \r
-            // move paging to own object\r
-            var paging = {\r
-                sort:  options.params.sort,\r
-                dir:   options.params.dir,\r
-                start: options.params.start,\r
-                limit: options.params.limit\r
-            };\r
-            \r
-            \r
-            var filterToolbar = Ext.getCmp('crmLeadsFilterToolbar');\r
-            var filter = filterToolbar ? filterToolbar.getValue() : [];\r
-            \r
-            // add container to filter\r
-            var nodeAttributes = Ext.getCmp('crmTree').getSelectionModel().getSelectedNode().attributes || {};\r
-            filter.push(\r
-                {field: 'containerType', operator: 'equals', value: nodeAttributes.containerType ? nodeAttributes.containerType : 'all' },\r
-                {field: 'container',     operator: 'equals', value: nodeAttributes.container ? nodeAttributes.container.id : null       },\r
-                {field: 'owner',         operator: 'equals', value: nodeAttributes.owner ? nodeAttributes.owner.accountId : null        }\r
-            );\r
-            \r
-            // toolbar\r
-            if (Ext.getCmp('crmShowClosedLeadsButton') && Ext.getCmp('crmShowClosedLeadsButton').pressed) {\r
-                filter.push({field: 'showClosed', operator: 'equals', value: true});\r
-            }\r
-            \r
-            options.params.paging = Ext.util.JSON.encode(paging);\r
-            options.params.filter = Ext.util.JSON.encode(filter);\r
-        }, this);\r
-        \r
-        this.store.on('datachanged', function(store) {\r
-            // get partner & customers from relations\r
-            store.each(function(record){\r
-                var relations = Tine.Crm.splitRelations(record.data.relations, true);\r
-                record.data.customer = relations.customer;\r
-                record.data.partner = relations.partner;\r
-            });        \r
-        });\r
-    },\r
-    \r
-    /**\r
-     * show\r
-     */\r
-    show: function(_node) \r
-    {          \r
-        var currentToolbar = Tine.Tinebase.MainScreen.getActiveToolbar();\r
-        if (currentToolbar === false || currentToolbar.id != 'crmToolbar') {\r
-            if (!this.gridPanel) {\r
-                this.initComponent();\r
-            }\r
-            Tine.Tinebase.MainScreen.setActiveContentPanel(this.gridPanel, true);\r
-            this.store.load({});\r
-            \r
-            this.showCrmToolbar();\r
-            this.updateMainToolbar();\r
-        } else {\r
-            // note: if node is clicked, it is not selected!\r
-            _node.getOwnerTree().selectPath(_node.getPath());\r
-               this.store.load({});\r
-        }\r
-        \r
-    },    \r
-        \r
-    /**\r
-     * updateMainToolbar\r
-     */\r
-    updateMainToolbar : function() \r
-    {\r
-        var menu = Ext.menu.MenuMgr.get('Tinebase_System_AdminMenu');\r
-        menu.removeAll();\r
-        menu.add(\r
-            // @todo    replace with standard popup windows\r
-            {text: this.translation._('Lead states'), handler: Tine.Crm.LeadState.EditDialog},\r
-            {text: this.translation._('Lead sources'), handler: Tine.Crm.LeadSource.EditDialog},\r
-            {text: this.translation._('Lead types'), handler: Tine.Crm.LeadType.EditDialog},\r
-            {text: this.translation._('Products'), handler: Tine.Crm.Product.EditDialog}\r
-        );\r
-\r
-       var adminButton = Ext.getCmp('tineMenu').items.get('Tinebase_System_AdminButton');\r
-        adminButton.setIconClass('crmThumbnailApplication');\r
-        if(Tine.Tinebase.common.hasRight('admin', 'Crm')) {\r
-            adminButton.setDisabled(false);\r
-        } else {\r
-               adminButton.setDisabled(true);\r
-        }\r
-    },\r
-\r
-    /**\r
-     * reload\r
-     */\r
-    reload: function() \r
-    {\r
-        if(Ext.ComponentMgr.all.containsKey('gridCrm')) {\r
-            setTimeout ("Ext.getCmp('gridCrm').getStore().reload()", 200);\r
-        }\r
-    },\r
-    \r
-    /**\r
-     * renderer\r
-     */\r
-    renderer: \r
-    {\r
-        shortContact: function(_data, _cell, _record, _rowIndex, _columnIndex, _store) {               \r
-            if( Ext.isArray(_data) && _data.length > 0 ) {\r
-                var org = ( _data[0].org_name !== null ) ? _data[0].org_name : '';\r
-                return '<b>' + Ext.util.Format.htmlEncode(org) + '</b><br />' + Ext.util.Format.htmlEncode(_data[0].n_fileas);\r
-            }\r
-        },          \r
-        \r
-        detailedContact: function(_data, _cell, _record, _rowIndex, _columnIndex, _store) {\r
-            if(typeof(_data) == 'object' && !Ext.isEmpty(_data)) {\r
-                var contactDetails = '', style = '';\r
-                for(var i=0; i < _data.length; i++){\r
-                    var org_name           = Ext.isEmpty(_data[i].org_name) === false ? _data[i].org_name : ' ';\r
-                    var n_fileas           = Ext.isEmpty(_data[i].n_fileas) === false ? _data[i].n_fileas : ' ';\r
-                    var adr_one_street     = Ext.isEmpty(_data[i].adr_one_street) === false ? _data[i].adr_one_street : ' ';\r
-                    var adr_one_postalcode = Ext.isEmpty(_data[i].adr_one_postalcode) === false ? _data[i].adr_one_postalcode : ' ';\r
-                    var adr_one_locality   = Ext.isEmpty(_data[i].adr_one_locality) === false ? _data[i].adr_one_locality : ' ';\r
-                    var tel_work           = Ext.isEmpty(_data[i].tel_work) === false ? _data[i].tel_work : ' ';\r
-                    var tel_cell           = Ext.isEmpty(_data[i].tel_cell) === false ? _data[i].tel_cell : ' ';\r
-                    \r
-                    if(i > 0) {\r
-                        style = 'borderTop';\r
-                    }\r
-                    \r
-                    contactDetails = contactDetails + '<table width="100%" height="100%" class="' + style + '">' +\r
-                                         '<tr><td colspan="2">' + Ext.util.Format.htmlEncode(org_name) + '</td></tr>' +\r
-                                         '<tr><td colspan="2"><b>' + Ext.util.Format.htmlEncode(n_fileas) + '</b></td></tr>' +\r
-                                         '<tr><td colspan="2">' + Ext.util.Format.htmlEncode(adr_one_street) + '</td></tr>' +\r
-                                         '<tr><td colspan="2">' + Ext.util.Format.htmlEncode(adr_one_postalcode) + ' ' + adr_one_locality + '</td></tr>' +\r
-                                         '<tr><td width="50%">' + Tine.Crm.Main.translation._('Phone') + ': </td><td width="50%">' + Ext.util.Format.htmlEncode(tel_work) + '</td></tr>' +\r
-                                         '<tr><td width="50%">' + Tine.Crm.Main.translation._('Cellphone') + ': </td><td width="50%">' + Ext.util.Format.htmlEncode(tel_cell) + '</td></tr>' +\r
-                                         '</table> <br />';\r
-                }\r
-                \r
-                return contactDetails;\r
-            }\r
-        }\r
-    }    \r
-}; // end of application (CRM MAIN DIALOG)\r
-  \r
-/*************************************** LEAD EDIT DIALOG ****************************************/\r
-\r
-Tine.Crm.LeadEditDialog = Ext.extend(Tine.widgets.dialog.EditRecord, {\r
-       \r
-    /**\r
-     * @cfg {Tine.Crm.Model.Lead} lead to edit\r
-     */\r
-    lead: null,\r
-    /**\r
-     * @cfg {Object} container\r
-     */\r
-    forceContainer: null,\r
-    \r
-    /**\r
-     * @private\r
-     */\r
-    windowNamePrefix: 'LeadEditWindow_',\r
-    labelAlign: 'top',\r
-    appName: 'Crm',\r
-    containerProperty: 'container_id',\r
-    showContainerSelector: true,\r
-    \r
-       /**\r
-        * define actions\r
-        */\r
-       actions: {\r
-        addContact: null,\r
-        editContact: null,\r
-        unlinkContact: null,\r
-        addTask: null,         \r
-        editTask: null,\r
-        linkTask: null,\r
-        unlinkTask: null,\r
-        unlinkProduct: null,\r
-        exportLead: null,\r
-        changeContactType: null\r
-       },\r
-       \r
-    /**\r
-     * event handlers\r
-     */\r
-    handlers: {   \r
-       \r
-        /**\r
-         * unlink action handler for linked objects\r
-         * \r
-         * remove selected objects from store\r
-         * needs _button.gridId and _button.storeName\r
-         */\r
-        unlink: function(_button, _event) {                                                    \r
-            var selectedRows = Ext.getCmp(_button.gridId).getSelectionModel().getSelections();\r
-            var store = Ext.StoreMgr.lookup(_button.storeName);\r
-            for (var i = 0; i < selectedRows.length; ++i) {\r
-                store.remove(selectedRows[i]);\r
-            }           \r
-        },\r
-\r
-        /**\r
-         * onclick handler for addContact\r
-         */\r
-        addContact: function(_button, _event) {\r
-            var contactWindow = Tine.Addressbook.ContactEditDialog.openWindow({\r
-                listeners: {\r
-                    scope: this,\r
-                    'update': this.onContactUpdate\r
-                }\r
-            });                \r
-        },\r
-            \r
-        /**\r
-         * onclick handler for editContact\r
-         */\r
-        editContact: function(_button, _event) {\r
-            var selectedRows = Ext.getCmp('crmGridContacts').getSelectionModel().getSelections();\r
-            \r
-            var contactWindow = Tine.Addressbook.ContactEditDialog.openWindow({\r
-                record: selectedRows[0],\r
-                listeners: {\r
-                    scope: this,\r
-                    'update': this.onContactUpdate\r
-                }\r
-            });         \r
-        },\r
-\r
-        /**\r
-         * onclick handler for changeContactType\r
-         */\r
-        changeContactType: function(_button, _event) {         \r
-               var selectedRows = Ext.getCmp('crmGridContacts').getSelectionModel().getSelections();\r
-               var store = Ext.StoreMgr.lookup('ContactsStore');\r
-               \r
-            for (var i = 0; i < selectedRows.length; ++i) {\r
-                selectedRows[i].data.relation_type = _button.contactType;                \r
-            }                          \r
-            \r
-            store.fireEvent('dataChanged', store);\r
-        },\r
-        \r
-        /**\r
-         * onclick handler for add task\r
-         * \r
-         */\r
-        addTask: function(_button, _event) {\r
-            var taskPopup = Tine.Tasks.EditDialog.openWindow({\r
-                relatedApp: 'Crm',\r
-                listeners: {\r
-                    scope: this,\r
-                    update: this.onTaskUpdate\r
-                }\r
-            });\r
-            //taskPopup.on('update', this.onTaskUpdate, this);\r
-        },\r
-            \r
-        /**\r
-         * onclick handler for editBtn\r
-         * \r
-         */\r
-        editTask: function(_button, _event) {\r
-            var selectedRows = Ext.getCmp('crmGridTasks').getSelectionModel().getSelections();\r
-            var selectedTask = selectedRows[0];\r
-            \r
-            var taskPopup = Tine.Tasks.EditDialog.openWindow({\r
-                record: selectedTask,\r
-                listeners: {\r
-                    scope: this,\r
-                    update: this.onTaskUpdate\r
-                }\r
-            });\r
-            //taskPopup.on('update', this.onTaskUpdate, this);\r
-        },\r
-        \r
-        /**\r
-         * linkTask\r
-         * \r
-         * link an existing task, open 'object' picker dialog\r
-         * @todo implement\r
-         */\r
-        linkTask: function(_button, _event) {\r
-            \r
-        },\r
-\r
-        /**\r
-         * onclick handler for exportBtn\r
-         */\r
-        exportLead: function(_button, _event) {         \r
-               \r
-               var leadId = Ext.util.JSON.encode([_button.leadId]);\r
-               \r
-            Tine.Tinebase.common.openWindow('exportWindow', 'index.php?method=Crm.exportLead&_format=pdf&_leadIds=' + leadId, 768, 1024);\r
-        }\r
-    },\r
-    \r
-    /**\r
-     * handler apply changes\r
-     */\r
-    handlerApplyChanges: function(_button, _event, _closeWindow) {\r
-        var leadForm = this.getForm();\r
-        var lead = this.lead;\r
-        \r
-        if(leadForm.isValid()) {  \r
-            Ext.MessageBox.wait(this.translation._('Please wait'), this.translation._('Saving lead') + '...');                \r
-            leadForm.updateRecord(lead);\r
-            \r
-            // get linked stuff\r
-            lead = this.getAdditionalData(lead);\r
-\r
-            Ext.Ajax.request({\r
-                scope: this,\r
-                params: {\r
-                    method: 'Crm.saveLead', \r
-                    lead: Ext.util.JSON.encode(lead.data)\r
-                },\r
-                success: function(response) {\r
-                    // 2009-08-03 commented out cause it makes problems with ext style windows\r
-                    // this should be gone when switching to EditDialog\r
-                    //this.onRecordLoad(response);\r
-                    \r
-                    this.fireEvent('update', this.lead);\r
-                    Ext.MessageBox.hide();\r
-                    \r
-                    if (_closeWindow === true) {\r
-                        this.purgeListeners();\r
-                        this.window.close();\r
-                    }\r
-                },\r
-                failure: function ( result, request) { \r
-                    Ext.MessageBox.alert(this.translation._('Failed'), this.translation._('Could not save lead.')); \r
-                }\r
-            });\r
-        } else {\r
-            Ext.MessageBox.alert('Errors', this.translation._('Please fix the errors noted.'));\r
-        }\r
-    },\r
-    \r
-    /**\r
-     * update event handler for related contacts\r
-     */\r
-    onContactUpdate: function(contact) {\r
-        var response = {\r
-            responseText: contact\r
-        };\r
-        contact = Tine.Addressbook.contactBackend.recordReader(response);\r
-        \r
-        var storeContacts = Ext.StoreMgr.lookup('ContactsStore');\r
-\r
-        var myContact = storeContacts.getById(contact.id);\r
-        if (myContact) {\r
-            myContact.beginEdit();\r
-            for (var p in contact.data) { \r
-                myContact.set(p, contact.get(p));\r
-            }\r
-            myContact.endEdit();\r
-        } else {\r
-            contact.data.relation_type = 'customer';\r
-            storeContacts.add(contact);\r
-        }        \r
-    },\r
-    \r
-    /**\r
-     * update event handler for related tasks\r
-     */\r
-    onTaskUpdate: function(task) {\r
-        var response = {\r
-            responseText: task\r
-        };\r
-        task = Tine.Tasks.JsonBackend.recordReader(response);\r
-        \r
-        var storeTasks = Ext.StoreMgr.lookup('TasksStore');\r
-        var myTask = storeTasks.getById(task.id);\r
-        \r
-        if (myTask) { \r
-            // copy values from edited task\r
-            myTask.beginEdit();\r
-            for (var p in task.data) { \r
-                myTask.set(p, task.get(p));\r
-            }\r
-            myTask.endEdit();\r
-            \r
-        } else {\r
-            task.data.relation_type = 'task';\r
-            storeTasks.add(task);        \r
-        }\r
-    },\r
-    \r
-    /**\r
-     * getRelationData\r
-     * get the record relation data (switch relation and related record)\r
-     * \r
-     * @param   Object record with relation data\r
-     * @return  Object relation with record data\r
-     */\r
-    getRelationData: function(record) {\r
-        var relation = null; \r
-        \r
-        if (record.data.relation) {\r
-            relation = record.data.relation;\r
-        } else {\r
-               // empty relation for new record\r
-            relation = {};\r
-        }\r
-\r
-        // set the relation type\r
-        if (!relation.type) {\r
-            relation.type = record.data.relation_type.toUpperCase();\r
-        }\r
-        \r
-        // do not do recursion!\r
-        delete record.data.relation;\r
-        //delete record.data.relation_type;\r
-        \r
-        // save record data        \r
-        relation.related_record = record.data;\r
-        \r
-        return relation;\r
-    },\r
-\r
-       /**\r
-     * getAdditionalData\r
-     * collects additional data (start/end dates, linked contacts, ...)\r
-     * \r
-     * @param   Tine.Crm.Model.Lead lead\r
-     * @return  Tine.Crm.Model.Lead lead\r
-     */\r
-    getAdditionalData: function(lead) {\r
-        // collect data of relations\r
-       var relations = [];\r
-       \r
-       // contacts\r
-        var storeContacts = Ext.StoreMgr.lookup('ContactsStore');\r
-        //console.log(storeContacts);\r
-        storeContacts.each(function(record) {                     \r
-            relations.push(this.getRelationData(record));\r
-        }, this);\r
-        \r
-        // tasks\r
-        var storeTasks = Ext.StoreMgr.lookup('TasksStore');    \r
-        //console.log(storeTasks);\r
-        storeTasks.each(function(record) {\r
-            relations.push(this.getRelationData(record));\r
-        }, this);\r
-        \r
-        //console.log(relations);        \r
-        lead.data.relations = relations;\r
-        \r
-        // add products\r
-        var linksProducts = [];\r
-        var storeProducts = Ext.StoreMgr.lookup('ProductsStore');       \r
-\r
-        storeProducts.each(function(record) {\r
-            linksProducts.push(record.data);\r
-        });\r
-        \r
-        lead.data.products = linksProducts;\r
-        \r
-        return lead;\r
-    },\r
-        \r
-    /**\r
-     * getContactsGrid\r
-     * get the grids for contacts\r
-     * \r
-     * @return  grid object\r
-     */\r
-    getContactsGrid: function() {\r
-       \r
-        var columnModel = new Ext.grid.ColumnModel([\r
-            {id:'id', header: "id", dataIndex: 'id', width: 25, sortable: true, hidden: true },\r
-            {id:'n_fileas', header: this.translation._('Name'), dataIndex: 'n_fileas', width: 100, sortable: true, renderer: \r
-                function(val, meta, record) {\r
-                    var org_name           = Ext.isEmpty(record.data.org_name) === false ? record.data.org_name : ' ';\r
-                    var n_fileas           = Ext.isEmpty(record.data.n_fileas) === false ? record.data.n_fileas : ' ';                            \r
-                    var formated_return = '<b>' + Ext.util.Format.htmlEncode(n_fileas) + '</b><br />' + Ext.util.Format.htmlEncode(org_name);\r
-                    \r
-                    return formated_return;\r
-                }\r
-            },\r
-            {id:'contact_one', header: this.translation._("Address"), dataIndex: 'adr_one_locality', width: 160, sortable: false, renderer: function(val, meta, record) {\r
-                    var adr_one_street     = Ext.isEmpty(record.data.adr_one_street) === false ? record.data.adr_one_street : ' ';\r
-                    var adr_one_postalcode = Ext.isEmpty(record.data.adr_one_postalcode) === false ? record.data.adr_one_postalcode : ' ';\r
-                    var adr_one_locality   = Ext.isEmpty(record.data.adr_one_locality) === false ? record.data.adr_one_locality : ' ';\r
-                    var formated_return =  \r
-                        Ext.util.Format.htmlEncode(adr_one_street) + '<br />' + \r
-                        Ext.util.Format.htmlEncode(adr_one_postalcode) + ' ' + Ext.util.Format.htmlEncode(adr_one_locality);\r
-                \r
-                    return formated_return;\r
-                }\r
-            },\r
-            {id:'tel_work', header: this.translation._("Contactdata"), dataIndex: 'tel_work', width: 160, sortable: false, renderer: function(val, meta, record) {\r
-                    var translation = new Locale.Gettext();\r
-                    translation.textdomain('Crm');\r
-                    var tel_work           = Ext.isEmpty(record.data.tel_work) === false ? translation._('Phone') + ': ' + record.data.tel_work : ' ';\r
-                    var tel_cell           = Ext.isEmpty(record.data.tel_cell) === false ? translation._('Cellphone') + ': ' + record.data.tel_cell : ' ';          \r
-                    var formated_return = tel_work + '<br/>' + tel_cell + '<br/>';\r
-                    return formated_return;\r
-                }                        \r
-            },    \r
-            {\r
-                id:'relation_type', \r
-                header: this.translation._("Type"), \r
-                dataIndex: 'relation_type', \r
-                width: 75, \r
-                sortable: true,\r
-                renderer: Tine.Crm.contactType.Renderer,\r
-                editor: new Tine.Crm.contactType.ComboBox({\r
-                    autoExpand: true,\r
-                    blurOnSelect: true,\r
-                    listClass: 'x-combo-list-small'\r
-                })\r
-            }\r
-        ]);\r
-        \r
-        var rowSelectionModel = new Ext.grid.RowSelectionModel({multiSelect:true});\r
-        rowSelectionModel.on('selectionchange', function(_selectionModel) {\r
-            var rowCount = _selectionModel.getCount();\r
-            if (this.lead && this.lead.get('container_id') && this.lead.get('container_id').account_grants) {\r
-                this.actions.unlinkContact.setDisabled(!this.lead.get('container_id').account_grants.editGrant || rowCount != 1);\r
-            }\r
-            this.actions.editContact.setDisabled(rowCount != 1);\r
-        }, this);\r
-        \r
-        var bbarItems = [                \r
-            this.actions.addContact,\r
-            this.actions.unlinkContact\r
-        ]; \r
-                \r
-        // get store and create grid\r
-        var gridStore = Ext.StoreMgr.lookup('ContactsStore');  \r
-        var grid = new Ext.grid.EditorGridPanel({\r
-            id: 'crmGridContacts',\r
-            //title: this.translation._('Contacts'),\r
-            cm: columnModel,\r
-            store: gridStore,\r
-            selModel: rowSelectionModel,\r
-            autoExpandColumn: 'n_fileas',\r
-            bbar: bbarItems,\r
-            baseCls: 'contact-grid',\r
-            tbar: new Ext.Panel({\r
-                layout: 'fit',\r
-                width: '100%',\r
-                //baseCls: 'x-tab-strip x-tab-strip-top',\r
-                items: [\r
-                    // @todo perhaps we could add an icon/button (i.e. edit-find.png) here\r
-                    new Tine.Crm.Contact.ComboBox({\r
-                        emptyText: this.translation._('Search for Contacts to add ...')\r
-                    })\r
-                ]\r
-            }),\r
-            clicksToEdit: 'auto',\r
-            height: 210\r
-        });\r
-\r
-        grid.on('rowdblclick', function(_gridPanel, _rowIndexPar, ePar) {\r
-            var record = _gridPanel.getStore().getAt(_rowIndexPar);\r
-            Tine.Addressbook.ContactEditDialog.openWindow({\r
-                record: record,\r
-                listeners: {\r
-                    scope: this,\r
-                    'update': this.onContactUpdate\r
-                }\r
-            });\r
-        }, this);\r
-            \r
-        return grid;       \r
-    },\r
-    \r
-    /**\r
-     * getTasksGrid\r
-     * get the grids for tasks\r
-     * \r
-     * @return  grid object\r
-     */\r
-    getTasksGrid: function() {\r
-\r
-       var columnModel = [{\r
-            id: 'summary',\r
-            header: this.translation._("Summary"),\r
-            width: 100,\r
-            sortable: true,\r
-            dataIndex: 'summary',\r
-            //editor: new Ext.form.TextField({\r
-            //  allowBlank: false\r
-            //}),\r
-            quickaddField: new Ext.form.TextField({\r
-                emptyText: this.translation._('Add a task...')\r
-            })\r
-        }, {\r
-            id: 'due',\r
-            header: this.translation._("Due Date"),\r
-            width: 55,\r
-            sortable: true,\r
-            dataIndex: 'due',\r
-            renderer: Tine.Tinebase.common.dateRenderer,\r
-            editor: new Ext.ux.form.ClearableDateField({\r
-                //format : 'd.m.Y'\r
-            }),\r
-            quickaddField: new Ext.ux.form.ClearableDateField({\r
-                //value: new Date(),\r
-                //format : "d.m.Y"\r
-            })\r
-        }, {\r
-            id: 'priority',\r
-            header: this.translation._("Priority"),\r
-            width: 45,\r
-            sortable: true,\r
-            dataIndex: 'priority',\r
-            renderer: Tine.widgets.Priority.renderer,\r
-            editor: new Tine.widgets.Priority.Combo({\r
-                allowBlank: false,\r
-                autoExpand: true,\r
-                blurOnSelect: true\r
-            }),\r
-            quickaddField: new Tine.widgets.Priority.Combo({\r
-                autoExpand: true\r
-            })\r
-        }, {\r
-            id: 'percent',\r
-            header: this.translation._("Percent"),\r
-            width: 50,\r
-            sortable: true,\r
-            dataIndex: 'percent',\r
-            renderer: Ext.ux.PercentRenderer,\r
-            editor: new Ext.ux.PercentCombo({\r
-                autoExpand: true,\r
-                blurOnSelect: true\r
-            }),\r
-            quickaddField: new Ext.ux.PercentCombo({\r
-                autoExpand: true\r
-            })\r
-        }, {\r
-            id: 'status_id',\r
-            header: this.translation._("Status"),\r
-            width: 45,\r
-            sortable: true,\r
-            dataIndex: 'status_id',\r
-            renderer: Tine.Tasks.status.getStatusIcon,\r
-            editor: new Tine.Tasks.status.ComboBox({\r
-                autoExpand: true,\r
-                blurOnSelect: true,\r
-                listClass: 'x-combo-list-small'\r
-            }),\r
-            quickaddField: new Tine.Tasks.status.ComboBox({\r
-                autoExpand: true\r
-            })\r
-        }];\r
-        \r
-        var rowSelectionModel = new Ext.grid.RowSelectionModel({multiSelect:true});\r
-        rowSelectionModel.on('selectionchange', function(_selectionModel) {\r
-            var rowCount = _selectionModel.getCount();\r
-            if (this.lead && this.lead.get('container_id') && this.lead.get('container_id').account_grants) {\r
-                this.actions.unlinkTask.setDisabled(!this.lead.get('container_id').account_grants.editGrant || rowCount != 1);\r
-            }\r
-            this.actions.editTask.setDisabled(rowCount != 1);\r
-        }, this);\r
-\r
-        var bbarItems = [\r
-            this.actions.addTask,\r
-            this.actions.unlinkTask\r
-        ]; \r
-\r
-        // get store and create grid\r
-        var gridStore = Ext.StoreMgr.lookup('TasksStore');  \r
-           \r
-        var grid = new Ext.ux.grid.QuickaddGridPanel({\r
-            title: this.translation._('Tasks'),\r
-            id: 'crmGridTasks',\r
-            border: false,\r
-            store: gridStore,\r
-            clicksToEdit: 'auto',\r
-            bbar: bbarItems,\r
-            enableColumnHide:false,\r
-            enableColumnMove:false,\r
-            sm: rowSelectionModel,\r
-            loadMask: true,\r
-            quickaddMandatory: 'summary',\r
-            autoExpandColumn: 'summary',\r
-            columns: columnModel,\r
-            view: new Ext.grid.GridView({\r
-                autoFill: true,\r
-                forceFit:true,\r
-                ignoreAdd: true,\r
-                emptyText: this.translation._('No Tasks to display'),\r
-                onLoad: Ext.emptyFn,\r
-                listeners: {\r
-                    beforerefresh: function(v) {\r
-                        v.scrollTop = v.scroller.dom.scrollTop;\r
-                    },\r
-                    refresh: function(v) {\r
-                        v.scroller.dom.scrollTop = v.scrollTop;\r
-                    }\r
-                }\r
-            })\r
-        });\r
-            \r
-        grid.on('newentry', function(taskData){\r
-\r
-            // add new task to store\r
-            //var gridStore = Ext.StoreMgr.lookup('TasksStore');      \r
-            var newTask = taskData;\r
-            newTask.relation_type = 'task';\r
-            gridStore.loadData([newTask], true);\r
-            \r
-            return true;\r
-        }, this);\r
-        \r
-        // hack to get percentage editor working\r
-        grid.on('rowclick', function(grid,row,e) {\r
-            var cell = Ext.get(grid.getView().getCell(row,1));\r
-            var dom = cell.child('div:last');\r
-            while (cell.first()) {\r
-                cell = cell.first();\r
-                cell.on('click', function(e){\r
-                    e.stopPropagation();\r
-                    grid.fireEvent('celldblclick', grid, row, 1, e);\r
-                });\r
-            }\r
-        }, this);        \r
-        \r
-        grid.on('rowdblclick', this.handlers.editTask, this);\r
-\r
-        return grid;       \r
-    },\r
-    \r
-    /**\r
-     * getProductsGrid\r
-     * get the grid for products\r
-     * \r
-     * @return  grid object\r
-     */\r
-    getProductsGrid: function() {\r
-       \r
-        var columnModel = [\r
-        {\r
-            header: this.translation._("Product"),\r
-            id: 'product_id',\r
-            dataIndex: 'product_id',\r
-            sortable: true,\r
-            width: 150,\r
-            editor: new Tine.Crm.Product.ComboBox({\r
-                store: Tine.Crm.Product.getStore() \r
-            }),\r
-            quickaddField: new Tine.Crm.Product.ComboBox({\r
-                emptyText: this.translation._('Add a product...'),\r
-                store: Tine.Crm.Product.getStore(),\r
-                setPrice: true,\r
-                id: 'new-product_combo'\r
-            }),\r
-            renderer: Tine.Crm.Product.renderer\r
-        },\r
-        {\r
-            id: 'product_desc',\r
-            header: this.translation._("Description"),\r
-            //width: 100,\r
-            sortable: true,\r
-            dataIndex: 'product_desc',\r
-            editor: new Ext.form.TextField({\r
-                allowBlank: false\r
-            }),\r
-            quickaddField: new Ext.form.TextField({\r
-                allowBlank: false\r
-            })\r
-        },\r
-        {\r
-            id: 'product_price',\r
-            header: this.translation._("Price"),\r
-            dataIndex: 'product_price',\r
-            width: 80,\r
-            align: 'right',\r
-            editor: new Ext.form.NumberField({\r
-                allowBlank: false,\r
-                allowNegative: false,\r
-                decimalSeparator: ','\r
-                }),\r
-            quickaddField: new Ext.form.NumberField({\r
-                allowBlank: false,\r
-                allowNegative: false,\r
-                decimalSeparator: ',',\r
-                id: 'new-product_price'\r
-                }),  \r
-            renderer: Ext.util.Format.euMoney\r
-        }                \r
-        ];\r
-        \r
-        var rowSelectionModel = new Ext.grid.RowSelectionModel({multiSelect:true});\r
-        rowSelectionModel.on('selectionchange', function(_selectionModel) {\r
-            var rowCount = _selectionModel.getCount();                    \r
-            if(rowCount < 1) {\r
-                this.actions.unlinkProduct.setDisabled(true);\r
-            } \r
-            if (rowCount == 1) {\r
-                this.actions.unlinkProduct.setDisabled(false);\r
-            }    \r
-            if(rowCount > 1) {                \r
-                this.actions.unlinkProduct.setDisabled(false);\r
-            }\r
-        }, this);\r
-        \r
-        var bbarItems = [\r
-            this.actions.unlinkProduct\r
-        ]; \r
-                \r
-        // get store and create grid\r
-        var gridStore = Ext.StoreMgr.lookup('ProductsStore');  \r
-        grid = new Ext.ux.grid.QuickaddGridPanel({\r
-            title: this.translation._('Products'),\r
-            id: 'crmGridProducts',\r
-            border: false,\r
-            store: gridStore,\r
-            clicksToEdit: 'auto',\r
-            bbar: bbarItems,\r
-            enableColumnHide:false,\r
-            enableColumnMove:false,\r
-            sm: rowSelectionModel,\r
-            loadMask: true,\r
-            quickaddMandatory: 'product_id',\r
-            autoExpandColumn: 'product_desc',\r
-            columns: columnModel\r
-        });\r
-        \r
-        grid.on('newentry', function(productData){\r
-            // add new product to store\r
-            //var gridStore = Ext.StoreMgr.lookup('ProductsStore');      \r
-            var newProduct = [productData];\r
-            gridStore.loadData(newProduct, true);\r
-            \r
-            return true;\r
-        }, this);\r
-        \r
-        return grid;       \r
-    },\r
-    \r
-    /**\r
-     * set context menu for link grid\r
-     * \r
-     * @param   string _type (Contacts|Tasks|Products)\r
-     */\r
-    setLinksContextMenu: function(_type) {\r
-       // init vars\r
-       var rowItems = [];\r
-       var gridItems = [];\r
-       \r
-        switch ( _type ) {\r
-            case 'Contacts':\r
-                // items for row context menu\r
-                rowItems = [\r
-                    this.actions.editContact,\r
-                    this.actions.unlinkContact,\r
-                    {\r
-                       text: this.translation._('Change contact type'),\r
-                       menu: [\r
-                          this.actions.changeContactTypeResponsible,\r
-                           this.actions.changeContactTypeCustomer,\r
-                           this.actions.changeContactTypePartner\r
-                       ]\r
-                    },\r
-                    '-',\r
-                    this.actions.addContact\r
-                ];\r
-                // items for all grid context menu\r
-                gridItems = [\r
-                    this.actions.addContact\r
-                ];\r
-                break;\r
-                \r
-            case 'Tasks':\r
-                // items for row context menu\r
-                rowItems = [\r
-                    this.actions.editTask,\r
-                    this.actions.unlinkTask,\r
-                    '-',\r
-                    //this.actions.linkTask,\r
-                    this.actions.addTask\r
-                ];\r
-                // items for all grid context menu\r
-                gridItems = [\r
-                    //this.actions.linkTask,\r
-                    this.actions.addTask\r
-                ];\r
-                break;\r
-                \r
-            case 'Products':\r
-                // items for row context menu\r
-                rowItems = [\r
-                    this.actions.unlinkProduct\r
-                ];\r
-                break;\r
-        }\r
-        \r
-        var grid = Ext.getCmp('crmGrid' + _type);\r
-        grid.on('rowcontextmenu', function(_grid, _rowIndex, _eventObject) {\r
-            _eventObject.stopEvent();\r
-\r
-            if(!_grid.getSelectionModel().isSelected(_rowIndex)) {\r
-                _grid.getSelectionModel().selectRow(_rowIndex);\r
-            }\r
-            \r
-            var ctxMenuGrid = new Ext.menu.Menu({\r
-                id:'ctxMenuGridRow' + _type, \r
-                items: rowItems\r
-            });\r
-\r
-            ctxMenuGrid.showAt(_eventObject.getXY());\r
-        }, this);\r
-        \r
-        grid.on('contextmenu', function(_eventObject) {\r
-            _eventObject.stopEvent();\r
-            \r
-            var ctxMenuGrid = new Ext.menu.Menu({\r
-                id:'ctxMenuGrid' + _type, \r
-                items: gridItems\r
-            });\r
-\r
-            ctxMenuGrid.showAt(_eventObject.getXY());\r
-        }, this);\r
-        \r
-    },\r
-    \r
-    /**\r
-     * get linked contacts store and put it into store manager\r
-     * \r
-     * @param   array _contacts\r
-     * @param   boolean _reload reload or create new store\r
-     */\r
-    loadContactsStore: function(_contacts, _reload) {\r
-       var storeContacts = null;\r
-       \r
-       if (_reload) {\r
-               storeContacts = Ext.StoreMgr.lookup('ContactsStore');\r
-\r
-               // empty store and fill with data\r
-               storeContacts.removeAll();\r
-               \r
-            if(_contacts) {\r
-                storeContacts.loadData(_contacts, true);                    \r
-            }\r
-               \r
-       } else {\r
-               \r
-               var contactFields = Tine.Addressbook.Model.ContactArray;\r
-               //console.log(contactFields);\r
-               contactFields.push({name: 'relation'});   // the relation object           \r
-            contactFields.push({name: 'relation_type'});     \r
-               \r
-            storeContacts = new Ext.data.JsonStore({\r
-                id: 'id',\r
-                fields: contactFields\r
-            });\r
-                \r
-            if(_contacts) {\r
-                storeContacts.loadData(_contacts, true);                    \r
-            }\r
-    \r
-            storeContacts.setDefaultSort('type', 'asc');   \r
-            \r
-            // focus+select new record\r
-            // @todo sort store again?\r
-            storeContacts.on('add', function(store, records, index){\r
-                var grid = Ext.getCmp('crmGridContacts');\r
-                \r
-                (function() {\r
-                    grid.getView().focusRow(index);\r
-                    grid.getSelectionModel().selectRow(index); \r
-                }).defer(300);\r
-            });\r
-            \r
-            Ext.StoreMgr.add('ContactsStore', storeContacts);\r
-       }\r
-    },\r
-\r
-    /**\r
-     * get linked tasks store and put it into store manager\r
-     * \r
-     * @param   array _tasks\r
-     * @param   boolean _reload reload or create new store\r
-     */\r
-    loadTasksStore: function(_tasks, _reload) {\r
-       var storeTasks = null;\r
-       \r
-       if (_reload) {\r
-\r
-               storeTasks = Ext.StoreMgr.lookup('TasksStore');\r
-\r
-            // empty store and fill with data\r
-            storeTasks.removeAll();\r
-               \r
-            if(_tasks) {\r
-                storeTasks.loadData(_tasks);                    \r
-            }\r
-\r
-        } else {               \r
-               \r
-            var taskFields = Tine.Tasks.TaskArray;\r
-            taskFields.push({name: 'relation'});   // the relation object           \r
-            taskFields.push({name: 'relation_type'});     \r
-               \r
-            storeTasks = new Ext.data.JsonStore({\r
-                id: 'id',\r
-                fields: taskFields\r
-            });\r
-                \r
-            if(_tasks) {\r
-                storeTasks.loadData(_tasks);                    \r
-            }\r
-\r
-            // focus+select new record\r
-            // @todo sort store again?\r
-            storeTasks.on('add', function(store, records, index){\r
-                var grid = Ext.getCmp('crmGridTasks');\r
-                \r
-                (function() {\r
-                    grid.getView().focusRow(index);\r
-                    grid.getSelectionModel().selectRow(index); \r
-                }).defer(300);\r
-                \r
-            });\r
-            \r
-            \r
-            Ext.StoreMgr.add('TasksStore', storeTasks);\r
-       }\r
-    },\r
-\r
-    /**\r
-     * get linked products store and put it into store manager\r
-     * \r
-     * @param   array _products\r
-     * @param   boolean _reload reload or create new store\r
-     */\r
-    loadProductsStore: function(_products, _reload) {\r
-       var storeProducts = null;\r
-       \r
-       if (_reload) {\r
-               \r
-            storeProducts = Ext.StoreMgr.lookup('ProductsStore');\r
-\r
-            // empty store and fill with data\r
-            storeProducts.removeAll();\r
-            \r
-            if(_products) {\r
-                storeProducts.loadData(_products);                    \r
-            }\r
-\r
-        } else {\r
-       \r
-            storeProducts = new Ext.data.JsonStore({\r
-                id: 'id',\r
-                fields: Tine.Crm.Model.ProductLink\r
-            });\r
-                \r
-            if(_products) {\r
-                storeProducts.loadData(_products);                    \r
-                //storeProducts.setDefaultSort('remark', 'asc');     \r
-            }\r
-            \r
-            // update price if new product is chosen\r
-            storeProducts.on('update', function(store, record, index) {\r
-                if(record.data.product_id && !arguments[1].modified.product_price) {          \r
-                    var st_productsAvailable = Tine.Crm.Product.getStore();\r
-                    var preset_price = st_productsAvailable.getById(record.data.product_id);\r
-                    record.data.product_price = preset_price.data.price;\r
-                }\r
-            }); \r
-            \r
-            Ext.StoreMgr.add('ProductsStore', storeProducts);\r
-       }\r
-    },    \r
-    \r
-    /**\r
-     * initActions\r
-     * sets the translation object and actions\r
-     * \r
-     * @param   Tine.Crm.Model.Lead lead lead data\r
-     */\r
-    initActions: function(lead) {\r
-        // contacts\r
-        this.actions.addContact = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            contactType: 'customer',\r
-            text: this.translation._('Add new contact'),\r
-            tooltip: this.translation._('Add new customer contact'),\r
-            iconCls: 'actionAdd',\r
-            scope: this,\r
-            handler: this.handlers.addContact\r
-        }); \r
-\r
-        this.actions.changeContactTypeCustomer = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            contactType: 'customer',\r
-            text: this.translation._('Customer'),\r
-            tooltip: this.translation._('Change type to Customer'),\r
-            iconCls: 'contactIconCustomer',\r
-            scope: this,\r
-            handler: this.handlers.changeContactType\r
-        }); \r
-        \r
-        this.actions.changeContactTypeResponsible = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            contactType: 'responsible',\r
-            text: this.translation._('Responsible'),\r
-            tooltip: this.translation._('Change type to Responsible'),\r
-            iconCls: 'contactIconResponsible',\r
-            scope: this,\r
-            handler: this.handlers.changeContactType\r
-        }); \r
-\r
-        this.actions.changeContactTypePartner = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            contactType: 'partner',\r
-            text: this.translation._('Partner'),\r
-            tooltip: this.translation._('Change type to Partner'),\r
-            iconCls: 'contactIconPartner',\r
-            scope: this,\r
-            handler: this.handlers.changeContactType\r
-        }); \r
-\r
-        this.actions.editContact = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            text: this.translation._('Edit contact'),\r
-            tooltip: this.translation._('Edit selected contact'),\r
-            disabled: true,\r
-            iconCls: 'actionEdit',\r
-            scope: this,\r
-            handler: this.handlers.editContact\r
-        });\r
-        \r
-        this.actions.unlinkContact = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            text: this.translation._('Unlink contact'),\r
-            tooltip: this.translation._('Unlink selected contacts'),\r
-            disabled: true,\r
-            iconCls: 'actionRemove',\r
-            scope: this,\r
-            gridId: 'crmGridContacts',\r
-            storeName: 'ContactsStore',            \r
-            handler: this.handlers.unlink\r
-        });\r
-        \r
-        // tasks\r
-        this.actions.addTask = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            text: this.translation._('Add task'),\r
-            tooltip: this.translation._('Add new task'),\r
-            iconCls: 'actionAdd',\r
-            scope: this,\r
-            handler: this.handlers.addTask\r
-        });\r
-        \r
-        this.actions.editTask = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            text: this.translation._('Edit task'),\r
-            tooltip: this.translation._('Edit selected task'),\r
-            disabled: true,\r
-            iconCls: 'actionEdit',\r
-            scope: this,\r
-            handler: this.handlers.editTask\r
-        });\r
-        \r
-        this.actions.linkTask = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            text: this.translation._('Add task'),\r
-            tooltip: this.translation._('Add existing task to lead'),\r
-            disabled: true,\r
-            iconCls: 'actionAddTask',\r
-            scope: this,\r
-            handler: this.handlers.linkTask\r
-        });\r
-\r
-        this.actions.unlinkTask = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            text: this.translation._('Remove tasks'),\r
-            tooltip: this.translation._('Remove selected tasks'),\r
-            disabled: true,\r
-            iconCls: 'actionRemove',\r
-            scope: this,\r
-            gridId: 'crmGridTasks',\r
-            storeName: 'TasksStore',            \r
-            handler: this.handlers.unlink\r
-        });\r
-\r
-        // products\r
-        this.actions.unlinkProduct = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            text: this.translation._('Remove products'),\r
-            tooltip: this.translation._('Remove selected products'),\r
-            disabled: true,\r
-            iconCls: 'actionRemove',\r
-            scope: this,\r
-            gridId: 'crmGridProducts',\r
-            storeName: 'ProductsStore',            \r
-            handler: this.handlers.unlink\r
-        });\r
-\r
-        // other\r
-        this.actions.exportLead = new Ext.Action({\r
-            requiredGrant: 'editGrant',\r
-            text: this.translation._('Export as PDF'),\r
-            tooltip: this.translation._('Export as PDF'),\r
-            iconCls: 'action_exportAsPdf',\r
-            scope: this,\r
-            handler: this.handlers.exportLead,\r
-            leadId: lead.data.id\r
-        });\r
-    },\r
-    \r
-    /**\r
-     * display the event edit dialog\r
-     *\r
-     * @param   array   _lead\r
-     */\r
-    initComponent: function() {        \r
-        this.lead = this.lead ? this.lead : new Tine.Crm.Model.Lead({}, 0);\r
-        \r
-        Ext.Ajax.request({\r
-            scope: this,\r
-            success: this.onRecordLoad,\r
-            params: {\r
-                method: 'Crm.getLead',\r
-                leadId: this.lead.id\r
-            }\r
-        });\r
-        \r
-        this.translation = new Locale.Gettext();\r
-        this.translation.textdomain('Crm');\r
-        \r
-        this.containerName = this.translation._('Leads');\r
-        this.containersName = this.translation._('Leads');\r
-        \r
-        // @todo rethink, we have no real lead at this point!\r
-        this.initActions(this.lead);\r
-       \r
-        /*********** INIT STORES *******************/\r
-        this.loadContactsStore(null);        \r
-        this.loadTasksStore(null);\r
-        this.loadProductsStore(null); \r
-                \r
-        /*********** the EDIT dialog ************/\r
-        \r
-        var addNoteButton = new Tine.widgets.activities.ActivitiesAddButton({});  \r
-        \r
-        this.tbarItems = [\r
-            this.actions.exportLead,\r
-            addNoteButton\r
-        ];\r
-        \r
-        // @todo getEditForm needs depend on data, which are loaded asynchronus. This is a missconception :-(\r
-        this.items = Tine.Crm.LeadEditDialog.getEditForm({\r
-            contactsPanel: this.getContactsGrid(),\r
-            tasksPanel: this.getTasksGrid(),\r
-            productsPanel: this.getProductsGrid()\r
-        }, this.lead.data);\r
-        \r
-        // add context menu events\r
-        this.setLinksContextMenu('Contacts');\r
-        this.setLinksContextMenu('Tasks');\r
-        this.setLinksContextMenu('Products');\r
-        \r
-        // fix to have the tab panel in the right height accross browsers\r
-        Ext.getCmp('editMainTabPanel').on('afterlayout', function(container) {\r
-            var height = this.getInnerHeight();\r
-            Ext.getCmp('editMainTabPanel').setHeight(660);\r
-        });\r
-        \r
-        Tine.Crm.LeadEditDialog.superclass.initComponent.call(this);\r
-    },\r
-    \r
-    /**\r
-     * @private\r
-     */\r
-    onRender: function(ct, position) {\r
-        Tine.Crm.LeadEditDialog.superclass.onRender.call(this, ct, position);\r
-        Ext.MessageBox.wait(this.translation._('Loading Lead...'), _('Please Wait'));\r
-    },\r
-    \r
-    /**\r
-     * @private\r
-     */\r
-    onRecordLoad: function(response) {\r
-        this.getForm().findField('lead_name').focus(false, 250);\r
-        var recordData = Ext.util.JSON.decode(response.responseText);\r
-        if (this.forceContainer) {\r
-            recordData.container_id = this.forceContainer;\r
-            // only force initially!\r
-            this.forceContainer = null;\r
-        }\r
-        this.updateRecord(recordData);\r
-        \r
-        // update stores\r
-        var relations = Tine.Crm.splitRelations(this.lead.data.relations);\r
-        this.loadContactsStore(relations.contacts, true);        \r
-        this.loadTasksStore(relations.tasks, true);\r
-        this.loadProductsStore(this.lead.data.products, true);\r
-        \r
-        this.updateToolbars.defer(10, this, [this.lead, 'container_id']);\r
-        Tine.widgets.actionUpdater(this.lead, [\r
-            this.actions.addContact,\r
-            this.actions.addTask,\r
-            this.actions.linkTask,\r
-            this.actions.exportLead\r
-        ], 'container_id');\r
-        \r
-        if (! this.lead.id) {\r
-            this.window.setTitle(this.translation.gettext('Add New Lead'));\r
-        } else {\r
-            this.window.setTitle(String.format(this.translation._('Edit Lead "{0}"'), this.lead.get('lead_name')));\r
-        }\r
-        \r
-        this.getForm().loadRecord(this.lead);\r
-                    \r
-        this.updateToolbars(this.lead, 'container_id');\r
-        Ext.MessageBox.hide();\r
-    },\r
-    \r
-    updateRecord: function(recordData) {\r
-        this.lead = new Tine.Crm.Model.Lead(recordData, recordData.id ? recordData.id : 0);\r
-        Tine.Crm.Model.Lead.FixDates(this.lead);\r
-    }\r
-}); // end of application CRM LEAD EDIT DIALOG\r
+Ext.extend(Tine.Crm.TreePanel , Tine.widgets.container.TreePanel);\r
 \r
 /**\r
- * Leads Edit Popup\r
+ * @namespace Tine.Crm\r
+ * @class Tine.Crm.FilterPanel\r
+ * @extends Tine.widgets.grid.PersistentFilterPicker\r
+ * Crm Filter Panel<br>\r
+ * \r
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3\r
+ * @author      Philipp Schuele <p.schuele@metaways.de>\r
+ * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)\r
+ * @version     $Id$\r
  */\r
-Tine.Crm.LeadEditDialog.openWindow = function (config) {\r
-    // if a concreate container is selected in the tree, take this as default container\r
-    var treeNode = Ext.getCmp('crmTree') ? Ext.getCmp('crmTree').getSelectionModel().getSelectedNode() : null;\r
-    if (treeNode && treeNode.attributes && treeNode.attributes.containerType == 'singleContainer') {\r
-        config.forceContainer = treeNode.attributes.container;\r
-    }\r
-    \r
-    config.lead = config.lead ? config.lead : new Tine.Crm.Model.Lead({}, 0);\r
-    var window = Tine.WindowFactory.getWindow({\r
-        width: 800,\r
-        height: 750,\r
-        name: Tine.Crm.LeadEditDialog.prototype.windowNamePrefix + config.lead.id,\r
-        layout: Tine.Crm.LeadEditDialog.prototype.windowLayout,\r
-        contentPanelConstructor: 'Tine.Crm.LeadEditDialog',\r
-        contentPanelConstructorConfig: config\r
-    });\r
-    return window;\r
+Tine.Crm.FilterPanel = function(config) {\r
+    Ext.apply(this, config);\r
+    Tine.Crm.FilterPanel.superclass.constructor.call(this);\r
 };\r
 \r
-/*************************************** CRM MODELS *********************************/\r
-\r
-Ext.namespace('Tine.Crm.Model');\r
-\r
-// lead\r
-Tine.Crm.Model.Lead = Ext.data.Record.create([\r
-    {name: 'id',            type: 'int'},\r
-    {name: 'lead_name',     type: 'string'},\r
-    {name: 'leadstate_id',  type: 'int'},\r
-    {name: 'leadtype_id',   type: 'int'},\r
-    {name: 'leadsource_id', type: 'int'},\r
-    {name: 'container_id'              },\r
-    {name: 'start',         type: 'date', dateFormat: Date.patterns.ISO8601Long},\r
-    {name: 'description',   type: 'string'},\r
-    {name: 'end',           type: 'date', dateFormat: Date.patterns.ISO8601Long},\r
-    {name: 'turnover',      type: 'int'},\r
-    {name: 'probability',   type: 'int'},\r
-    {name: 'end_scheduled', type: 'date', dateFormat: Date.patterns.ISO8601Long},\r
-    {name: 'lastread'},\r
-    {name: 'lastreader'},\r
-    {name: 'responsible'},\r
-    {name: 'customer'},\r
-    {name: 'partner'},\r
-    {name: 'tasks'},\r
-    {name: 'relations'},\r
-    {name: 'products'},\r
-    {name: 'tags'},\r
-    {name: 'notes'},\r
-    {name: 'creation_time',      type: 'date', dateFormat: Date.patterns.ISO8601Long},\r
-    {name: 'created_by',         type: 'int'                  },\r
-    {name: 'last_modified_time', type: 'date', dateFormat: Date.patterns.ISO8601Long},\r
-    {name: 'last_modified_by',   type: 'int'                  },\r
-    {name: 'is_deleted',         type: 'boolean'              },\r
-    {name: 'deleted_time',       type: 'date', dateFormat: Date.patterns.ISO8601Long},\r
-    {name: 'deleted_by',         type: 'int'                  }\r
-]);\r
-\r
-// product link\r
-Tine.Crm.Model.ProductLink = Ext.data.Record.create([\r
-    {name: 'id'},\r
-    {name: 'product_id'},\r
-    {name: 'product_desc'},\r
-    {name: 'product_price'}\r
-]);\r
+Ext.extend(Tine.Crm.FilterPanel, Tine.widgets.grid.PersistentFilterPicker, {\r
+    filter: [{field: 'model', operator: 'equals', value: 'Crm_Model_LeadFilter'}]\r
+});\r
 \r
+/**\r
+ * @namespace Tine.Crm\r
+ * @class Tine.Crm.leadBackend\r
+ * @extends Tine.Tinebase.data.RecordProxy\r
+ * \r
+ * Lead Backend\r
+ */ \r
+Tine.Crm.leadBackend = new Tine.Tinebase.data.RecordProxy({\r
+    appName: 'Crm',\r
+    modelName: 'Lead',\r
+    recordClass: Tine.Crm.Model.Lead\r
+});\r
 \r
-// work arround nasty ext date bug\r
-// @todo is that still needed?\r
-Tine.Crm.Model.Lead.FixDates = function(lead) {\r
-    lead.data.start         = lead.data.start         ? Date.parseDate(lead.data.start, Date.patterns.ISO8601Long)         : lead.data.start;\r
-    lead.data.end           = lead.data.end           ? Date.parseDate(lead.data.end, Date.patterns.ISO8601Long)           : lead.data.end;\r
-    lead.data.end_scheduled = lead.data.end_scheduled ? Date.parseDate(lead.data.end_scheduled, Date.patterns.ISO8601Long) : lead.data.end_scheduled;\r
-};\r
-        \r
index acd0449..b6e69df 100644 (file)
-/**
+/*
  * Tine 2.0
  * 
  * @package     Crm
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Philipp Schuele <p.schuele@metaways.de>
  * @copyright   Copyright (c) 2007-2008 Metaways Infosystems GmbH (http://www.metaways.de)
- * @version     $Id$
+ * @version     $Id:LeadEditDialog.js 7170 2009-03-05 10:58:55Z p.schuele@metaways.de $
  *
  */
-
-/****************** lead edit dialog layout ************************/
+Ext.namespace('Tine.Crm');
 
 /**
- * Lead Edit Dialog
- * separate layout from logic
+ * @namespace   Tine.Crm
+ * @class       Tine.Crm.LeadEditDialog
+ * @extends     Tine.widgets.dialog.EditDialog
+ * 
+ * <p>Lead Edit Dialog</p>
+ * <p></p>
+ * 
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Philipp Schuele <p.schuele@metaways.de>
+ * @copyright   Copyright (c) 2007-2008 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @version     $Id:LeadEditDialog.js 7170 2009-03-05 10:58:55Z p.schuele@metaways.de $
  * 
- * @todo    add more components/panels
- * @todo    add history
+ * @param       {Object} config
+ * @constructor
+ * Create a new Tine.Crm.LeadEditDialog
  */
-Tine.Crm.LeadEditDialog.getEditForm = function(_linkTabpanels, _lead) {
-
-       var translation = new Locale.Gettext();
-    translation.textdomain('Crm');
-
-    /*********** OVERVIEW form fields ************/
-
-    var txtfld_leadName = new Ext.form.TextField({
-        hideLabel: true,
-        id: 'lead_name',
-        //fieldLabel:'Projektname', 
-        emptyText: translation._('Enter short name'),
-        name:'lead_name',
-        allowBlank: false,
-        selectOnFocus: true,
-        anchor:'100%'
-        //selectOnFocus:true            
-        }); 
-    var combo_leadstatus = new Ext.form.ComboBox({
-        fieldLabel: translation._('Leadstate'), 
-        id:'leadstatus',
-        name:'leadstate_id',
-        store: Tine.Crm.LeadState.getStore(),
-        displayField:'leadstate',
-        valueField:'id',
-        mode: 'local',
-        triggerAction: 'all',
-        editable: false,
-        allowBlank: false,
-//        listWidth: '25%',        IE does not like it
-        forceSelection: true,
-        anchor:'95%',
-        lazyInit: false
-    });
+Tine.Crm.LeadEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
     
-    combo_leadstatus.on('select', function(combo, record, index) {
-        if (record.data.probability !== null) {
-            var combo_probability = Ext.getCmp('combo_probability');
-            combo_probability.setValue(record.data.probability);
-        }
-
-        if (record.data.endslead == '1') {
-            var combo_endDate = Ext.getCmp('end');
-            combo_endDate.setValue(new Date());
-        }
-    });
+    /**
+     * @private
+     */
+    windowNamePrefix: 'LeadEditWindow_',
+    appName: 'Crm',
+    recordClass: Tine.Crm.Model.Lead,
+    recordProxy: Tine.Crm.leadBackend,
+    loadRecord: false,
+    tbarItems: [{xtype: 'widget-activitiesaddbutton'}],
+    evalGrants: false,
     
-    var combo_leadtyp = new Ext.form.ComboBox({
-        fieldLabel: translation._('Leadtype'), 
-        id:'leadtype',
-        name:'leadtype_id',
-        store: Tine.Crm.LeadType.getStore(),
-        mode: 'local',
-        displayField:'leadtype',
-        valueField:'id',
-        typeAhead: true,
-        triggerAction: 'all',
-//        listWidth: '25%',                
-        editable: false,
-        allowBlank: false,
-        forceSelection: true,
-        anchor:'95%'    
-    });
-
-    var combo_leadsource = new Ext.form.ComboBox({
-            fieldLabel: translation._('Leadsource'), 
-            id:'leadsource',
-            name:'leadsource_id',
-            store: Tine.Crm.LeadSource.getStore(),
-            displayField:'leadsource',
-            valueField:'id',
-            typeAhead: true,
-//            listWidth: '25%',                
-            mode: 'local',
-            triggerAction: 'all',
-            editable: false,
-            allowBlank: false,
-            forceSelection: true,
-            anchor:'95%'    
-    });
-
-    var combo_probability = new Ext.ux.PercentCombo({
-        fieldLabel: translation._('Probability'), 
-        id: 'combo_probability',
-        anchor:'95%',            
-//        listWidth: '25%',            
-        name:'probability'
-    });
+    /**
+     * overwrite update toolbars function (we don't have record grants yet)
+     * @private
+     */
+    updateToolbars: function() {
 
-    var date_start = new Ext.form.DateField({
-        fieldLabel: translation._('Start'), 
-        allowBlank: false,
-        id: 'start',             
-        anchor: '95%'
-    });
+    },
     
-    var date_scheduledEnd = new Ext.ux.form.ClearableDateField({
-        fieldLabel: translation._('Estimated end'), 
-        id: 'end_scheduled',
-        anchor: '95%'
-    });
+    /**
+     * executed after record got updated from proxy
+     * 
+     * @private
+     */
+    onRecordLoad: function() {
+        // you can do something here
+
+        Tine.Crm.LeadEditDialog.superclass.onRecordLoad.call(this);        
+    },
     
-    var date_end = new Ext.ux.form.ClearableDateField({
-        fieldLabel: translation._('End'), 
-        id: 'end',
-        anchor: '95%'
-    });
+    /**
+     * executed when record gets updated from form
+     * - add attachments to record here
+     * 
+     * @private
+     */
+    onRecordUpdate: function() {
+        Tine.Crm.LeadEditDialog.superclass.onRecordUpdate.call(this);
+        
+        // you can do something here    
+    },
     
-    /*********** OVERVIEW tab panel ************/
-
-    var tabPanelOverview = {
-        title: translation._('Overview'),
-        layout:'border',
-        layoutOnTabChange:true,
-        defaults: {
-            border: true,
-            frame: true            
-        },
-        items: [{
-            layout: 'accordion',
-            border: true,
-            animate: true,             
-            region: 'east',
-            width: 210,
-            split: true,
-            collapsible: true,
-            collapseMode: 'mini',
-            items: [
-                new Ext.Panel({
-                    // @todo generalise!
-                    title: translation._('Description'),
-                    iconCls: 'descriptionIcon',
-                    layout: 'form',
+    /**
+     * returns dialog
+     * 
+     * NOTE: when this method gets called, all initalisation is done.
+     * 
+     * @return {Object}
+     * @private
+     */
+    getFormItems: function() {
+        return {
+            xtype: 'tabpanel',
+            border: false,
+            plain:true,
+            activeTab: 0,
+            border: false,
+            items:[{               
+                title: this.app.i18n._('Lead'),
+                autoScroll: true,
+                border: false,
+                frame: true,
+                layout: 'border',
+                items: [{
+                    region: 'center',
+                    xtype: 'columnform',
                     labelAlign: 'top',
-                    border: false,
-                    items: [{
-                        style: 'margin-top: -4px; border 0px;',
+                    formDefaults: {
+                        xtype:'textfield',
+                        anchor: '100%',
                         labelSeparator: '',
-                        xtype:'textarea',
+                        columnWidth: .333
+                    },
+                    items: [/*[{
+                        fieldLabel: this.app.i18n._('Number'),
+                        name: 'number',
+                        allowBlank: false
+                        }, {
+                        columnWidth: .666,
+                        fieldLabel: this.app.i18n._('Title'),
+                        name: 'title',
+                        allowBlank: false
+                        }], [{
+                        columnWidth: 1,
+                        xtype: 'textarea',
                         name: 'description',
-                        hideLabel: true,
-                        grow: false,
-                        preventScrollbars:false,
-                        anchor:'100% 100%',
-                        emptyText: translation._('Enter description')                        
-                    }]
-                }),
-                new Tine.widgets.tags.TagPanel({
-                    app: 'Crm',
-                    border: false,
-                    bodyStyle: 'border:1px solid #B5B8C8;'
-                }),
-                new Tine.widgets.activities.ActivitiesPanel({
-                    app: 'Crm',
-                    showAddNoteForm: false,
-                    border: false,
-                    bodyStyle: 'border:1px solid #B5B8C8;'
-                })                                    
-            ]
-        },{
-            region:'center',
-            layout: 'form',
-            autoHeight: true,
-            id: 'editCenterPanel',
-            items: [
-                txtfld_leadName,
-                {
-                    xtype: 'panel',
-                    id: 'linkPanelTop',
-                    height: 210,
-                    items: [ _linkTabpanels.contactsPanel ]
-                },
-                {
-                layout:'column',
-                height: 140,
-                id: 'lead_combos',
-                anchor:'100%',                        
-                items: [{
-                    columnWidth: 0.33,
-                    items:[{
-                        layout: 'form',
-                        items: [
-                            combo_leadstatus, 
-                            combo_leadtyp,
-                            combo_leadsource
-                        ]
-                    }]                          
-                },{
-                    columnWidth: 0.33,
-                    items:[{
-                        layout: 'form',
-                        border:false,
-                        items: [
-                        {
-                            xtype:'numberfield',
-                            fieldLabel: translation._('Expected turnover'), 
-                            name: 'turnover',
-                            selectOnFocus: true,
-                            anchor: '95%'
-                        },  
-                            combo_probability//,
-                            //folderTrigger 
-                        ]
-                    }]              
-                },{
-                    columnWidth: 0.33,
-                    items:[{
-                        layout: 'form',
-                        border:false,
-                        items: [
-                            date_start,
-                            date_scheduledEnd,
-                            date_end   
-                        ]
-                    }]
+                        height: 150
+                        }], [{
+                            fieldLabel: this.app.i18n._('Unit'),
+                            name: 'price_unit'
+                        }, {
+                            xtype: 'numberfield',
+                            fieldLabel: this.app.i18n._('Unit Price'),
+                            name: 'price',
+                            allowNegative: false
+                            //decimalSeparator: ','
+                        }, {
+                            fieldLabel: this.app.i18n._('Budget'),
+                            name: 'budget'
+                        }, {
+                            hideLabel: true,
+                            boxLabel: this.app.i18n._('Timesheets are billable'),
+                            name: 'is_billable',
+                            xtype: 'checkbox'
+                        }, {
+                            fieldLabel: this.app.i18n._('Status'),
+                            name: 'is_open',
+                            xtype: 'combo',
+                            mode: 'local',
+                            forceSelection: true,
+                            triggerAction: 'all',
+                            store: [[0, this.app.i18n._('closed')], [1, this.app.i18n._('open')]]
+                        }, {
+                            fieldLabel: this.app.i18n._('Billed'),
+                            name: 'status',
+                            xtype: 'combo',
+                            mode: 'local',
+                            forceSelection: true,
+                            triggerAction: 'all',
+                            value: 'not yet billed',
+                            store: [
+                                ['not yet billed', this.app.i18n._('not yet billed')], 
+                                ['to bill', this.app.i18n._('to bill')],
+                                ['billed', this.app.i18n._('billed')]
+                            ]
+                        }]*/] 
+                }, {
+                    // activities and tags
+                    layout: 'accordion',
+                    animate: true,
+                    region: 'east',
+                    width: 210,
+                    split: true,
+                    collapsible: true,
+                    collapseMode: 'mini',
+                    margins: '0 5 0 5',
+                    border: true,
+                    items: [
+                    new Tine.widgets.activities.ActivitiesPanel({
+                        app: 'Crm',
+                        showAddNoteForm: false,
+                        border: false,
+                        bodyStyle: 'border:1px solid #B5B8C8;'
+                    }),
+                    new Tine.widgets.tags.TagPanel({
+                        app: 'Crm',
+                        border: false,
+                        bodyStyle: 'border:1px solid #B5B8C8;'
+                    })]
                 }]
-            }, {
-                xtype: 'tabpanel',
-                //style: 'margin-top: 10px;',
-                id: 'linkPanelBottom',
-                activeTab: 0,
-                height: 250,
-                items: [
-                    _linkTabpanels.tasksPanel,
-                    _linkTabpanels.productsPanel
-                ]
-            }
-            ]
-        }]
-    };        
-    
-    /*********** HISTORY tab panel ************/
-
-    var tabPanelActivities = new Tine.widgets.activities.ActivitiesTabPanel({
-        app: 'Crm',
-        record_id: _lead.id,
-        record_model: 'Crm_Model_Lead'
-    });
-
-    /*********** MAIN tab panel ************/
-    
-    var tabPanel = new Ext.TabPanel({
-        plain:true,
-        activeTab: 0,
-        id: 'editMainTabPanel',
-        layoutOnTabChange:true,  
-        items:[
-            tabPanelOverview,
-            tabPanelActivities                    
-        ]
-    });
-    
-    // @todo add savePath (container) to the bottom and remove it from form in the middle
-    return [
-        tabPanel
-        //savePath
-    ];
-};
-
-/*********************** crm widgets ************************/
-
-Ext.namespace('Tine.Crm', 'Tine.Crm.contactType');
-
-/**
- * contact type select combo box
- * 
- */
-Tine.Crm.contactType.ComboBox = Ext.extend(Ext.form.ComboBox, {        
-       /**
-     * @cfg {bool} autoExpand Autoexpand comboBox on focus.
-     */
-    autoExpand: false,
-    /**
-     * @cfg {bool} blurOnSelect blurs combobox when item gets selected
-     */
-    blurOnSelect: false,
-    
-    displayField: 'label',
-    valueField: 'relation_type',
-    mode: 'local',
-    triggerAction: 'all',
-    lazyInit: false,
-    
-    //private
-    initComponent: function() {
-       
-        var translation = new Locale.Gettext();
-        translation.textdomain('Crm');
-       
-        Tine.Crm.contactType.ComboBox.superclass.initComponent.call(this);
-        // allways set a default
-        if(!this.value) {
-            this.value = 'responsible';
-        }
-            
-        this.store = new Ext.data.SimpleStore({
-            fields: ['label', 'relation_type'],
-            data: [
-                    [translation._('Responsible'), 'responsible'],
-                    [translation._('Customer'), 'customer'],
-                    [translation._('Partner'), 'partner']
-                ]
-        });
-        
-        if (this.autoExpand) {
-            this.lazyInit = false;
-            this.on('focus', function(){
-                this.selectByValue(this.getValue());
-                this.onTriggerClick();
-            });
-        }
-        
-        if (this.blurOnSelect){
-            this.on('select', function(){
-                this.fireEvent('blur', this);
-            }, this);
-        }
+            }, new Tine.widgets.activities.ActivitiesTabPanel({
+                app: this.appName,
+                record_id: this.record.id,
+                record_model: this.appName + '_Model_' + this.recordClass.getMeta('modelName')
+            })]
+        };
     }
 });
-Ext.reg('leadcontacttypecombo', Tine.Crm.contactType.ComboBox);
 
 /**
- * contact type renderer
+ * Crm Edit Popup
  * 
- * @param   string type
- * @return  contact type icon
+ * @param   {Object} config
+ * @return  {Ext.ux.Window}
  */
-Tine.Crm.contactType.Renderer = function(type)
-{
-    var translation = new Locale.Gettext();
-    translation.textdomain('Crm');
-    
-    switch ( type ) {
-        case 'responsible':
-            var iconClass = 'contactIconResponsible';
-            var qTip = translation._('Responsible');
-            break;
-        case 'customer':
-            var iconClass = 'contactIconCustomer';
-            var qTip = translation._('Customer');
-            break;
-        case 'partner':
-            var iconClass = 'contactIconPartner';
-            var qTip = translation._('Partner');
-            break;
-    }
-    
-    var icon = '<img class="x-menu-item-icon contactIcon ' + iconClass + '" src="library/ExtJS/resources/images/default/s.gif" ext:qtip="' + qTip + '"/>';
-    
-    return icon;
+Tine.Crm.LeadEditDialog.openWindow = function (config) {
+    var id = (config.record && config.record.id) ? config.record.id : 0;
+    var window = Tine.WindowFactory.getWindow({
+        width: 800,
+        height: 470,
+        name: Tine.Crm.LeadEditDialog.prototype.windowNamePrefix + id,
+        contentPanelConstructor: 'Tine.Crm.LeadEditDialog',
+        contentPanelConstructorConfig: config
+    });
+    return window;
 };
diff --git a/tine20/Crm/js/LeadGridPanel.js b/tine20/Crm/js/LeadGridPanel.js
new file mode 100644 (file)
index 0000000..aa35bc5
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Tine 2.0
+ * 
+ * @package     Crm
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Philipp Schuele <p.schuele@metaways.de>
+ * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @version     $Id:GridPanel.js 7170 2009-03-05 10:58:55Z p.schuele@metaways.de $
+ *
+ */
+Ext.namespace('Tine.Crm');
+
+/**
+ * Lead grid panel
+ * 
+ * @namespace   Tine.Crm
+ * @class       Tine.Crm.GridPanel
+ * @extends     Tine.Tinebase.widgets.app.GridPanel
+ * 
+ * <p>Lead Grid Panel</p>
+ * <p><pre>
+ * </pre></p>
+ * 
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Philipp Schuele <p.schuele@metaways.de>
+ * @copyright   Copyright (c) 2007-2008 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @version     $Id:GridPanel.js 7170 2009-03-05 10:58:55Z p.schuele@metaways.de $
+ * 
+ * @param       {Object} config
+ * @constructor
+ * Create a new Tine.Crm.GridPanel
+ */
+Tine.Crm.GridPanel = Ext.extend(Tine.Tinebase.widgets.app.GridPanel, {
+    /**
+     * record class
+     * @cfg {Tine.Crm.Model.Lead} recordClass
+     */
+    recordClass: Tine.Crm.Model.Lead,
+    
+    /**
+     * eval grants
+     * @cfg {Boolean} evalGrants
+     */
+    evalGrants: true,
+    
+    /**
+     * grid specific
+     * @private
+     */
+    defaultSortInfo: {field: 'lead_name', direction: 'DESC'},
+    gridConfig: {
+        loadMask: true,
+        autoExpandColumn: 'title'
+    },
+     
+    /**
+     * inits this cmp
+     * @private
+     */
+    initComponent: function() {
+        this.recordProxy = Tine.Crm.recordBackend;
+        
+        //this.actionToolbarItems = this.getToolbarItems();
+        this.gridConfig.cm = this.getColumnModel();
+        //this.initFilterToolbar();
+        
+        this.plugins = this.plugins || [];
+        //this.plugins.push(this.filterToolbar);
+        
+        Tine.Crm.GridPanel.superclass.initComponent.call(this);
+        
+        //this.action_addInNewWindow.setDisabled(! Tine.Tinebase.common.hasRight('manage', 'Crm', 'records'));
+        //this.action_editInNewWindow.requiredGrant = 'editGrant';
+        
+    },
+    
+    /**
+     * initialises filter toolbar
+     *  @private
+     */
+    initFilterToolbar: function() {
+        this.filterToolbar = new Tine.widgets.grid.FilterToolbar({
+            filterModels: [
+                // @todo add filtes
+                /*
+                {label: this.app.i18n._('Lead'),    field: 'query',       operators: ['contains']},
+                {label: this.app.i18n._('Description'),    field: 'description', operators: ['contains']},
+                new Tine.Crm.TimeAccountStatusGridFilter({
+                    field: 'status'
+                }),
+                */
+                new Tine.widgets.tags.TagFilter({app: this.app})
+             ],
+             defaultFilter: 'query',
+             filters: []
+        });
+    },    
+    
+    /**
+     * returns cm
+     * 
+     * @return Ext.grid.ColumnModel
+     * @private
+     * 
+     * TODO    add more columns
+     */
+    getColumnModel: function(){
+        return new Ext.grid.ColumnModel({ 
+            defaults: {
+                sortable: true,
+                resizable: true
+            },
+            columns: [/*{
+                id: 'number',
+                header: this.app.i18n._("Number"),
+                width: 100,
+                sortable: true,
+                dataIndex: 'number'
+            },{
+                id: 'title',
+                header: this.app.i18n._("Title"),
+                width: 350,
+                sortable: true,
+                dataIndex: 'title'
+            },{
+                id: 'status',
+                header: this.app.i18n._("Status"),
+                width: 150,
+                sortable: true,
+                dataIndex: 'status',
+                renderer: this.statusRenderer.createDelegate(this)
+            },{
+                id: 'budget',
+                header: this.app.i18n._("Budget"),
+                width: 100,
+                sortable: true,
+                dataIndex: 'budget'
+            }*/]
+        });
+    },
+    
+    /**
+     * status column renderer
+     * @param {string} value
+     * @return {string}
+     */
+    statusRenderer: function(value) {
+        return this.app.i18n._hidden(value);
+    },
+    
+    /**
+     * return additional tb items
+     * @private
+     */
+    getToolbarItems: function(){
+        /*
+        this.action_showClosedToggle = new Tine.widgets.grid.FilterButton({
+            text: this.app.i18n._('Show closed'),
+            iconCls: 'action_showArchived',
+            field: 'showClosed'
+        });
+        */
+        
+        return [
+            /*
+            new Ext.Toolbar.Separator(),
+            this.action_showClosedToggle
+            */
+        ];
+    }    
+});
diff --git a/tine20/Crm/js/Models.js b/tine20/Crm/js/Models.js
new file mode 100644 (file)
index 0000000..eef5015
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Tine 2.0
+ * 
+ * @package     Crm
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Philipp Schuele <p.schuele@metaways.de>
+ * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @version     $Id$
+ *
+ */
+Ext.namespace('Tine.Crm', 'Tine.Crm.Model');
+
+/**
+ * @namespace Tine.Crm.Model
+ * @class Tine.Crm.Model.Lead
+ * @extends Tine.Tinebase.data.Record
+ * 
+ * Message Record Definition
+ */ 
+Tine.Crm.Model.Lead = Tine.Tinebase.data.Record.create([
+        {name: 'id',            type: 'int'},
+        {name: 'lead_name',     type: 'string'},
+        {name: 'leadstate_id',  type: 'int'},
+        {name: 'leadtype_id',   type: 'int'},
+        {name: 'leadsource_id', type: 'int'},
+        {name: 'container_id'              },
+        {name: 'start',         type: 'date', dateFormat: Date.patterns.ISO8601Long},
+        {name: 'description',   type: 'string'},
+        {name: 'end',           type: 'date', dateFormat: Date.patterns.ISO8601Long},
+        {name: 'turnover',      type: 'int'},
+        {name: 'probability',   type: 'int'},
+        {name: 'end_scheduled', type: 'date', dateFormat: Date.patterns.ISO8601Long},
+        {name: 'lastread'},
+        {name: 'lastreader'},
+        {name: 'responsible'},
+        {name: 'customer'},
+        {name: 'partner'},
+        {name: 'tasks'},
+        {name: 'relations'},
+        {name: 'products'},
+        {name: 'tags'},
+        {name: 'notes'},
+        {name: 'creation_time',      type: 'date', dateFormat: Date.patterns.ISO8601Long},
+        {name: 'created_by',         type: 'int'                  },
+        {name: 'last_modified_time', type: 'date', dateFormat: Date.patterns.ISO8601Long},
+        {name: 'last_modified_by',   type: 'int'                  },
+        {name: 'is_deleted',         type: 'boolean'              },
+        {name: 'deleted_time',       type: 'date', dateFormat: Date.patterns.ISO8601Long},
+        {name: 'deleted_by',         type: 'int'                  }
+    ], {
+    appName: 'Crm',
+    modelName: 'Lead',
+    idProperty: 'id',
+    titleProperty: 'title',
+    // ngettext('Lead', 'Leads', n);
+    recordName: 'Lead',
+    recordsName: 'Leads',
+    containerProperty: 'container_id',
+    // ngettext('record list', 'record lists', n);
+    containerName: 'Leads',
+    containersName: 'Leads'
+    /*,
+    getTitle: function() {
+        return this.get('number') ? (this.get('number') + ' ' + this.get('title')) : false;
+    }
+    */
+});
+
+/**
+ * @namespace Tine.Crm.Model
+ * @class Tine.Crm.Model.ProductLink
+ * @extends Tine.Tinebase.data.Record
+ * 
+ * Product Link Record Definition
+ * 
+ * TODO remove that?
+ */ 
+Tine.Crm.Model.ProductLink = Ext.data.Record.create([
+    {name: 'id'},
+    {name: 'product_id'},
+    {name: 'product_desc'},
+    {name: 'product_price'}
+]);
+
+// work arround nasty ext date bug
+// TODO is that still needed?
+/*
+Tine.Crm.Model.Lead.FixDates = function(lead) {
+    lead.data.start         = lead.data.start         ? Date.parseDate(lead.data.start, Date.patterns.ISO8601Long)         : lead.data.start;
+    lead.data.end           = lead.data.end           ? Date.parseDate(lead.data.end, Date.patterns.ISO8601Long)           : lead.data.end;
+    lead.data.end_scheduled = lead.data.end_scheduled ? Date.parseDate(lead.data.end_scheduled, Date.patterns.ISO8601Long) : lead.data.end_scheduled;
+};
+*/
+