/**
* @class Zarafa.core.Container
* @extends Object
*
* Container class for lazy instantiation of globally used objects such as a Request instance, HierarchyStore instance, etc.
* The Container is also used to register plugins and populate insertion points.
* <p>
* Don't instantiate the a new Container object yourself, just use the global <code>container</code> instance.
*/
Zarafa.core.Container = Ext.extend(Ext.util.Observable, {
/**
* List of registered {@link Zarafa.core.Context context instances}.
* @property
* @private
* @type Array
*/
contexts : undefined,
/**
* The Meta Data for all registered {@link #contexts}. This is an array
* of {@link Zarafa.core.ContextMetaData ContextMetaData instances}.
* @property
* @private
* @type Array
*/
contextsMetaData : undefined,
/**
* List of registered {@link Zarafa.core.Plugin plugin instances}
* (also includes {@link #contexts}).
* @property
* @private
* @type Array
*/
plugins : undefined,
/**
* The Meta Data for all registered {@link #plugins}. This is an array
* of {@link Zarafa.core.PluginMetaData PluginMetaData instances}.
* @property
* @private
* @type Array
*/
pluginsMetaData : undefined,
/**
* The Meta Data for all registered {@link Zarafa.core.ui.widget.Widget widgets}. This is an array
* of {@link Zarafa.core.ui.widget.WidgetMetaData WidgetMetaData instances}.
* @property
* @private
* @type Array
*/
widgetsMetaData : undefined,
/**
* @constructor
*/
constructor : function()
{
this.addEvents([
/**
* @event contextswitch fired right before the context switch is made
* @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder folder that is loaded for the new context
* @param {Zarafa.core.Context} oldContext context being switched out
* @param {Zarafa.core.Context} newContext new context being switched in
* @return {Boolean} False to prevent the context from being switched
*/
'beforecontextswitch',
/**
* @event contextswitch fired after a context switch has been performed
* @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder folder that is loaded for the new context
* @param {Zarafa.core.Context} oldContext context that was switched out
* @param {Zarafa.core.Context} newContext new context that was switched
*/
'contextswitch',
/**
* @event contextswitch fired after a context switch has been performed and after the contextswitch has been fired.
* @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder folder that is loaded for the new context
* @param {Zarafa.core.Context} oldContext context that was switched out
* @param {Zarafa.core.Context} newContext new context that was switched
*/
'aftercontextswitch',
/**
* Fires when the user selects a folder from the hierarchy.
* @event folderselect
* @param {Zarafa.hierarchy.data.MAPIFolderRecord[]} folder MAPI folder object.
*/
'folderselect',
/**
* Fires when the webapp has loaded and the loading mask has been removed.
* @event webapploaded
*/
'webapploaded',
/**
* Fires when the webapp will reload. Return false from an event handler to cancel the reload.
* @event beforewebappreload
*/
'beforewebappreload',
/**
* Fires before the user logs out from the webapp. Return false from an event handler to stop the show.
* @event beforelogout
*/
'beforelogout',
/**
* Fires when the user logs out from the webapp.
* @event logout
*/
'logout'
]);
Zarafa.core.Container.superclass.constructor.call(this);
// initialize properties
this.plugins = [];
this.pluginsMetaData = [];
this.contexts = [];
this.contextsMetaData = [];
this.widgetsMetaData = [];
},
/**
* Logout from the Webapp. this will fire the {@link #beforelogout} and
* {@link #logout} events before calling {@link #doLogout}.
* @param {Boolean} preserveUser True to preserve the username when he
* is forwarded to the logon page
* @param {Boolean} preserveSession True to preserve the existing session
* on the server and only redirect the user to the logon page.
*/
logout : function(preserveUser, preserveSession)
{
if (this.fireEvent('beforelogout') !== false) {
this.fireEvent('logout');
this.doLogout(preserveUser, preserveSession);
}
},
/**
* Logout from the Webapp (this function is called by {@link #logout}.
* Override this to change the logout method.
* @param {Boolean} preserveUser True to preserve the username when he
* is forwarded to the logon page
* @param {Boolean} preserveSession True to preserve the existing session
* on the server and only redirect the user to the logon page.
* @protected
*/
doLogout : function(preserveUser, preserveSession)
{
var user = ((preserveUser === true) ? ('&user=' + this.getUser().getUserName()) : '');
Zarafa.core.Util.disableLeaveRequester();
if (preserveSession !== true) {
window.location = 'index.php?logout' + user;
} else {
window.location = 'index.php?load=logon' + user;
}
},
/**
* Obtain the server configuration data
* @return {Zarafa.core.data.ServerConfig} The server configuration data
*/
getServerConfig : function()
{
return this.serverConfigRecord;
},
/**
* Set the server configuration data
* @param {Object} serverData The server configuration data.
*/
setServerConfig : function(serverData)
{
this.serverConfigRecord = new Zarafa.core.data.ServerConfig(serverData);
},
/**
* Obtain the user data for the currently logged in user.
* @return {Zarafa.core.data.User} The user data of the currently logged in user.
*/
getUser : function()
{
return this.userRecord;
},
/**
* Set the user data for the currently logged in user.
* @param {Object} userData The user data of the currently logged in user.
*/
setUser : function(userData)
{
this.userRecord = new Zarafa.core.data.User(userData);
},
/**
* Obtain the versioning data for the WebApp environment
* @return {Zarafa.core.data.Version} The version data of the WebApp environment
*/
getVersion : function()
{
return this.versionRecord;
},
/**
* Set the version data for the WebApp environment
* @param {Object} versionData The version data of the WebApp environment
*/
setVersion : function(versionData)
{
this.versionRecord = new Zarafa.core.data.Version(versionData);
},
/**
* Obtain the array of all available languages. Each item in the array
* contains 2 keys. The first key is 'lang' which is the language code
* (e.g. en_GB), and the second key is 'name' which is the display name.
* @return {Array} The array of available languages
*/
getLanguages : function()
{
return this.languages;
},
/**
* Set the languages which are available to the user
* @param {Array} languages The available languages
*/
setLanguages : function(languages)
{
this.languages = languages;
},
/**
* Returns the currently active {@link Zarafa.core.Context context}.
* @return {Zarafa.core.Context} the currently active context.
*/
getCurrentContext : function()
{
return this.currentContext || this.getContextByName('default');
},
/**
* Returns the global {@link Zarafa.core.Request request} instance.
* All server requests should be lodged through this instance.
*
* @return {Zarafa.core.Request} the global {@link Zarafa.core.Request Request} instance.
*/
getRequest : function()
{
return this.request || (this.request = new Zarafa.core.Request({ url:"kopano.php" }));
},
/**
* Returns the global {@link Zarafa.core.ResponseRouter ResponseRouter} instance.
* All server responses are lodged through this instance.
*
* @return {Zarafa.core.ResponseRouter} the global {@link Zarafa.core.ResponseRouter ResponseRouter} instance.
*/
getResponseRouter : function()
{
return this.responseRouter || (this.responseRouter = new Zarafa.core.ResponseRouter());
},
/**
* Returns the global {@link Zarafa.core.data.NotificationResolver NotificationResolver} instance.
* All notifications are being resolved through this instance, the constructed
* {@link Zarafa.core.data.AbstractResponseHandler ResponseHandler} is then returned to the
* {@link Zarafa.core.ResponseRouter ResponseRouter} for further processing.
*
* @return {Zarafa.core.data.NotificationResolver} the global {@link Zarafa.core.data.NotificationResolver NotificationResolver} instance.
*/
getNotificationResolver : function()
{
return this.notificationResolver || (this.notificationResolver = new Zarafa.core.data.NotificationResolver());
},
/**
* Returns the global {@link Zarafa.hierarchy.data.HierarchyStore HierarchyStore} instance.
* @return {Zarafa.hierarchy.data.HierarchyStore} the global {@link Zarafa.hierarchy.data.HierarchyStore HierarchyStore} instance.
*/
getHierarchyStore : function()
{
return this.hierarchyStore || (this.hierarchyStore = new Zarafa.hierarchy.data.HierarchyStore());
},
/**
* Returns the global {@link Zarafa.settings.SettingsModel SettingsModel} instance.
* @return {Zarafa.settings.SettingsModel} the global {@link Zarafa.settings.SettingsModel SettingsModel} instance.
*/
getSettingsModel : function()
{
return this.settingsModel || (this.settingsModel = new Zarafa.settings.SettingsModel());
},
/**
* Returns the global {@link Zarafa.settings.PersistentSettingsModel PersistentSettingsModel} instance.
* @return {Zarafa.settings.PersistentSettingsModel} the global
* {@link Zarafa.settings.PersistentSettingsModel PersistentSettingsModel} instance.
*/
getPersistentSettingsModel : function()
{
return this.persistentSettingsModel || (this.persistentSettingsModel = new Zarafa.settings.PersistentSettingsModel());
},
/**
* Returns the {@link Zarafa.core.data.ShadowStore ShadowStore} instance.
* @return {Zarafa.core.data.ShadowStore} the {@link Zarafa.core.data.ShadowStore ShadowStore} instance.
*/
getShadowStore : function()
{
return this.shadowStore || (this.shadowStore = new Zarafa.core.data.ShadowStore());
},
/**
* Returns the {@link Zarafa.core.ui.notifier.Notifier Notifier} instance which can be used
* for sending notifications to the user.
* @return {Zarafa.core.ui.notifier.Notifier} The notifier for User notifications
*/
getNotifier : function()
{
return this.notifier || (this.notifier = new Zarafa.core.ui.notifier.Notifier());
},
/**
* Returns the application main panel.
* @return {Zarafa.core.ui.MainViewport} the application main panel.
*/
getMainPanel : function()
{
return this.mainPanel || (this.mainPanel = new Zarafa.core.ui.MainViewport());
},
/**
* Resturns the applications main toolbar
* @return {Zarafa.core.ui.MainToolbar} then application main tool bar
*/
getMainToolbar : function()
{
return this.getMainPanel().mainToolbar;
},
/**
* Returns the application welcome panel.
* @return {Zarafa.core.ui.WelcomeViewport} the application welcome panel.
*/
getWelcomePanel : function()
{
return this.welcomePanel || (this.welcomePanel = new Zarafa.core.ui.WelcomeViewport());
},
/**
* Returns the application tab panel
* @return {Zarafa.core.ui.ContextContainer} The application tab panel
*/
getTabPanel : function()
{
return this.getMainPanel().getContentPanel();
},
/**
* Returns the application content panel
* @return {Zarafa.common.ui.ContextMainPanel} the application content panel.
*/
getContentPanel : function()
{
return this.getTabPanel().get(0).getActiveItem();
},
/**
* Returns the application navigation sidebar.
* @return {Zarafa.core.ui.NavigationPanel} the navigation sidebar
*/
getNavigationBar : function()
{
return this.getMainPanel().getNavigationPanel();
},
* Returns the application widget sidebar.
* @return {Zarafa.core.ui.widget.WidgetPanel} the application widget sidebar.
*/
getWidgetSideBar : function()
{
return this.getMainPanel().getWidgetPanel();
},
/**
* Returns an array of all registered {@link Zarafa.core.Plugin plugins}.
* @return {Array} plugins
*/
getPlugins : function()
{
return this.plugins;
},
/**
* Returns the Meta Data for {@link #pluginsMetaData all registered} {@link Zarafa.core.Plugin plugins}.
* @return {Array} The plugins meta data
*/
getPluginsMetaData : function()
{
return this.pluginsMetaData;
},
/**
* Returns an array of all registered {@link Zarafa.core.Context contexts}.
* @return {Array} Contexts
*/
getContexts : function()
{
return this.contexts;
},
/**
* Returns the Meta Data for {@link #contextsMetaData all registered} {@link Zarafa.core.Context contexts}.
* @return {Array} The contexts meta data
*/
getContextsMetaData : function()
{
return this.contextsMetaData;
},
/**
* Returns the Meta Data for {@link #widgetsMetaData all registered} {@link Zarafa.core.ui.widget.Widget widgets}.
* @return {Array} The widgets meta data
*/
getWidgetsMetaData : function()
{
return this.widgetsMetaData;
},
/**
* Returns the context that matches the supplied name.
* @param {String} name The name of the context which is requested
* @return {Zarafa.core.Context} matching context or <code>undefined</code> if not found.
*/
getContextByName : function(name)
{
var contexts = this.getContexts();
for (var index = 0, len = contexts.length; index < len; index++) {
if (contexts[index].getName() === name) {
return contexts[index];
}
}
},
/**
* Returns the Context Meta Data that matches the supplied name.
* @param {String} name The name of the context for which the meta data is requested
* @return {Zarafa.core.ContextMetaData} The Meta Data for the context or <code>undefined</code> if not found.
*/
getContextMetaDataByName : function(name)
{
var contexts = this.getContextsMetaData();
for (var index = 0, len = contexts.length; index < len; index++) {
if (contexts[index].getName() === name) {
return contexts[index];
}
}
},
/**
* Returns the plug-in that matches the supplied name.
* @param {String} name The name of the plugin which is requested
* @return {Zarafa.core.Plugin} matching plug-in or <code>undefined</code> if not found.
*/
getPluginByName : function(name)
{
var plugins = this.getPlugins();
for (var index = 0, len = plugins.length; index < len; index++) {
if (plugins[index].getName() === name) {
return plugins[index];
}
}
},
/**
* Returns the Plugin Meta Data that matches the supplied name.
* @param {String} name The name of the plugin for which the meta data is requested
* @return {Zarafa.core.PluginMetaData} The Meta Data for the plugin or <code>undefined</code> if not found.
*/
getPluginMetaDataByName : function(name)
{
var plugins = this.getPluginsMetaData();
for (var index = 0, len = plugins.length; index < len; index++) {
if (plugins[index].getName() === name) {
return plugins[index];
}
}
},
/**
* Returns the Widget Meta Data that matches the supplied name.
* @param {String} name The name of the widget for which the meta data is requested
* @return {Zarafa.core.ui.widget.WidgetMetaData} The Meta Data for the widget or <code>undefined</code> if not found.
*/
getWidgetMetaDataByName : function(name)
{
var widgets = this.getWidgetsMetaData();
for (var index = 0, len = widgets.length; index < len; index++) {
if (widgets[index].getName() === name) {
return widgets[index];
}
}
},
/**
* Queries registered plug-ins in for components and returns the gathered results.
* @param {String} insertionPoint name of the insertion point
* @param {Object} args (optional) optional arguments such as scope
* @return {Ext.Component[]} an array of components
*/
populateInsertionPoint : function(insertionPoint)
{
var plugins = this.getPlugins();
var items = [];
// convert arguments object to a real array
var args = Ext.toArray(arguments);
for (var i = 0, len = plugins.length; i < len; i++) {
var plugin = plugins[i];
var components = plugin.getComponents.apply(plugin, args);
// FIXME: Why do we need to assign the plugin to the component?
Ext.each(components, function(component) {
component.plugin = plugin;
items.push(component);
});
}
// every plugin will give items in their own array so we need to merge all arrays
// this will not interfere with objects
return Ext.flatten(items);
},
/**
* Registers a Context Meta Data instance with the container.
* @param {Zarafa.core.ContextMetaData} info context to register
*/
registerContext : function(info)
{
this.getContextsMetaData().push(info);
if (info.isEnabled()) {
this.getContexts().push(info.getInstance());
}
// A Context is also a plugin, so register it
// as such as well.
this.registerPlugin(info);
},
/**
* Registers a Plugin Meta Data instance with the container.
* @param {Zarafa.core.PluginMetaData} info plugin info to register
*/
registerPlugin : function(info)
{
// Get the list of plugins that are always enabled
var alwaysEnabledPlugins = this.getServerConfig().getAlwaysEnabledPluginsList().split(';');
if ( alwaysEnabledPlugins.indexOf(info.name)>=0 ){
info.allowUserDisable = false;
info.enable = true;
}
this.getPluginsMetaData().push(info);
if (info.isEnabled()) {
this.getPlugins().push(info.getInstance());
}
},
/**
* Registers a Widget Meta Data instance with the container.
* @param {Zarafa.core.ui.widget.WidgetMetaData} info widget meta data to register
*/
registerWidget : function(info)
{
this.getWidgetsMetaData().push(info);
},
/**
* Performs a context switch by switching out the current context and switching in the new one.
* @param {Zarafa.core.Context} context context to switch to.
* @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder folder that should be shown by the selected context.
* @param {Boolean} suspended True if the {@link Zarafa.core.ContextModel model} for the
* {@link Zarafa.core.Context context} should be enabled {@link Zarafa.core.ContextModel#suspendLoading suspended}.
* @private
*/
switchContext : function(context, folder, suspended)
{
var oldContext = this.getCurrentContext();
if (oldContext !== context && this.fireEvent('beforecontextswitch', folder, oldContext, context) !== false) {
if (oldContext) {
oldContext.disable();
var oldModel = oldContext.getModel();
if (oldModel) {
oldModel.un('folderchange', this.onContextFolderChange, this);
}
}
context.enable(folder, suspended);
var newModel = context.getModel();
if (newModel) {
newModel.on('folderchange', this.onContextFolderChange, this);
}
this.currentContext = context;
this.fireEvent('folderselect', folder);
this.fireEvent('contextswitch', folder, oldContext, context);
// Nothing needs to be done between 'contextswitch' and 'aftercontextswitch',
// the difference between the two events is that the first one can be used
// internally for building up the UI, while the latter event is ideal for
// plugins which want the UI components to be setup correctly.
this.fireEvent('aftercontextswitch', folder, oldContext, context);
}
},
/**
* The container will start a bidding round to determine which context should be chosen to
* display the given folder.
* @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder folder for which we need to find corresponding context.
* @return {Zarafa.core.Context} context that should be used to load data from {@link Zarafa.hierarchy.data.MAPIFolderRecord MAPIFolder}.
*/
getContextByFolder : function(folder)
{
// walk over the context list and select the one that provides the highest bid
var selectedContext;
var highestBid;
var contexts = this.getContexts();
if (contexts) {
for(var index = 0, len = contexts.length; index < len; index++) {
var context = contexts[index];
var bid = context.bid(folder);
if (highestBid === undefined || bid > highestBid) {
highestBid = bid;
selectedContext = context;
}
}
}
return selectedContext;
},
/**
* Select a specific folder in the UI. The container will start a bidding round to determine which context should be chosen to
* display the given folder. The current context is then disabled and switched out, and the newly chosen context is enabled and
* switched in. Fires the 'folderselect' event.
* @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder folder to select.
*/
selectFolder : function(folder)
{
var selectedContext = this.getContextByFolder(folder);
// Check if a new context has been selected, if we can
// stay with the current context, then simply update
// the folders.
if (this.getCurrentContext() !== selectedContext) {
this.switchContext(selectedContext, folder);
} else {
var model = selectedContext.getModel();
if (model) {
model.setFolders(folder);
}
}
},
/**
* Helps in reloading a context. It checks if given changed folder is holded by current context
* then re-enables context all of its folders.
* @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder which is changed
*/
reloadContext : function(folder)
{
var currentContext = this.getCurrentContext();
var contextModel = currentContext.getModel();
if (!Ext.isDefined(contextModel)) {
return;
}
folder = contextModel.getFolder(folder.get('entryid'));
if (Ext.isDefined(folder)) {
var allFolders = contextModel.getFolders();
currentContext.disable();
currentContext.enable(allFolders);
}
},
/**
* Event handler which is fired when the {@link #getCurrentContext current context}
* {@link Zarafa.core.ContextModel model} fires the {@link Zarafa.core.ContextModel#folderchange} event.
* This will redirect the event and fire the {@link #folderselect} event.
* @param {Zarafa.core.ContextModel} model The model which fired the event
* @param {Zarafa.hierarchy.data.MAPIFolderRecord[]} folders The folders which are
* currently selected
* @private
*/
onContextFolderChange : function(model, folders)
{
this.fireEvent('folderselect', folders);
},
/**
* Starts a bidding round to determine what plug-in should be chosen to
* deliver the component that is requested. The component of the highest
* bidder is returned. If the supplied type is undefined the function will
* return undefined as well.
* @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
* @param {Ext.data.Record} record (optional) Passed record.
* @return {Ext.Component} Component
*
*/
getSharedComponent: function(type, record)
{
// walk over the context list and select the one that provides the highest bid
var selectedPlugin;
var highestBid;
var plugins = container.getPlugins();
var component;
if (type) {
for (var i = 0, len = plugins.length; i < len; i++) {
var plugin = plugins[i];
var bid = plugin.bidSharedComponent(type, record);
if (highestBid === undefined || bid > highestBid) {
highestBid = bid;
selectedPlugin = plugin;
}
}
if (selectedPlugin && highestBid >= 0) {
component = selectedPlugin.getSharedComponent(type, record);
}
}
return component;
},
/**
* Returns the global {@link Zarafa.common.reminder.data.ReminderStore RemimderStore} instance.
* @return {Zarafa.common.reminder.data.ReminderStore} instance.
*/
getReminderStore : function()
{
return this.reminderStore || (this.reminderStore = new Zarafa.common.reminder.data.ReminderStore());
},
/**
* Returns base url of webapp without trailing slash, which can be used to request resources from server.
* @return {String} base url of webapp.
*/
getBaseURL : function()
{
var loc = window.location;
var url = loc.protocol + '//' + loc.host + loc.pathname;
// it's possible that webapp is loaded without index.php in url, so if it the case
// then append index.php in url so that will make our base url complete
if(url.indexOf('index.php') === -1) {
url += 'index.php';
}
return url;
},
/**
* Returns base path of webapp with trailing slash, which can be used to request resources from server.
* @return {String} base path of webapp.
*/
getBasePath : function()
{
var baseURL = container.getBaseURL();
return baseURL.substring(0, baseURL.lastIndexOf('index.php'));
}
});