Ext.namespace('Zarafa'); /** * @class Zarafa * Global convenience methods. * @singleton * #core */ Ext.apply(Zarafa, { /** * Ready flag which indicates that Webapp has been loaded. * (See {@link #onReady}). * @property * @type Boolean */ isReady : false, /** * Registration object for {@link #onReady} onto which all event * handlers are being registered which want to be notified when * WebApp has been intialized and ready for plugin interaction. * * @property * @type Ext.util.Event * @private */ readyEvent : new Ext.util.Event(), /** * Ready flag which indicates that Webapp UI has been loaded. * (See {@link #onUIReady}). * @property * @type Boolean */ uiReady : false, /** * Registration object for {@link #uiReady} onto which all event * handlers are being registered which want to be notified when * WebApp has drawn the main UI and has loaded the hierarchy panel. * * @property * @type Ext.util.Event * @private */ uiReadyEvent : new Ext.util.Event(), /** * The time that the user has not done any action * (like mousemove, click, or keypress) in the WebApp. * * @property * @type Integer * @private */ idleTime : 0, /** * True if the Wingdings font is installed on the system of the user, false * otherwise * @property * @type {Boolean} */ wingdingsInstalled : false, /** * True if the user is running DeskApp to view WebApp, false otherwise. * * @property * @type {Boolean} */ isDeskApp : Ext.isDefined(window.nw), /** * Adds a listener to be notified when WebApp is ready. This will be somewhere during {@link Ext.onReady}, when * WebApp has initialized the bare essentials. When the event is fired, the {@link Zarafa.core.Container} will * be available, and plugins are allowed to register. * * @param {Function} fn The method the event invokes. * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window. * @param {Boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options * <code>{single: true}</code> be used so that the handler is removed on first invocation. */ onReady : function(fn, scope, options) { this.readyEvent.addListener(fn, scope, options); // If the environment is already ready, can // should call fireReady again to fire the // just registered event. if (this.isReady) { this.fireReady(); } }, /** * Called when {@link Ext.onReady} has been invoked, and WebApp has been initialized. * All handlers registered through {@link #onReady} will now be fired and {@link #isReady} * will be set. * * @private */ fireReady : function() { this.isReady = true; this.readyEvent.fire(); this.readyEvent.clearListeners(); }, /** * Adds a listener to be notified when WebApp UI is drawn and the hierarchy is loaded. * @param {Function} fn The method the event invokes. * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window. * @param {Boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options * <code>{single: true}</code> be used so that the handler is removed on first invocation. */ onUIReady : function(fn, scope, options) { // Force single is true for events. options = options || {}; options.single = true; this.uiReadyEvent.addListener(fn, scope, options); // If the environment is already ready, call fireUIReady again // to fire the just registered event. if (this.uiReady) { this.fireUIReady(); } }, /** * Called when WebApp's UI has been loaded and the hiearchy is loaded. * All handlers registered through {@link #onUIReady} will now be fired and {@link #uiReady} * will be set. * * @private */ fireUIReady : function() { this.uiReady = true; this.uiReadyEvent.fire(); this.uiReadyEvent.clearListeners(); }, /** * Initialize all Global variables as used by the WebApp. * * This will utilize some global objects as received by the PHP * side, and apply them into the proper classes, after which the * global objects will be destroyed. * * This will instantiate {@link Zarafa.core.Container container}. * @private */ initializeGlobals : function() { // Use native json handling of browser for performance benefit Ext.USE_NATIVE_JSON = true; //show confirm dialog before user leave the page. Zarafa.core.Util.enableLeaveRequester(); // When the browser is unloading, all active requests will be aborted and // If more than one browser windows are open then close all browser windows. window.onunload = function () { if(Zarafa.core.BrowserWindowMgr.browserWindows.length > 1){ Zarafa.core.BrowserWindowMgr.closeAllBrowserWindow(); } container.getRequest().paralyze(Zarafa.core.data.ParalyzeReason.BROWSER_RELOADING); }; // Create global container object /*jshint -W020 */ /* Ignore global read-only warning. */ container = new Zarafa.core.Container(); // Set the server object /*jshint -W051 */ /* Ignore variables should not be deleted warning. */ container.setServerConfig(serverconfig); delete serverconfig; // Load all settings /*jshint -W051 */ /* Ignore variables should not be deleted warning. */ container.getSettingsModel().initialize(settings); delete settings; // Load all persistent settings (i.e. settings that will not be deleted when the user resets his settings) // Persistent settings are not added to the welcome screen, so check if they exist first. if ( Ext.isDefined(window.persistentsettings) ){ container.getPersistentSettingsModel().initialize(window.persistentsettings); /*jshint -W051 */ /* Ignore variables should not be deleted warning. */ delete window.persistentsettings; } // Set the user object container.setUser(user); delete user; // Set the version object container.setVersion(version); delete version; // Set the language object container.setLanguages(languages); delete languages; }, /** * Initialize the ExtJs/WebApp environment, register generic event listeners, * This will listen to the 'contextmenu' event on the {@link Ext#getBody body element} * as well as the exception events on the {@link Zarafa.core.data.IPMStoreMgr} and * {@link Zarafa.core.ResponseRouter}. * @private */ initializeEnvironment : function() { // Register the State provider which uses the SettingsModel. Ext.state.Manager.setProvider(new Zarafa.core.data.SettingsStateProvider()); // Disable contextmenu globaly Ext.getBody().on('contextmenu', this.onBodyContextMenu, this); // Disable default file drop behavior Ext.EventManager.on(window, 'dragover', this.onWindowDragDrop, this); Ext.EventManager.on(window, 'drop', this.onWindowDragDrop, this); // Add main event handlers to listen for errors container.getRequest().on('connectionparalyzed', this.onConnectionParalyze, this); container.getRequest().on('connectioninterrupted', this.onConnectionLoss, this); container.getRequest().on('connectionrestored', this.onConnectionRestore, this); container.getResponseRouter().on('receiveexception', this.onReceiveException, this); // We listen on the Ext.data.DataProxy object to listen in on all exception events Ext.data.DataProxy.on('exception', this.onException, this); // Enable tooltips Ext.QuickTips.init(); }, /** * Event handler which is fired when the {@link Ext#getBody <body>} elements fires * the 'contextmenu' event. If the element which fired the event doesn't have the * 'zarafa-contextmenu-enabled' class and isn't a regular text input then the * Browser contextmenu will be disabled. * @param {Ext.EventObject} event The event object * @param {Ext.Element} el The element on which the contextmenu was requested * @private */ onBodyContextMenu : function(event, el) { el = Ext.get(el); // Don't disable the browser contextmenu when the // 'zarafa-contextmenu-enabled' CSS class is applied // on the element. if ( el.hasClass('zarafa-contextmenu-enabled') ){ return; } // Don't disable the browser contextmenu for regular // text inputs. if ( el.dom.tagName.toUpperCase()==='INPUT' ){ var type = el.getAttribute('type') || ''; var readonly = !Ext.isEmpty(el.dom.attributes.readonly); if ( type.toUpperCase() === 'TEXT' && !readonly ) { return; } } // Disable contextmenu. event.preventDefault(); }, /** * Event handler which is fired when the 'window' element fires the 'dragover' or * the 'drop' event. This happens when the user drops a file over the webpage. On * some UI fields this will provide a special action, but the browsers default is * to open the file in the current page, which is not what we want. * @param {Ext.EventObject} event The event object * @private */ onWindowDragDrop : function(event) { event.stopPropagation(); event.preventDefault(); return false; }, /** * Event handler called when the {@link Ext.data.DataProxy} fired the * {@link Ext.data.DataProxy#storeexception storeexception} event. * This will check what type of exception it was ('response' or 'remote') and * handle the exception accordingly. * * @param {Misc} misc See {@link Ext.data.DataProxy}#{@link Ext.data.DataProxy#exception exception} * for description. * @private */ onException : function(proxy, type, action, options, response, args) { var message; if (type === 'response') { // The error message can be in args when it is an Error object. This happens when the // processing of the response throws an Javascript Exception. if (Ext.isDefined(args) && args.error instanceof Error) { message = args.error.toString(); } else { // When the exception has to do with the response itself we delegate this behavior to // onReceiveException this.onReceiveException(options, response); return; } } else if (response && response.error) { switch (response.error.type) { case Zarafa.core.ErrorType['MAPI']: case Zarafa.core.ErrorType['ZARAFA']: case Zarafa.core.ErrorType['GENERAL']: message = response.error.info.display_message; break; default: message = _('The server reported an unknown error on your request.'); break; } } else { message = _('The server reported an unspecified error on your request.'); } if (Ext.get('loading')) { this.setErrorLoadingMask(_('Error'), message); } else { container.getNotifier().notify('error.proxy', _('Error'), message); } }, /** * Called when the connection is being paralyzed and no further requests can be made * to the server. Check if we should show a notification to the user about this, and * ask if the user wishes to return to the logon page. * @param {Zarafa.core.Request} request The request object * @param {Zarafa.core.data.ParalyzeReason} reason The reason to paralyze the WebApp * @private */ onConnectionParalyze : function(request, reason) { var message = ''; var logoutFn = Ext.emptyFn; switch (reason) { case Zarafa.core.data.ParalyzeReason.BROWSER_RELOADING: /* falls through */ default: // No message for the user needed. return; case Zarafa.core.data.ParalyzeReason.SESSION_EXPIRED: message = _('The session has expired, reauthentication is required.'); // When logging out, we preserve the username for convenience. logoutFn = container.logout.createDelegate(container, [ true ], false); break; case Zarafa.core.data.ParalyzeReason.SESSION_INVALID: message = _('The session in the current browser window has been closed from another browser window or tab.'); // When logging out, we preserve the username for convenience, // but we won't close the session which was created in the other tab. logoutFn = container.logout.createDelegate(container, [ true, true ], false); break; } if (Ext.get('loading')) { this.setErrorLoadingMask(_('Error'), message); } else { Ext.MessageBox.show({ title: _('Kopano WebApp'), msg : message + '<br>' + _('Do you wish to be redirected to the logon page?'), icon : Ext.MessageBox.ERROR, buttons : Ext.MessageBox.YESNO, fn : this.onConnectionParalyzeConfirmation, scope: this, logoutFn : logoutFn }); } }, /** * Event handler for the {@link Ext.MessageBox MessageBox} which was opened by {@link #onConnectionParalyze}. * This determines what the user has pressed, and if the user wishes to {@link Zarafa.core.Container#logout} * immediately or not. If not, then a {@link Zarafa.core.ui.notifier.Notifier notification} will be shown * to remind the user about the session. * @param {String} button The button which was pressed by the user * @param {String} id The id of the button which was clicked * @param {Object} opt The options which was used to create the MessageBox. * @private */ onConnectionParalyzeConfirmation : function(button, value, opt) { if (button === 'yes') { opt.logoutFn.call(this); } else { container.getNotifier().notify('error.connection', _('Session expired'), _('Reauthentication required, click here to go to back to logon page.'), { persistent : true, listeners : { click : opt.logoutFn } }); } }, /** * Periodic function which is used to update the {@link Zarafa.core.ui.notification.Notifier notification} * on the screen with the message that there is a connection problem, and the requests will be retried * after the given timeout. This timeout counter will be updated every second. * @param Zarafa.core.PingService} service The ping service handling the connection loss * @param {Object} object Containing the information related to the connection loss * @param {Number} timeout The number of seconds until the next retry to connect to the server * @private */ onConnectionTimeupdate : function(service, object, timeout) { var request = container.getRequest(); // Since we use defers, it is possible that the // connection was already restored. In that case, // we don't need to update the Notification message. if (!request.isInterrupted() || request.isParalyzed()) { return; } // Create the notification, we store the reference in connEl, // if it already exists, this will be an update action, otherwise // a new notification will be created. this.connEl = container.getNotifier().notify('error.connection', _('Connection problem'), String.format(_('Could not connect to server, retrying in {0} second(s)'), timeout / 1000) + '<br />' + _('Click to retry now'), { persistent : true, update : !!this.connEl, reference : this.connEl, listeners : { // If the user clicks on the notification, // immediately retry to connecto to the server. click : service.retry, scope: service } }); // Defer the function by 1 second, so we can update the retry counter. if (timeout > 1000) { this.connElTask.delay(1000, this.onConnectionTimeupdate, this, [ service, object, timeout - 1000 ]); } }, /** * Event handler for the {@link Zarafa.core.PingService#retry} event. This will * cancel the currently scheduled call to {@link #onConnectionTimeupdate} and * invoke it manually with the updated information. * @param Zarafa.core.PingService} service The ping service handling the connection loss * @param {Object} object Containing the information related to the connection loss * @param {Number} timeout The number of seconds until the next retry to connect to the server * @private */ onConnectionRetry : function(service, object, timeout) { // In case there was still a pending // update task, we interrupt that one. this.connElTask.cancel(); // No we can update the notification this.onConnectionTimeupdate(service, object, timeout); }, /** * Event handler for the {@link Zarafa.core.Request#connectionloss} event. This will register the * event handlers to the provided {@link Zarafa.core.PingService} which will give us the information * about the next reconnect attempt. * @param {Zarafa.core.Request} request The request object * @param {Zarafa.core.PingService} service The ping service which is going to handle the connection loss * @private */ onConnectionLoss : function(request, service) { this.connElTask = new Ext.util.DelayedTask(this.onConnectionTimeupdate, this); service.on('retry', this.onConnectionRetry, this); }, /** * Event handler for the {@link Zarafa.core.Request#connectionrestore} event. This will remove * the error.connection {@link Zarafa.core.ui.notification.Notifier notification} * and will show a info.connection.restore {@link Zarafa.core.ui.notification.Notifier notification}. * @param {Zarafa.core.Request} request The request object * @private */ onConnectionRestore : function(request) { if (this.connElTask) { this.connElTask.cancel(); delete this.connElTask; } if (this.connEl) { container.getNotifier().notify('error.connection', null, null, { destroy : true, reference : this.connEl }); container.getNotifier().notify('info.connection.restore', _('Connection restored'), _('Connection with server has been restored')); delete this.connEl; } }, /** * Event handler called when the PHP server returned an error * in the root of the response. This indicates that the communication * with the PHP server failed and the user should login again. * * @param {Object} requestdata The request data which was send to the server. * @param {Object} xmlHttpRequest The raw browser response objec * @private */ onReceiveException : function(requestdata, xmlHttpRequest) { var loading = Ext.get('loading'); var errorTitle; var errorMsg; if (xmlHttpRequest.status !== 200) { // # TRANSLATORS: Example: HTTP 404 errorTitle = String.format(_('HTTP {0}'), xmlHttpRequest.status); errorMsg = xmlHttpRequest.statusText; } else { errorTitle = _('Error'); errorMsg = _('Invalid data received from the server'); } if (loading) { this.setErrorLoadingMask(errorTitle, errorMsg); } else { container.getNotifier().notify('error.json', errorTitle, errorMsg); } }, /** * Hide the loading mask which is shown before the {@link Ext.Viewport} is being rendered. * The loadmask is marked with the element classname 'loading' and the background 'loading-mask'. * @param {Function} callback An optional callback function that will be called when the the * loading mask is completely hidden. * @private */ hideLoadingMask : function(callback) { var loadingMask = Ext.get('loading-mask'); if ( loadingMask ) { // Hide loading mask loadingMask.fadeOut({ duration: 1, remove: true, callback: Ext.isFunction(callback) ? callback : Ext.EmptyFn }); } }, /** * Set an error text in the loading screen. * @param {String} newError The title for the loading screen * @param {String} newMessage The message for the loading screen * @private */ setErrorLoadingMask : function(newTitle, newMessage) { var template = new Ext.Template('<div><b>{title}</b><br />{msg}</div>', { compiled : true, disableFormats : true }); var message = Ext.get('loading-message'); if (message) { message.dom.className = 'loading-error'; template.overwrite(message, { title: newTitle, msg: newMessage }); } }, /** * Validate the {@link Zarafa.hierarchy.data.HierarchyStore HierarchyStore} to determine * if the {@link Zarafa.hierarchy.data.HierarchyStore#getDefaultStore Default Store} is present * along with all the {@link Zarafa.hierarchy.data.HierarchyStore#getDefaultFolder Default Folders}. * If there is a problem, this will show a {@link Zarafa.core.ui.notifier.Notifier Notification} * indicating which store or folders are missing, and warning the user that not all functionality * might be working as expected. * @param {Zarafa.hierarchy.data.HierarchyStore} store The Hierarchy Store to validate * @private */ validateHierarchy : function(store) { if (!store.getDefaultStore()) { container.getNotifier().notify('error.hierarchy.defaultfolder', _('Missing store'), _('The default store is missing from the hierarchy.') + '<br>' + _('Not all functionality of WebApp might be working properly because of this.'), { persistent : true, listeners : { 'click' : this.onHierarchyNotifierClick, 'scope': this } } ); return; } // The following default folders are required to be present // to be able to properly work with the WebApp. var defaultFolders = [{ type : 'inbox', name : pgettext('hierarchy.foldername', 'Inbox') },{ type : 'outbox', name : pgettext('hierarchy.foldername', 'Outbox') },{ type : 'sent', name : pgettext('hierarchy.foldername', 'Sent Items') },{ type : 'wastebasket', name : pgettext('hierarchy.foldername', 'Deleted items') },{ type : 'calendar', name : pgettext('hierarchy.foldername', 'Calendar') },{ type : 'contact', name : pgettext('hierarchy.foldername', 'Contacts') },{ type : 'drafts', name : pgettext('hierarchy.foldername', 'Drafts') },{ type : 'journal', name : pgettext('hierarchy.foldername', 'Journal') },{ type : 'note', name : pgettext('hierarchy.foldername', 'Notes') },{ type : 'task', name : pgettext('hierarchy.foldername', 'Tasks') },{ type : 'junk', name : pgettext('hierarchy.foldername', 'Junk E-mail') }]; var missing = []; // Go over all default folders which we expect to be present, // if any of them is missing we update the 'missing' array // and will show them in a notification box. for (var i = 0, len = defaultFolders.length; i < len; i++) { var folder = defaultFolders[i]; if (!store.getDefaultFolder(folder.type)) { missing.push(folder.name); } } // If any of the folders is missing, show a notification to the // user to inform him of the missing folders. if (!Ext.isEmpty(missing)) { // Construct an HTML list of the missing folders var list = '<ul><li>' + missing.join('</li><li>') + '</li></ul>'; container.getNotifier().notify('error.hierarchy.defaultfolder', _('Missing folders'), String.format( ngettext('The following required folder is missing in the hierarchy: {0}', 'The following required folders are missing in the hierarchy: {0}', missing.length), list) + _('Not all functionality of WebApp might be working properly because of this.'), { persistent : true, listeners : { 'click' : this.onHierarchyNotifierClick, 'scope': this } } ); } }, /** * Event handler which is fired when the user clicked on the {@link Zarafa.core.ui.notifier.Notifier notification}. * This will remove the notification. * @param {Ext.Element} element The notification element * @param {Ext.EventObject} event The event object * @private */ onHierarchyNotifierClick : function(element, event) { container.getNotifier().notify('error.hierarchy.defaultfolder', null, null, { reference : element, destroy : true }); }, /** * Event handler called when load is received in hierarchy. This will {@link #hideLoadingMask hide the loadmask}. * @param {Zarafa.hierarchy.data.HierarchyStore} store The store which was loaded * @param {Ext.data.Record[]} records The records which were loaded by the store * @param {Object} options The options which were originally passed to {@link Ext.data.Store#load}. * @private */ onHierarchyLoad : function(store, records, options) { if (!Ext.isEmpty(records)) { // We have the hierarchy, load the entire UI container.getMainPanel(); // validate the hierarchy, if the hierarchy is not valid a warning // notification box will be shown to the user informing him of incompatibilities. this.validateHierarchy(store); // Remove loading mask, we might still be busy loading the data for the // store of the user, but the context will have its own loadmask for that. // The user is at least allowed to see the Hierarchy and press buttons. this.hideLoadingMask(function(){ container.fireEvent('webapploaded'); // Notify that the WebApp UI is loaded. Zarafa.fireUIReady(); }); // Remove resize event listener of loading page window.removeEventListener('resize', resizeLoginBox); // Register webapp to handle mailto urls this.registerMailto(); // Process data that was passed as URL data Zarafa.core.URLActionMgr.execute(urlActionData); /*jshint -W051 */ delete urlActionData; // Start the keepalive to make sure we stay logged into the zarafa-server, // the keepalive is also used to get notifications back to the client. store.startKeepAlive(); } else { this.setErrorLoadingMask(_('Error'), _('Loading model from server failed')); } }, /** * Function will register webapp as default client to handle mailto urls * Ideally we should only register this handler if already not registered * but browsers support is not proper for that, so we are always registering it * in chrome this is not a problem, in firefox it will show bar that handler is already registered * but that is a bug and already fixed in trunk version https://bugzilla.mozilla.org/show_bug.cgi?id=912347 * Support for isProtocolHandlerRegistered is also limited https://bugzilla.mozilla.org/show_bug.cgi?id=440620 */ registerMailto : function() { var navigator = window.navigator; if (Ext.isFunction(navigator.registerProtocolHandler)) { // Register webapp handler for 'mailto' protocol in browsers. var url = container.getBaseURL() + '?action=mailto&to=%s'; // Check if we have already registered for this protocol var register = true; if (Ext.isFunction(navigator.isProtocolHandlerRegistered)) { register = !navigator.isProtocolHandlerRegistered('mailto', url); } // Register if required if (register) { navigator.registerProtocolHandler('mailto', url, 'Kopano WebApp'); } } }, /** * Load the default Webclient into the browser. This will initialize the environment, * load the {@link Zarafa.hierarchy.data.HierarchyStore} and open the * {@link Zarafa.core.ui.MainViewport MainViewport}. */ loadWebclient : function() { // Initialize globals & environment Zarafa.initializeGlobals(); Zarafa.initializeEnvironment(); // Start loading all plugins Zarafa.fireReady(); Zarafa.whatsnew.Actions.openWhatsNewDialog(); // Check if user is out of office and ask them if they want to switch it off this.checkOof(); // Initialize context - check if there is one selected in settings this.initContext(); // Start polling the server to get reminders data back to client container.getReminderStore().initializeReminderInterval(); // Setup the hierarchy, this will complete the initialization // and allow the Main Viewport to be opened var hierarchyStore = container.getHierarchyStore(); // For the hierarchy we only need to register the event handler once, // as only during the first time we have to remove the load mask. hierarchyStore.on('load', this.onHierarchyLoad, this, { single: true }); // Load the folder hierarchy hierarchyStore.load(); // When a client timeout has been defined, we will start keeping track // of idle time. var server = container.getServerConfig(); var clientTimeout = server.getClientTimeout(); if (clientTimeout){ this.startIdleTimeChecker(clientTimeout); } // Check if the Wingdings font is installed this.wingdingsInstalled = window.checkfont.exists('Wingdings'); }, /** * Starts the checking of idle time. * This function uses original javascript events because we cannot set * the useCapture property with ExtJS events. * See https://developer.mozilla.org/en/docs/Web/API/EventTarget.addEventListener * * @param {Number} clientTimeout The timout time in seconds. * @private */ startIdleTimeChecker : function(clientTimeout) { if ( !document.addEventListener ) { // User is using a browser that does not support addEventListener. // Probably IE<9 which we don't support. // However there is no reason to create errors for IE<9 // Client timeout will still be handled by the backend though, // but the message will only be shown to the user when he tries to // connect to the backend after the session has timed out. return; } var me = this; document.addEventListener('click', function(){ me.idleTime = 0; }, true); document.addEventListener('mousemove', function(){ me.idleTime = 0; }, true); document.addEventListener('keydown', function(){ me.idleTime = 0; }, true); var hierarchyStore = container.getHierarchyStore(); // Start an interval for increasing the idle time Ext.TaskMgr.start.createDelegate(this, [{ run : function(){ // Add 5 seconds to the idle time counter this.idleTime += 5; if ( this.idleTime > clientTimeout ){ hierarchyStore.sendDestroySession(); } }, scope : this, interval : 5000 //Run every 5 seconds }]).defer(5000); // Start after 5 seconds // Start an interval for sending keepalive requests // We need this keepalive to keep the connection alive if the user has made an // action in the WebApp without connecting to the server. (like mousemove, click, keydown) // Substracting 5 seconds to account for latency var interval = (clientTimeout-5)*1000; if ( interval < 5000 ){ // This one is especially for Sean who was so smart to set a client timeout of 5 seconds // causing keepalive requests to be sent constantly and thereby ddos'ing his own server :-) // Let's never send keepalive requests with an interval lower than 5 seconds. // Anyone who sets a timeout this low deserves to be logged out! (and punished severly) interval = 5000; } Ext.TaskMgr.start.createDelegate(this, [{ run : function(){ hierarchyStore.sendKeepAlive(); }, scope : this, interval : interval }]).defer(interval); //Start sending keepalives after interval milliseconds. }, /** * init UI by lazily constructing the main panel (implicit in container.getMainPanel) and * setting the default context to visible. Note that during onHierarchyLoad we will also * open the default folder for that specific context to ensure the user can see all from * the folder. * @private */ initContext : function() { var defaultContext = container.getSettingsModel().get('zarafa/v1/main/default_context'); var plugin = container.getContextByName(defaultContext); if (!plugin) { // If the defaultContext has an invalid name, // we will default to the 'today' context plugin = container.getContextByName('today'); } container.switchContext(plugin); }, /** * Check if user is out of office * If so, ask them if they want to switch OOF off * @private */ checkOof : function() { var oof = false; if (container.getSettingsModel().get('zarafa/v1/contexts/mail/outofoffice/set') === true) { var oofFrom = container.getSettingsModel().get('zarafa/v1/contexts/mail/outofoffice/from'); var oofUntil = container.getSettingsModel().get('zarafa/v1/contexts/mail/outofoffice/until'); var date = new Date().getTime()/1000; // Check if current date fall within the time span of OOF start-date and end-date, if configured. if (oofFrom <= date) { // Check if end-date is configured, no need to check otherwise if(oofUntil === 0 || oofUntil > date) { oof = true; } else { // Current date falls out of the configured time span, so disable the OOF container.getSettingsModel().set('zarafa/v1/contexts/mail/outofoffice/set', false); } } } if ( oof ) { Ext.MessageBox.confirm( _('Kopano WebApp'), _('Out of Office currently on. Would you like to turn it off?'), this.onOofConfirm, this ); } }, /** * Handler for the out of office confirmation box * If the user pressed Yes/Ok, then disable out of office * @param {String} id of the button that was clicked * @private */ onOofConfirm : function(button) { if (button === 'yes') { container.getSettingsModel().set('zarafa/v1/contexts/mail/outofoffice/set', false); container.getNotifier().notify('info.saved', _('Out of office off'), _('Out of office has been turned off')); } }, /** * Load the Welcome message for new users into the browser. This will initialize * the environment and open the {@link Zarafa.core.ui.WelcomeViewport WelcomeViewport}. */ loadWelcome : function() { // Setup globals & environment this.initializeGlobals(); this.initializeEnvironment(); // Start loading all plugins this.fireReady(); // Load the welcome view container.getWelcomePanel(); this.hideLoadingMask(); }, // // Generic Utility functions, should probably be moved elsewhere // /** * This will resize a canvas {@link Ext.Element element}. * Canvas resizing is more tricky then one would expect. For canvas 2 settings are * important: the CSS and attribute dimensions. * * As one would expect, the CSS dimensions, given through a CSS file, or * 'style' attribute (controlled by the functions {@link Ext.Element#setWidth setWidth} * and {@link Ext.Element#setHeight setHeight}) controls how big the element itself * is. It however does not mean that the drawing area is of the same size. The drawing * area is controlled by the element attributes 'width' and 'height'. * * Now for the fun part, if you use CSS to size of the element to 1000x1000px, * thus the element looks like: * <canvas style="width=1000px; height=1000px;"> * and set the attributes to 50x50px, making the complete element look like: * <canvas style="width=1000px; height=1000px;" width="50" height="50"> * you can then draw anything you like on the canvas, but only the first * 50x50px of the canvas will be resized to the entire element size. Making * your cute little drawing of a giraffe completely stretched. * * @param {Ext.Element} canvas The canvas element which must be resized. * @param {Number} width The desired width of the canvas element * @param {Number} height The desired height of the canvas element */ resizeCanvas : function(canvas, width, height) { canvas.setWidth(canvas.dom.width = width); canvas.setHeight(canvas.dom.height = height); }, /** * Generate a random string. * @param {Number} len Length of the string * @return {String} Random string */ generateId : function(len) { var text = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for( var i = 0; i < len; i++ ) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; }, /** * Determine if separate window popout is supported. * Support is based on browser's ability to render any element into another browser window. * * @return {Boolean} True if popout is supported, false otherwise */ supportsPopOut : function() { // Currently, we do not support the popout in case of IE/Edge. return (!(Ext.isIE || Ext.isEdge)); } });