Merge branch '2013.03'
[tine20] / tine20 / Tinebase / js / widgets / mainscreen / WestPanel.js
1 /*
2  * Tine 2.0
3  * 
4  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
5  * @author      Cornelius Weiss <c.weiss@metaways.de>
6  * @copyright   Copyright (c) 2007-2013 Metaways Infosystems GmbH (http://www.metaways.de)
7  */
8 Ext.ns('Tine.widgets.mainscreen');
9
10 /**
11  * @namespace   Tine.widgets.mainscreen
12  * @class       Tine.widgets.mainscreen.WestPanel
13  * @extends     Ext.ux.Portal
14  * 
15  * @author      Cornelius Weiss <c.weiss@metaways.de>
16  * 
17  * @todo make save button working again -> move to here
18  * 
19  * @constructor
20  * @xtype       tine.widgets.mainscreen.westpanel
21  */
22 Tine.widgets.mainscreen.WestPanel = function(config) {
23     Ext.apply(this, config);
24     
25     this.defaults = {};
26     
27     Tine.widgets.mainscreen.WestPanel.superclass.constructor.apply(this, arguments);
28 };
29
30 Ext.extend(Tine.widgets.mainscreen.WestPanel, Ext.ux.Portal, {
31     
32     /**
33      * @cfg {Array} contentTypes
34      * Array of Objects
35      * Object Properties: "model", "requiredRight"
36      * Prop. model (e.g. "Abc") will be expanded to Tine.Application.Model.Abc
37      * Prop. requiredRight (e.g. "read") will be expanded to Tine.Tinebase.common.hasRight('read', this.app.appName, recordClass.getMeta('recordsName').toLowerCase())
38      * Prop. singularContainerMode (bool): when true, the records of the model doesn't have containers, so no containertreepanel is rendered
39      *                                     but a containertreenode must be defined in Application.js
40      * Prop. genericCtxActions (Array of Strings e.g. "['rename','grant']") Creates a Tine.widgets.tree.ContextMenu with the actions in this array                                    
41      */
42     contentTypes: null,
43
44     /**
45      * @cfg {String} contentType
46      * defines the contentType (e.g. Xyz), which will be expanded to Tine.Application.Model.Xyz, this panel controls 
47      */
48     contentType: null,
49     
50     /**
51      * @cfg {String} containerTreeClass
52      * name of container tree class in namespace of this app (defaults to TreePanel)
53      * the class name will be expanded to Tine[this.appName][this.containerTreePanelClassName]
54      */
55     containerTreePanelClassName: 'TreePanel',
56     
57     /**
58      * @cfg {String} favoritesPanelClassName
59      * name of favorites class in namespace of this app (defaults to FilterPanel)
60      * the class name will be expanded to Tine[this.appName][this.favoritesPanelClassName]
61      */
62     favoritesPanelClassName: 'FilterPanel',
63     
64     /**
65      * @cfg {Bool} hasContentTypeTreePanel
66      * west panel has modulePanel (defaults to null -> autodetection)
67      */
68     hasContentTypeTreePanel: null,
69     
70     /**
71      * @cfg {Bool} hasContainerTreePanel
72      * west panel has containerTreePanel (defaults to null -> autodetection)
73      */
74     hasContainerTreePanel: null,
75     
76     /**
77      * @cfg {Bool} hasFavoritesPanel
78      * west panel has favorites panel (defaults to null -> autodetection)
79      */
80     hasFavoritesPanel: null,
81     
82     layout: 'column',
83     cls : 'x-portal',
84     defaultType : 'portalcolumn',
85     autoHeight: true,
86     border: false,
87     stateful: true,
88     stateEvents: ['collapse', 'expand', 'drop'],
89
90     /**
91      * inits this west panel
92      */
93     initComponent: function() {
94         this.stateId = this.app.appName + this.getContentType() + '-mainscreen-westpanel';
95         var fpcn = this.getContentType() + this.favoritesPanelClassName;
96         this.hasFavoritesPanel = Ext.isBoolean(this.hasFavoritesPanel) ? this.hasFavoritesPanel : !! Tine[this.app.appName][fpcn];
97         this.hasContentTypeTreePanel = Ext.isArray(this.contentTypes) && this.contentTypes.length > 1;
98         
99         if (this.hasContainerTreePanel === null) {
100             this.hasContainerTreePanel = true;
101             if (this.contentTypes) {
102                 Ext.each(this.contentTypes, function(ct) {
103                     if ((ct.hasOwnProperty('modelName') && ct.modelName == this.contentType) && (ct.singularContainerMode)) {
104                         this.hasContainerTreePanel = false;
105                         return false;
106                     }
107                 }, this);
108             }
109         }
110         
111         this.items = this.getPortalColumn();
112         Tine.widgets.mainscreen.WestPanel.superclass.initComponent.apply(this, arguments);
113     },
114     
115     /**
116      * called after rendering process
117      */
118     afterRender: function() {
119         Tine.widgets.mainscreen.WestPanel.superclass.afterRender.apply(this, arguments);
120         
121         this.getPortalColumn().items.each(function(item, idx) {
122             // kill x-scrollers
123             if (item.getEl && item.getEl()) {
124                 this.xScrollKiller(item);
125             } else {
126                 item.on('afterrender', this.xScrollKiller, this);
127             }
128             
129             //bubble state events
130             item.enableBubble(['collapse', 'expand', 'selectionchange']);
131         }, this);
132     },
133     
134     /**
135      * initializes the stateif no state is saved
136      * overwrites the Ext.Component initState method
137      */
138     initState: function() {
139         var state = Ext.state.Manager.get(this.stateId) ? Ext.state.Manager.get(this.stateId) : this.getState();
140         
141         if(this.fireEvent('beforestaterestore', this, state) !== false){
142             this.applyState(Ext.apply({}, state));
143             this.fireEvent('staterestore', this, state);
144         }
145     },
146     
147     /**
148      * applies state to cmp
149      * 
150      * @param {Object} state
151      */
152     applyState: function(state) {
153         var collection = this.getPortalColumn().items,
154             c = new Array(collection.getCount()), k = collection.keys, items = collection.items;
155         
156         Ext.each(state.order, function(position, idx) {
157             c[idx] = {key: k[position], value: items[position], index: position};
158         }, this);
159         
160         for(var i = 0, len = collection.length; i < len; i++){
161             if (c[i] && c[i].value) {
162                 items[i] = c[i].value;
163                 k[i] = c[i].key;
164             }
165         }
166         collection.fireEvent('sort', collection);
167         
168         collection.each(function(item, idx) {
169             if ((! item) || typeof item.addFill === 'function') return;
170             if (item.getEl()) {
171                 item[state.collapsed[idx] ? 'collapse' : 'expand'](false);
172             } else {
173                 item.collapsed = !!state.collapsed[idx];
174             }
175         }, this);
176         
177     },
178     
179     /**
180      * returns additional items for the westpanel
181      * template fn to be overrwiten by subclasses
182      * 
183      * @return {Array} of Ext.Panel
184      */
185     getAdditionalItems: function() {
186         return this.additionalItems || [];
187     },
188
189     getContentType: function() {
190         return (this.contentType) ? this.contentType : '';
191     },
192     
193     /**
194      * returns containerTree panel
195      * 
196      * @return {Tine.Tinebase.widgets.ContainerTreePanel}
197      */
198     getContainerTreePanel: function() {
199         var panelName = this.app.getMainScreen().getActiveContentType() + 'TreePanel';
200         if (! this[panelName]) {
201             if (Tine[this.app.appName].hasOwnProperty(panelName)) {
202                 this[panelName] = new Tine[this.app.appName][panelName]({app: this.app});
203             } else {
204                 this[panelName] = new Tine.widgets.persistentfilter.PickerPanel({app: this.app});
205             }
206             this[panelName].on('click', function (node, event) {
207                 if(node != this.lastClickedNode) {
208                     this.lastClickedNode = node;
209                     this.fireEvent('selectionchange');
210                 }
211             });
212         }
213         
214         return this[panelName];
215
216     },
217     
218     /**
219      * returns favorites panel
220      * 
221      * @return {Ext.Panel}
222      */
223     getFavoritesPanel: function() {
224         var ct = this.app.getMainScreen().getActiveContentType(),
225             panelName = ct + 'FilterPanel';
226         
227         try {
228             if(!this[panelName]) {
229                 this[panelName] = new Tine[this.app.appName][panelName]({
230                     
231                     rootVisible : false,
232                     border : false,
233                     collapsible : true,
234                 
235                     root: null,
236                     
237                     titleCollapse: true,
238                     title: '',
239                     baseCls: 'ux-arrowcollapse',
240                     
241                     app: this.app,
242                     contentType: ct,
243
244                     style: {
245                         width: '100%',
246                         overflow: 'hidden'
247                     },
248                     
249                     treePanel: (this.hasContainerTreePanel) ? this.getContainerTreePanel() : null,
250                     listeners: {
251                         scope: this,
252                         click: function (node, event) {
253                             if(node != this.lastClickedNode) {
254                                 this.lastClickedNode = node;
255                                 this.fireEvent('selectionchange');
256                             }
257                         }
258                     }
259                 });
260             }
261         } catch(e) {
262             Tine.log.info('No Favorites Panel created');
263         }
264         
265         return this[panelName];
266     },
267     
268     /**
269      * returns filter plugin of west panel for given content type
270      * 
271      * @param {String} contentType
272      * @return {Tine.widgets.grid.FilterPlugin}
273      */
274     getFilterPlugin: function(contentType) {
275         if (this.hasContainerTreePanel) {
276             return this.getContainerTreePanel().getFilterPlugin();
277         } else {
278             return new Tine.widgets.grid.FilterPlugin({
279                 getValue: function() {
280                     return [
281                     ];
282                 }
283             });
284         }
285     },
286     
287     /**
288      * returns the one and only portalcolumn of this west panel
289      * 
290      * @return {Ext.ux.PortalColumn}
291      */
292     getPortalColumn: function() {
293         if (! this.portalColumn) {
294
295             var items = [];
296             
297             if (this.hasContainerTreePanel) {
298                 var containerTreePanel = this.getContainerTreePanel();
299                 
300                 var containersName = containerTreePanel.recordClass
301                     ? containerTreePanel.recordClass.getContainersName()
302                     : _('containers');
303                 
304                 // recheck if container tree is a container tree as in apps not dealing
305                 // with containers we don't want a collapsed arrow header
306                 var isContainerTreePanel = typeof containerTreePanel.selectContainerPath === 'function';
307                 
308                 if (isContainerTreePanel) {
309                     this.defaults = {
310                         collapsible: true,
311                         baseCls: 'ux-arrowcollapse',
312                         animCollapse: true,
313                         titleCollapse:true,
314                         draggable : true,
315                         autoScroll: false
316                     };
317                 }
318                 
319                 items.push(Ext.apply(this.getContainerTreePanel(), {
320                     title: isContainerTreePanel ? containersName : false,
321                     collapsed: isContainerTreePanel
322                 }, this.defaults));
323                 
324             }
325             
326             if (this.hasFavoritesPanel) {
327                 // favorites panel
328                 items.unshift(Ext.apply(this.getFavoritesPanel(), {
329                     title: _('Favorites')
330                 }, this.defaults));
331             }
332             
333             items = items.concat(this.getAdditionalItems());
334
335             // save original/programatical position
336             // NOTE: this has to be done before applyState!
337             if (items.length) {
338                 Ext.each(items, function(item, idx) {
339                     item.startPosition = idx;
340                 }, this);
341             }
342             this.portalColumn = new Ext.ux.PortalColumn({
343                 columnWidth: 1,
344                 items: items
345             });
346             
347             this.portalColumn.on('resize', function(cmp, width) {
348                 this.portalColumn.items.each(function(item) {
349                     item.setWidth(width);
350                 }, this);
351             }, this);
352         }
353         
354         return this.portalColumn;
355     },
356     
357     /**
358      * gets state of this cmp
359      */
360     getState: function() {
361         var state = {
362             order: [],
363             collapsed: []
364         };
365         
366         this.getPortalColumn().items.each(function(item, idx) {
367             state.order.push(item.startPosition);
368             state.collapsed.push(!!item.collapsed);
369         }, this);
370         
371         return state;
372     },
373     
374     /**
375      * kill x scrollers of given component
376      * 
377      * @param {Ext.Component} cmp
378      */
379     xScrollKiller:  function(cmp) {
380         var panelEls = cmp.getEl().child('div[class^=' + this.defaults.baseCls + '-body]');
381         if (panelEls) {
382             panelEls.applyStyles('overflow-x: hidden');
383         }
384     }
385 });
386
387 Ext.reg('tine.widgets.mainscreen.westpanel', Tine.widgets.mainscreen.WestPanel);