0011286: allow contacts without org/family name
[tine20] / tine20 / Addressbook / js / ContactGrid.js
1 /*
2  * Tine 2.0
3  * 
4  * @package     Addressbook
5  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
6  * @author      Cornelius Weiss <c.weiss@metaways.de>
7  * @copyright   Copyright (c) 2007-2013 Metaways Infosystems GmbH (http://www.metaways.de)
8  *
9  */
10  
11 Ext.ns('Tine.Addressbook');
12
13 /**
14  * Contact grid panel
15  * 
16  * @namespace   Tine.Addressbook
17  * @class       Tine.Addressbook.ContactGridPanel
18  * @extends     Tine.widgets.grid.GridPanel
19  * 
20  * <p>Contact Grid Panel</p>
21  * 
22  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
23  * @author      Cornelius Weiss <c.weiss@metaways.de>
24  * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
25  * 
26  * @param       {Object} config
27  * @constructor
28  * Create a new Tine.Addressbook.ContactGridPanel
29  */
30 Tine.Addressbook.ContactGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
31     /**
32      * record class
33      * @cfg {Tine.Addressbook.Model.Contact} recordClass
34      */
35     recordClass: Tine.Addressbook.Model.Contact,
36     
37     /**
38      * grid specific
39      * @private
40      */ 
41     defaultSortInfo: {field: 'n_fileas', direction: 'ASC'},
42     gridConfig: {
43         autoExpandColumn: 'n_fileas',
44         enableDragDrop: true,
45         ddGroup: 'containerDDGroup'
46     },
47     copyEditAction: true,
48     felamimail: false,
49     multipleEdit: true,
50     duplicateResolvable: true,
51     
52     /**
53      * @cfg {Bool} hasDetailsPanel 
54      */
55     hasDetailsPanel: true,
56     
57     /**
58      * inits this cmp
59      * @private
60      */
61     initComponent: function() {
62         this.recordProxy = Tine.Addressbook.contactBackend;
63         
64         // check if felamimail is installed and user has run right and wants to use felamimail in adb
65         if (Tine.Felamimail && Tine.Tinebase.common.hasRight('run', 'Felamimail') && Tine.Felamimail.registry.get('preferences').get('useInAdb')) {
66             this.felamimail = (Tine.Felamimail.registry.get('preferences').get('useInAdb') == 1);
67         }
68         this.gridConfig.cm = this.getColumnModel();
69
70         if (this.hasDetailsPanel) {
71             this.detailsPanel = this.getDetailsPanel();
72         }
73
74         Tine.Addressbook.ContactGridPanel.superclass.initComponent.call(this);
75     },
76     
77     /**
78      * returns column model
79      * 
80      * @return Ext.grid.ColumnModel
81      * @private
82      */
83     getColumnModel: function() {
84         return new Ext.grid.ColumnModel({
85             defaults: {
86                 sortable: true,
87                 hidden: true,
88                 resizable: true
89             },
90             columns: this.getColumns()
91         });
92     },
93     
94     /**
95      * returns array with columns
96      * 
97      * @return {Array}
98      */
99     getColumns: function() {
100         return [
101             { id: 'tid', header: this.app.i18n._('Type'), dataIndex: 'tid', width: 30, renderer: this.contactTypeRenderer.createDelegate(this), hidden: false },
102             { id: 'tags', header: this.app.i18n._('Tags'), dataIndex: 'tags', width: 50, renderer: Tine.Tinebase.common.tagsRenderer, sortable: false, hidden: false  },
103             { id: 'salutation', header: this.app.i18n._('Salutation'), dataIndex: 'salutation', renderer: Tine.Tinebase.widgets.keyfield.Renderer.get('Addressbook', 'contactSalutation') },
104             {
105                 id: 'container_id',
106                 dataIndex: 'container_id',
107                 header: this.recordClass.getContainerName(),
108                 width: 150,
109                 renderer: Tine.Tinebase.common.containerRenderer
110             },
111             { id: 'n_prefix', header: this.app.i18n._('Title'), dataIndex: 'n_prefix', width: 80 },
112             { id: 'n_middle', header: this.app.i18n._('Middle Name'), dataIndex: 'n_middle', width: 80 },
113             { id: 'n_family', header: this.app.i18n._('Last Name'), dataIndex: 'n_family' },
114             { id: 'n_given', header: this.app.i18n._('First Name'), dataIndex: 'n_given', width: 80 },
115             { id: 'n_fn', header: this.app.i18n._('Full Name'), dataIndex: 'n_fn', renderer: this.displayNameRenderer },
116             { id: 'n_fileas', header: this.app.i18n._('Display Name'), dataIndex: 'n_fileas', hidden: false, renderer: this.displayNameRenderer},
117             { id: 'org_name', header: this.app.i18n._('Company'), dataIndex: 'org_name', width: 120, hidden: false },
118             { id: 'org_unit', header: this.app.i18n._('Unit'), dataIndex: 'org_unit'  },
119             { id: 'title', header: this.app.i18n._('Job Title'), dataIndex: 'title' },
120 //            { id: 'role', header: this.app.i18n._('Job Role'), dataIndex: 'role' },
121 //            { id: 'room', header: this.app.i18n._('Room'), dataIndex: 'room' },
122             { id: 'adr_one_street', header: this.app.i18n._('Street'), dataIndex: 'adr_one_street' },
123             { id: 'adr_one_locality', header: this.app.i18n._('City'), dataIndex: 'adr_one_locality', width: 150, hidden: false },
124             { id: 'adr_one_region', header: this.app.i18n._('Region'), dataIndex: 'adr_one_region' },
125             { id: 'adr_one_postalcode', header: this.app.i18n._('Postalcode'), dataIndex: 'adr_one_postalcode' },
126             { id: 'adr_one_countryname', header: this.app.i18n._('Country'), dataIndex: 'adr_one_countryname' },
127             { id: 'adr_two_street', header: this.app.i18n._('Street (private)'), dataIndex: 'adr_two_street' },
128             { id: 'adr_two_locality', header: this.app.i18n._('City (private)'), dataIndex: 'adr_two_locality' },
129             { id: 'adr_two_region', header: this.app.i18n._('Region (private)'), dataIndex: 'adr_two_region' },
130             { id: 'adr_two_postalcode', header: this.app.i18n._('Postalcode (private)'), dataIndex: 'adr_two_postalcode' },
131             { id: 'adr_two_countryname', header: this.app.i18n._('Country (private)'), dataIndex: 'adr_two_countryname' },
132             { id: 'email', header: this.app.i18n._('Email'), dataIndex: 'email', width: 150, hidden: false },
133             { id: 'tel_work', header: this.app.i18n._('Phone'), dataIndex: 'tel_work', hidden: false },
134             { id: 'tel_cell', header: this.app.i18n._('Mobile'), dataIndex: 'tel_cell', hidden: false },
135             { id: 'tel_fax', header: this.app.i18n._('Fax'), dataIndex: 'tel_fax' },
136             { id: 'tel_car', header: this.app.i18n._('Car phone'), dataIndex: 'tel_car' },
137             { id: 'tel_pager', header: this.app.i18n._('Pager'), dataIndex: 'tel_pager' },
138             { id: 'tel_home', header: this.app.i18n._('Phone (private)'), dataIndex: 'tel_home' },
139             { id: 'tel_fax_home', header: this.app.i18n._('Fax (private)'), dataIndex: 'tel_fax_home' },
140             { id: 'tel_cell_private', header: this.app.i18n._('Mobile (private)'), dataIndex: 'tel_cell_private' },
141             { id: 'email_home', header: this.app.i18n._('Email (private)'), dataIndex: 'email_home' },
142             { id: 'url', header: this.app.i18n._('Web'), dataIndex: 'url' },
143             { id: 'url_home', header: this.app.i18n._('URL (private)'), dataIndex: 'url_home' },
144             { id: 'note', header: this.app.i18n._('Note'), dataIndex: 'note' },
145             { id: 'tz', header: this.app.i18n._('Timezone'), dataIndex: 'tz' },
146             { id: 'geo', header: this.app.i18n._('Geo'), dataIndex: 'geo' },
147             { id: 'bday', header: this.app.i18n._('Birthday'), dataIndex: 'bday', renderer: Tine.Tinebase.common.dateRenderer }
148         ].concat(this.getModlogColumns().concat(this.getCustomfieldColumns()));
149     },
150     
151     /**
152      * @private
153      */
154     initActions: function() {
155         this.actions_exportContact = new Ext.Action({
156             requiredGrant: 'exportGrant',
157             text: String.format(this.app.i18n.ngettext('Export {0}', 'Export {0}', 50), this.i18nRecordsName),
158             singularText: String.format(this.app.i18n.ngettext('Export {0}', 'Export {0}', 1), this.i18nRecordName),
159             pluralText:  String.format(this.app.i18n.ngettext('Export {0}', 'Export {0}', 1), this.i18nRecordsName),
160             translationObject: this.app.i18n,
161             iconCls: 'action_export',
162             scope: this,
163             disabled: true,
164             allowMultiple: true,
165             menu: {
166                 items: [
167                     new Tine.widgets.grid.ExportButton({
168                         text: this.app.i18n._('Export as PDF'),
169                         iconCls: 'action_exportAsPdf',
170                         format: 'pdf',
171                         exportFunction: 'Addressbook.exportContacts',
172                         gridPanel: this
173                     }),
174                     new Tine.widgets.grid.ExportButton({
175                         text: this.app.i18n._('Export as CSV'),
176                         iconCls: 'tinebase-action-export-csv',
177                         format: 'csv',
178                         exportFunction: 'Addressbook.exportContacts',
179                         gridPanel: this
180                     }),
181                     new Tine.widgets.grid.ExportButton({
182                         text: this.app.i18n._('Export as ODS'),
183                         format: 'ods',
184                         iconCls: 'tinebase-action-export-ods',
185                         exportFunction: 'Addressbook.exportContacts',
186                         gridPanel: this
187                     }),
188                     new Tine.widgets.grid.ExportButton({
189                         text: this.app.i18n._('Export as XLS'),
190                         format: 'xls',
191                         iconCls: 'tinebase-action-export-xls',
192                         exportFunction: 'Addressbook.exportContacts',
193                         gridPanel: this
194                     }),
195                     new Tine.widgets.grid.ExportButton({
196                         text: this.app.i18n._('Export as DOC'),
197                         format: 'doc',
198                         iconCls: 'tinebase-action-export-doc',
199                         exportFunction: 'Addressbook.exportContacts',
200                         gridPanel: this
201                     }),
202                     new Tine.widgets.grid.ExportButton({
203                         text: this.app.i18n._('Export as ...'),
204                         iconCls: 'tinebase-action-export-xls',
205                         exportFunction: 'Addressbook.exportContacts',
206                         showExportDialog: true,
207                         gridPanel: this
208                     })
209                 ]
210             }
211         });
212
213         this.actions_import = new Ext.Action({
214             //requiredGrant: 'addGrant',
215             text: this.app.i18n._('Import contacts'),
216             disabled: false,
217             handler: this.onImport,
218             iconCls: 'action_import',
219             scope: this,
220             allowMultiple: true
221         });
222         
223         // register actions in updater
224         this.actionUpdater.addActions([
225             this.actions_exportContact,
226             this.actions_import
227         ]);
228         
229         Tine.Addressbook.ContactGridPanel.superclass.initActions.call(this);
230     },
231     
232     /**
233      * add custom items to action toolbar
234      * 
235      * @return {Object}
236      */
237     getActionToolbarItems: function() {
238         return [
239             {
240                 xtype: 'buttongroup',
241                 columns: 1,
242                 frame: false,
243                 items: [
244                     this.actions_exportContact,
245                     this.actions_import
246                 ]
247             }
248         ];
249     },
250     
251     /**
252      * add custom items to context menu
253      * 
254      * @return {Array}
255      */
256     getContextMenuItems: function() {
257         var items = [
258             '-',
259             this.actions_exportContact,
260             '-'
261         ];
262         
263         return items;
264     },
265     
266     /**
267      * import contacts
268      * 
269      * @param {Button} btn 
270      * 
271      * TODO generalize this & the import button
272      */
273     onImport: function(btn) {
274         var popupWindow = Tine.widgets.dialog.ImportDialog.openWindow({
275             appName: 'Addressbook',
276             modelName: 'Contact',
277             defaultImportContainer: this.app.getMainScreen().getWestPanel().getContainerTreePanel().getDefaultContainer('defaultAddressbook'),
278             
279             // update grid after import
280             listeners: {
281                 scope: this,
282                 'finish': function() {
283                     this.loadGridData({
284                         preserveCursor:     false, 
285                         preserveSelection:  false, 
286                         preserveScroller:   false,
287                         removeStrategy:     'default'
288                     });
289                 }
290             }
291         });
292     },
293         
294     /**
295      * tid renderer
296      * 
297      * @private
298      * @return {String} HTML
299      */
300     contactTypeRenderer: function(data, cell, record) {
301         var i18n = Tine.Tinebase.appMgr.get('Addressbook').i18n,
302             hasAccount = ((record.get && record.get('account_id')) || record.account_id),
303             cssClass = hasAccount ? 'renderer_typeAccountIcon' : 'renderer_typeContactIcon',
304             qtipText = Tine.Tinebase.common.doubleEncode(hasAccount ? i18n._('Contact of a user account') : i18n._('Contact'));
305         
306         return '<div ext:qtip="' + qtipText + '" style="background-position:0px;" class="' + cssClass + '">&#160</div>';
307     },
308
309     displayNameRenderer: function(data) {
310         var i18n = Tine.Tinebase.appMgr.get('Addressbook').i18n;
311         return data ? data : ('<div class="renderer_displayNameRenderer_noName">' + i18n._('No name') + '</div>');
312     },
313
314     /**
315      * returns details panel
316      * 
317      * @private
318      * @return {Tine.Addressbook.ContactGridDetailsPanel}
319      */
320     getDetailsPanel: function() {
321         return new Tine.Addressbook.ContactGridDetailsPanel({
322             gridpanel: this,
323             il8n: this.app.i18n,
324             felamimail: this.felamimail
325         });
326     }
327 });