Merge branch '2014.11-develop' into 2015.07
[tine20] / tine20 / Tinebase / js / widgets / ActivitiesPanel.js
1 /*
2  * Tine 2.0
3  * 
4  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
5  * @author      Philipp Schuele <p.schuele@metaways.de>
6  * @author      Michael Spahn <m.spahn@metaways.de
7  * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
8  **/
9
10 /*global Ext, Tine, Locale*/
11
12 Ext.ns('Tine.widgets', 'Tine.widgets.activities');
13
14
15 /************************* tab panel *********************************/
16
17 /**
18  * Class for a activities tab with notes/activities grid*
19  * 
20  * @namespace   Tine.widgets.activities
21  * @class       Tine.widgets.activities.ActivitiesTabPanel
22  * @extends     Ext.Panel
23  */
24 Tine.widgets.activities.ActivitiesTabPanel = Ext.extend(Ext.Panel, {
25
26     /**
27      * @cfg {String} app Application which uses this panel
28      */
29     app: '',
30     
31     /**
32      * @var {Ext.data.JsonStore}
33      * Holds activities of the record this panel is displayed for
34      */
35     store: null,
36     
37     /**
38      * the translation object
39      */
40     translation: null,
41
42     /**
43      * @cfg {Object} paging defaults
44      */
45     paging: {
46         start: 0,
47         limit: 20,
48         sort: 'creation_time',
49         dir: 'DESC'
50     },
51
52     /**
53      * the record id
54      */
55     record_id: null,
56     
57     /**
58      * the record model
59      */
60     record_model: null,
61     
62     /**
63      * other config options
64      */
65     title: null,
66     layout: 'fit',
67     
68     getActivitiesGrid: function () {
69         // @todo add row expander on select ?
70         // @todo add context menu ?
71         // @todo add buttons ?        
72         // @todo add more renderers ?
73         
74         // the columnmodel
75         var columnModel = new Ext.grid.ColumnModel([
76             { resizable: true, id: 'note_type_id', header: this.translation.gettext('Type'), dataIndex: 'note_type_id', width: 15, 
77                 renderer: Tine.widgets.activities.getTypeIcon },
78             { resizable: true, id: 'note', header: this.translation.gettext('Note'), dataIndex: 'note'},
79             { resizable: true, id: 'created_by', header: this.translation.gettext('Created By'), dataIndex: 'created_by', width: 70},
80             { resizable: true, id: 'creation_time', header: this.translation.gettext('Timestamp'), dataIndex: 'creation_time', width: 50, 
81                 renderer: Tine.Tinebase.common.dateTimeRenderer }
82         ]);
83
84         columnModel.defaultSortable = true; // by default columns are sortable
85         
86         // the rowselection model
87         var rowSelectionModel = new Ext.grid.RowSelectionModel({multiSelect: true});
88
89         // the paging toolbar
90         var pagingToolbar = new Ext.PagingToolbar({
91             pageSize: 20,
92             store: this.store,
93             displayInfo: true,
94             displayMsg: this.translation.gettext('Displaying history records {0} - {1} of {2}'),
95             emptyMsg: this.translation.gettext("No history to display")
96         });
97
98         // the gridpanel
99         var gridPanel = new Ext.grid.GridPanel({
100             id: 'Activities_Grid',
101             cls: 'tw-activities-grid',
102             store: this.store,
103             cm: columnModel,
104             tbar: pagingToolbar,     
105             selModel: rowSelectionModel,
106             border: false,                  
107             //autoExpandColumn: 'note',
108             //enableColLock:false,
109             //autoHeight: true,
110             viewConfig: {
111                 autoFill: true,
112                 forceFit: true,
113                 ignoreAdd: true,
114                 autoScroll: true
115             }  
116         });
117         
118         return gridPanel;
119     },
120     
121     /**
122      * init the contacts json grid store
123      */
124     initStore: function () {
125
126         this.store = new Ext.data.JsonStore({
127             id: 'id',
128             autoLoad: false,
129             root: 'results',
130             totalProperty: 'totalcount',
131             fields: Tine.Tinebase.Model.Note,
132             remoteSort: true,
133             baseParams: {
134                 method: 'Tinebase.searchNotes'
135             },
136             sortInfo: {
137                 field: this.paging.sort,
138                 direction: this.paging.dir
139             }
140         });
141         
142         // register store
143         Ext.StoreMgr.add('NotesGridStore', this.store);
144         
145         // prepare filter
146         this.store.on('beforeload', function (store, options) {
147             if (!options.params) {
148                 options.params = {};
149             }
150             
151             // paging toolbar only works with this properties in the options!
152             options.params.sort  = store.getSortState() ? store.getSortState().field : this.paging.sort;
153             options.params.dir   = store.getSortState() ? store.getSortState().direction : this.paging.dir;
154             options.params.start = options.params.start ? options.params.start : this.paging.start;
155             options.params.limit = options.params.limit ? options.params.limit : this.paging.limit;
156             
157             options.params.paging = Ext.copyTo({}, options.params, 'sort,dir,start,limit');
158             
159             var filterToolbar = Ext.getCmp('activitiesFilterToolbar');
160             var filter = filterToolbar ? filterToolbar.getValue() : [];
161             filter.push(
162                 {field: 'record_model', operator: 'equals', value: this.record_model },
163                 {field: 'record_id', operator: 'equals', value: this.getRecordId() },
164                 {field: 'record_backend', operator: 'equals', value: 'Sql' }
165             );
166                         
167             options.params.filter = filter;
168         }, this);
169         
170         // add new notes from notes store
171         this.store.on('load', function (store, operation) {
172             var notesStore = Ext.StoreMgr.lookup('NotesStore');
173             if (notesStore) {
174                 notesStore.each(function (note) {
175                     if (!note.data.creation_time) {
176                         store.insert(0, note);
177                     }
178                 });
179             }
180         }, this);
181     },
182
183     /**
184      * @public
185      *
186      * @returns {string}
187      */
188     getRecordId: function() {
189         return (this.record_id) ? this.record_id : 0;
190     },
191
192     /**
193      * @private
194      */
195     initComponent: function () {
196         
197         // get translations
198         this.translation = new Locale.Gettext();
199         this.translation.textdomain('Tinebase');
200         
201         // translate / update title
202         this.title = this.translation.gettext('History');
203         
204         // get store
205         this.initStore();
206
207         // get grid
208         this.activitiesGrid = this.getActivitiesGrid();
209         
210         // the filter toolbar
211         var filterToolbar = new Tine.widgets.grid.FilterToolbar({
212             id : 'activitiesFilterToolbar',
213             filterModels: [
214                 {label: _('Quick Search'), field: 'query',         operators: ['contains']},
215                 //{label: this.translation._('Time'), field: 'creation_time', operators: ['contains']}
216                 {label: this.translation.gettext('Time'), field: 'creation_time', valueType: 'date', pastOnly: true}
217                 // user search is note working yet -> see NoteFilter.php
218                 //{label: this.translation._('User'), field: 'created_by', defaultOperator: 'contains'},
219                 // type search isn't implemented yet
220                 //{label: this.translation._('Type'), field: 'note_type_id', defaultOperator: 'contains'}
221             ],
222             defaultFilter: 'query',
223             filters: []
224         });
225         
226         filterToolbar.on('change', function () {
227             this.store.load({});
228         }, this);
229                                                 
230         this.items = [        
231             new Ext.Panel({
232                 layout: 'border',
233                 items: [{
234                     region: 'center',
235                     xtype: 'panel',
236                     layout: 'fit',
237                     border: false,
238                     items: this.activitiesGrid
239                 }, {
240                     region: 'north',
241                     border: false,
242                     items: filterToolbar,
243                     listeners: {
244                         scope: this,
245                         afterlayout: function (ct) {
246                             ct.suspendEvents();
247                             ct.setHeight(filterToolbar.getHeight());
248                             ct.ownerCt.layout.layout();
249                             ct.resumeEvents();
250                         }
251                     }
252                 }]
253             })
254         ];
255                 
256         // load store on activate
257         this.on('activate', function (panel) {
258             panel.store.load({});
259         });
260         
261         // no support for multiple edit
262         Tine.widgets.dialog.MultipleEditDialogPlugin.prototype.registerSkipItem(this);
263         
264         Tine.widgets.activities.ActivitiesTabPanel.superclass.initComponent.call(this);
265     }
266 });
267 Ext.reg('tineactivitiestabpanel', Tine.widgets.activities.ActivitiesTabPanel);
268
269 /************************* helper *********************************/
270
271 /**
272  * get note / activities types store
273  * if available, load data from initial data
274  *
275  * @return Ext.data.JsonStore with activities types
276  *
277  * @todo translate type names / descriptions
278  */
279 Tine.widgets.activities.getTypesStore = function () {
280     var store = Ext.StoreMgr.get('noteTypesStore');
281     if (!store) {
282         store = new Ext.data.JsonStore({
283             fields: Tine.Tinebase.Model.NoteType,
284             baseParams: {
285                 method: 'Tinebase.getNoteTypes'
286             },
287             root: 'results',
288             totalProperty: 'totalcount',
289             id: 'id',
290             remoteSort: false
291         });
292         /*if (Tine.Tinebase.registry.get('NoteTypes')) {
293             store.loadData(Tine.Tinebase.registry.get('NoteTypes'));
294         } else*/
295         if (Tine.Tinebase.registry.get('NoteTypes')) {
296             store.loadData(Tine.Tinebase.registry.get('NoteTypes'));
297         }
298         Ext.StoreMgr.add('noteTypesStore', store);
299     }
300
301     return store;
302 };
303
304 /**
305  * get type icon
306  *
307  * @param   id of the note type record
308  * @returns img tag with icon source
309  *
310  * @todo use icon_class here
311  */
312 Tine.widgets.activities.getTypeIcon = function (id) {
313     var typesStore = Tine.widgets.activities.getTypesStore();
314     var typeRecord = typesStore.getById(id);
315     if (typeRecord) {
316         return '<img src="' + typeRecord.data.icon + '" ext:qtip="' + Tine.Tinebase.common.doubleEncode(typeRecord.data.description) + '"/>';
317     } else {
318         return '';
319     }
320 };