1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * @overview 26 * This file defines the application context class. 27 * 28 */ 29 30 /** 31 * Creates an application context. 32 * @class 33 * This class is a container for application context information. 34 * 35 */ 36 ZmAppCtxt = function() { 37 38 this._trees = {}; 39 40 this.accountList = new ZmAccountList(); 41 // create dummy account for startup 42 this.accountList.add(new ZmZimbraAccount(ZmAccountList.DEFAULT_ID, null, false)); 43 44 // public properties 45 this.inStartup = false; // true if we are starting app (set in ZmZimbraMail) 46 this.currentRequestParams = null; // params of current SOAP request (set in ZmRequestMgr) 47 this.rememberMe = null; 48 this.userDomain = ""; 49 50 // account-specific 51 this.isFamilyMbox = false; 52 this.multiAccounts = false; 53 this.sendAsEmails = []; 54 this.sendOboEmails = []; 55 56 this._evtMgr = new AjxEventMgr(); 57 58 this._itemCache = {}; 59 this._itemCacheDeferred = {}; 60 this._acCache = {}; // autocomplete 61 this._isExpandableDL = {}; // distribution lists 62 63 this._checkAuthTokenWarning(); 64 }; 65 66 ZmAppCtxt.ONE_MINUTE = 60 * 1000; 67 ZmAppCtxt.MAX_TIMEOUT_VALUE = 2147483647; 68 69 ZmAppCtxt._ZIMLETS_EVENT = 'ZIMLETS'; 70 ZmAppCtxt._AUTHTOKEN_EVENT = 'AUTHTOKEN'; 71 72 //Regex constants 73 //Bug fix # 79986, #81095. Invalid file names are < > , ? | / \ * : 74 ZmAppCtxt.INVALID_NAME_CHARS = "[\\|?<>:*\"\\\\\/]"; 75 ZmAppCtxt.INVALID_NAME_CHARS_RE = new RegExp(ZmAppCtxt.INVALID_NAME_CHARS); 76 77 /** 78 * Returns a string representation of the application context. 79 * 80 * @return {String} a string representation of the application context 81 */ 82 ZmAppCtxt.prototype.toString = 83 function() { 84 return "ZmAppCtxt"; 85 }; 86 87 ZmAppCtxt.prototype._checkAuthTokenWarning = 88 function() { 89 this._authIntervalId = window.setInterval(this._authTokenWarningTimeout.bind(this), ZmAppCtxt.ONE_MINUTE); 90 }; 91 ZmAppCtxt.prototype._setAuthTokenWarning = 92 function(delay) { 93 window.setTimeout(this._authTokenWarningTimeout.bind(this), delay); 94 }; 95 96 /** 97 * Adds a listener to the auth token warning event. This listener is fired once 98 * per minute when less than five minutes remain before token expiry. 99 * 100 * @param {AjxCallback} listener the listener 101 * @param {int} index the index to where to add the listener 102 * @return {Boolean} <code>true</code> if the listener is added; <code>false</code> otherwise 103 */ 104 ZmAppCtxt.prototype.addAuthTokenWarningListener = 105 function(listener, index) { 106 return this._evtMgr.addListener(ZmAppCtxt._AUTHTOKEN_EVENT, listener, index); 107 }; 108 109 /** 110 * Removes a listener for the auth token warning event. 111 * 112 * @param {AjxCallback} listener the listener 113 * @return {Boolean} <code>true</code> if the listener is removed; <code>false</code> otherwise 114 */ 115 ZmAppCtxt.prototype.removeAuthTokenWarningListener = 116 function(listener) { 117 return this._evtMgr.removeListener(ZmAppCtxt._AUTHTOKEN_EVENT, listener); 118 }; 119 120 ZmAppCtxt.prototype._authTokenWarningTimeout = 121 function () { 122 123 if (!window.authTokenExpires) { 124 return; //for cases we the auth token expires is not available. (e.g. some new windows we didn't set it for yet, or for saved rest URLs 125 } 126 127 var now = new Date().getTime(); 128 var millisToLive = window.authTokenExpires - now; 129 var minutesToLive = Math.round(millisToLive / ZmAppCtxt.ONE_MINUTE); 130 var delay; 131 132 if (minutesToLive > 5 || millisToLive <= 0) { 133 // Outside the times to issue warnings 134 if (minutesToLive === 6) { 135 // Line up the timer to go off at exactly 5 minutes (or as exact as we can make it), which is 136 // when we start issuing warnings 137 window.clearInterval(this._authIntervalId); 138 delay = millisToLive - (5 * ZmAppCtxt.ONE_MINUTE); 139 this._setAuthTokenWarning(delay); 140 } 141 return; 142 } 143 144 if (this._evtMgr.isListenerRegistered(ZmAppCtxt._AUTHTOKEN_EVENT)) { 145 var event = new ZmEvent(ZmAppCtxt._AUTHTOKEN_EVENT); 146 this._evtMgr.notifyListeners(ZmAppCtxt._AUTHTOKEN_EVENT, event); 147 } 148 149 var msg; 150 var decaSecondsToLive = 0; 151 var toastDuration; 152 if (minutesToLive > 1) { 153 msg = AjxMessageFormat.format(ZmMsg.authTokenExpirationWarning, [minutesToLive, ZmMsg.minutes]); 154 toastDuration = ZmAppCtxt.ONE_MINUTE / 4; 155 } else { 156 // Get the number of 10-second intervals remaining - used once we are within 1 minute 157 decaSecondsToLive = Math.round(millisToLive / 10000); 158 toastDuration = 8000; 159 if (decaSecondsToLive >= 6) { 160 // 1 minute+ to go. But should be pretty close to 1 minute 161 msg = AjxMessageFormat.format(ZmMsg.authTokenExpirationWarning, [1, ZmMsg.minute]); 162 } else { 163 // Seconds remain 164 msg = AjxMessageFormat.format(ZmMsg.authTokenExpirationWarning, [decaSecondsToLive * 10, ZmMsg.seconds]); 165 } 166 } 167 168 var params = { 169 msg: msg, 170 level: ZmStatusView.LEVEL_WARNING, 171 transitions: [{type: "fade-in", duration: 500}, {type: "pause", duration: toastDuration}, {type: "fade-out", duration: 500} ] 172 }; 173 this.setStatusMsg(params); 174 175 if (minutesToLive > 1) { 176 var floorMinutesToLive = Math.floor(millisToLive / ZmAppCtxt.ONE_MINUTE); 177 if (floorMinutesToLive === minutesToLive) { 178 floorMinutesToLive--; 179 } 180 delay = millisToLive - (floorMinutesToLive * ZmAppCtxt.ONE_MINUTE); 181 } else { 182 decaSecondsToLive--; 183 delay = millisToLive - (decaSecondsToLive * 10000); 184 } 185 if (delay > 0) { 186 this._setAuthTokenWarning(delay); 187 } 188 }; 189 190 ZmAppCtxt.prototype.setZimbraMail = function(zimbraMail) { 191 this._zimbraMail = zimbraMail; 192 } 193 194 ZmAppCtxt.prototype.getZimbraMail = function() { 195 return this._zimbraMail; 196 } 197 198 /** 199 * Sets the application controller. 200 * 201 * @param {ZmController} appController the controller 202 */ 203 ZmAppCtxt.prototype.setAppController = 204 function(appController) { 205 this._appController = appController; 206 }; 207 208 /** 209 * Gets the application controller. 210 * 211 * @return {ZmController} the controller 212 */ 213 ZmAppCtxt.prototype.getAppController = 214 function() { 215 return this._appController; 216 }; 217 218 /** 219 * Gets the application chooser. 220 * 221 * @return {ZmAppChooser} the chooser 222 */ 223 ZmAppCtxt.prototype.getAppChooser = 224 function() { 225 return this._appController.getAppChooser(); 226 }; 227 228 /** 229 * Sets the request manager. 230 * 231 * @param {ZmRequestMgr} requestMgr the request manager 232 */ 233 ZmAppCtxt.prototype.setRequestMgr = 234 function(requestMgr) { 235 this._requestMgr = requestMgr; 236 }; 237 238 /** 239 * Gets the request manager. 240 * 241 * @return {ZmRequestMgr} the request manager 242 */ 243 ZmAppCtxt.prototype.getRequestMgr = 244 function() { 245 return this._requestMgr; 246 }; 247 248 /** 249 * Sets the status message to display. 250 * 251 * 252 * @param {Hash} params a hash of parameters 253 * @param {String} params.msg the status message 254 * @param {constant} params.level the status level {@link ZmStatusView} (may be <code>null</code>) 255 * @param {String} params.detail the details (may be <code>null</code>) 256 * @param {Object} params.transitions the transitions (may be <code>null</code>) 257 * @param {Object} params.toast the toast control (may be <code>null</code>) 258 * @param {boolean} params.force force any displayed toasts out of the way (dismiss them and run their dismissCallback). Enqueued messages that are not yet displayed will not be displayed 259 * @param {AjxCallback} params.dismissCallback callback to run when the toast is dismissed (by another message using [force], or explicitly calling ZmStatusView.prototype.dismiss()) 260 * @param {AjxCallback} params.finishCallback callback to run when the toast finishes its transitions by itself (not when dismissed) 261 * </ul> 262 * 263 */ 264 ZmAppCtxt.prototype.setStatusMsg = 265 function(params) { 266 params = Dwt.getParams(arguments, ZmStatusView.MSG_PARAMS); 267 this._appController.setStatusMsg(params); 268 }; 269 270 /** 271 * Dismisses the displayed status message, if any 272 */ 273 274 ZmAppCtxt.prototype.dismissStatusMsg = 275 function(all) { 276 this._appController.dismissStatusMsg(all); 277 }; 278 279 /** 280 * Gets the settings for the given account. 281 * 282 * @param {ZmZimbraAccount} account the account 283 * @return {ZmSettings} the settings 284 */ 285 ZmAppCtxt.prototype.getSettings = 286 function(account) { 287 var al = this.accountList; 288 289 var acct = account || al.activeAccount || al.mainAccount 290 || al.getAccount(ZmAccountList.DEFAULT_ID); //Probably doesn't ever happen, and if it does, returns null. Might be some historical artifact - did we ever have account with id "main"? I'm still afraid to remove it without being sure it won't cause regression. 291 292 return acct && acct.settings; 293 }; 294 295 /** 296 * Sets the settings for the given account. 297 * 298 * @param {ZmSettings} settings the settings 299 * @param {ZmZimbraAccount} account the account 300 */ 301 ZmAppCtxt.prototype.setSettings = 302 function(settings, account) { 303 var al = this.accountList; 304 var id = account 305 ? account.id 306 : al.activeAccount ? al.activeAccount.id : ZmAccountList.DEFAULT_ID; 307 308 var acct = al.getAccount(id); 309 if (acct) { 310 acct.settings = settings; 311 } 312 }; 313 314 /** 315 * Gets the value of the given setting. 316 * 317 * @param {constant} id the setting id 318 * @param {String} key the setting key (for settings that are of the hash type) 319 * @param {ZmZimbraAccount} account the account 320 * @return {Object} the setting value 321 */ 322 ZmAppCtxt.prototype.get = function(id, key, account) { 323 324 //use parentAppCtxt in case of new window 325 var context = this.isChildWindow ? parentAppCtxt : this; 326 327 // for offline, global settings always come from the "local" parent account 328 var acct = (context.multiAccounts && ZmSetting.IS_GLOBAL[id]) 329 ? context.accountList.mainAccount : account; 330 return context.getSettings(acct).get(id, key); 331 }; 332 333 /** 334 * Sets the value of the given setting. 335 * 336 * @param {constant} id the setting id 337 * @param {Object} value the setting value 338 * @param {String} key the setting key (for settings that are of the hash type) 339 * @param {Boolean} setDefault if <code>true</code>, also replace setting default value 340 * @param {Boolean} skipNotify if <code>true</code>, do not notify setting listeners 341 * @param {ZmZimbraAccount} account if set, use this account setting instead of the currently active account 342 * @param {Boolean} skipImplicit if <code>true</code>, do not check for change to implicit pref 343 */ 344 ZmAppCtxt.prototype.set = 345 function(id, value, key, setDefault, skipNotify, account, skipImplicit) { 346 // for offline, global settings always come from "parent" account 347 var acct = (this.multiAccounts && ZmSetting.IS_GLOBAL[id]) 348 ? this.accountList.mainAccount : account; 349 var setting = this.getSettings(acct).getSetting(id); 350 351 if (setting) { 352 setting.setValue(value, key, setDefault, skipNotify, skipImplicit); 353 } 354 }; 355 356 /** 357 * Gets the application. 358 * 359 * @param {String} appName the application name 360 * @return {ZmApp} the application or <code>null</code> if not found 361 */ 362 ZmAppCtxt.prototype.getApp = 363 function(appName) { 364 return this._appController.getApp(appName); 365 }; 366 367 /** 368 * Gets the name of the current application. 369 * 370 * @return {String} the application name 371 */ 372 ZmAppCtxt.prototype.getCurrentAppName = 373 function() { 374 var context = this.isChildWindow ? parentAppCtxt : this; 375 return context._appController.getActiveApp(); 376 }; 377 /** 378 * 379 */ 380 ZmAppCtxt.prototype.getLoggedInUsername = 381 function() { 382 return appCtxt.get(ZmSetting.USERNAME); 383 }; 384 385 /** 386 * Gets the current application. 387 * 388 * @return {ZmApp} the current application 389 */ 390 ZmAppCtxt.prototype.getCurrentApp = 391 function() { 392 return this.getApp(this.getCurrentAppName()); 393 }; 394 395 /** 396 * Gets the application view manager. 397 * 398 * @return {ZmAppViewMgr} the view manager 399 */ 400 ZmAppCtxt.prototype.getAppViewMgr = 401 function() { 402 return this._appController.getAppViewMgr(); 403 }; 404 405 /** 406 * Gets the client command handler. 407 * 408 * @param {ZmClientCmdHandler} clientCmdHdlr not used 409 * @return {ZmClientCmdHandler} the command handler 410 */ 411 ZmAppCtxt.prototype.getClientCmdHandler = 412 function(clientCmdHdlr) { 413 if (!this._clientCmdHandler) { 414 AjxDispatcher.require("Extras"); 415 this._clientCmdHandler = new ZmClientCmdHandler(); 416 } 417 return this._clientCmdHandler; 418 }; 419 420 /** 421 * Gets the search bar controller. 422 * 423 * @return {ZmSearchController} the search controller 424 */ 425 ZmAppCtxt.prototype.getSearchController = 426 function() { 427 if (!this._searchController) { 428 this._searchController = new ZmSearchController(this._shell); 429 } 430 return this._searchController; 431 }; 432 433 /** 434 * Gets the overview controller. Creates a new one if not already set, unless dontCreate is true. 435 * 436 * @param {boolean} dontCreate (optional) - don't create overviewController if not created already. (see ZmApp for usage with dontCreate == true) 437 * 438 * @return {ZmOverviewController} the overview controller 439 */ 440 ZmAppCtxt.prototype.getOverviewController = 441 function(dontCreate) { 442 if (!this._overviewController) { 443 if (dontCreate) { 444 return null; 445 } 446 this._overviewController = new ZmOverviewController(this._shell); 447 } 448 return this._overviewController; 449 }; 450 451 /** 452 * Gets the import/export controller. 453 * 454 * @return {ZmImportExportController} the controller 455 */ 456 ZmAppCtxt.prototype.getImportExportController = function() { 457 if (!this._importExportController) { 458 AjxDispatcher.require("ImportExport"); 459 this._importExportController = new ZmImportExportController(); 460 } 461 return this._importExportController; 462 }; 463 464 /** 465 * Gets the message dialog. 466 * 467 * @return {DwtMessageDialog} the message dialog 468 */ 469 ZmAppCtxt.prototype.getMsgDialog = 470 function() { 471 if (!this._msgDialog) { 472 this._msgDialog = new DwtMessageDialog({parent:this._shell, id: "ZmMsgDialog"}); 473 } 474 return this._msgDialog; 475 }; 476 477 /** 478 * Gets the message dialog with a help button. 479 * 480 * @return {DwtMessageDialog} the message dialog 481 */ 482 ZmAppCtxt.prototype.getHelpMsgDialog = 483 function() { 484 if (!this._helpMsgDialog) { 485 this._helpMsgDialog = new DwtMessageDialog({parent:this._shell, helpText:ZmMsg.help, id: "ZmHelpMsgDialog"}); 486 } 487 return this._helpMsgDialog; 488 }; 489 490 /** 491 * Gets the yes/no message dialog. 492 * 493 * @return {DwtMessageDialog} the message dialog 494 */ 495 ZmAppCtxt.prototype.getYesNoMsgDialog = 496 function(id) { 497 if (!this._yesNoMsgDialog) { 498 this._yesNoMsgDialog = new DwtMessageDialog({parent:this._shell, buttons:[DwtDialog.YES_BUTTON, DwtDialog.NO_BUTTON], id: "YesNoMsgDialog"}); 499 } 500 return this._yesNoMsgDialog; 501 }; 502 503 /** 504 * Gets the yes/no/cancel message dialog. 505 * 506 * @return {DwtMessageDialog} the message dialog 507 */ 508 ZmAppCtxt.prototype.getYesNoCancelMsgDialog = 509 function() { 510 if (!this._yesNoCancelMsgDialog) { 511 this._yesNoCancelMsgDialog = new DwtMessageDialog({parent:this._shell, buttons:[DwtDialog.YES_BUTTON, DwtDialog.NO_BUTTON, DwtDialog.CANCEL_BUTTON], id:"YesNoCancel"}); 512 } 513 return this._yesNoCancelMsgDialog; 514 }; 515 516 /** 517 * Gets the ok/cancel message dialog. 518 * 519 * @return {DwtMessageDialog} the message dialog 520 */ 521 ZmAppCtxt.prototype.getOkCancelMsgDialog = 522 function() { 523 if (!this._okCancelMsgDialog) { 524 this._okCancelMsgDialog = new DwtMessageDialog({parent:this._shell, buttons:[DwtDialog.OK_BUTTON, DwtDialog.CANCEL_BUTTON], id:"OkCancel"}); 525 } 526 return this._okCancelMsgDialog; 527 }; 528 529 /** 530 * Gets the cancel message dialog. 531 * 532 * @return {DwtMessageDialog} the message dialog 533 */ 534 ZmAppCtxt.prototype.getCancelMsgDialog = 535 function() { 536 if (!this._cancelMsgDialog) { 537 this._cancelMsgDialog = new DwtMessageDialog({parent:this._shell, buttons:[DwtDialog.CANCEL_BUTTON]}); 538 } 539 return this._cancelMsgDialog; 540 }; 541 542 /** 543 * Gets the error dialog. 544 * 545 * @return {ZmErrorDialog} the error dialog 546 */ 547 ZmAppCtxt.prototype.getErrorDialog = 548 function() { 549 if (!this._errorDialog) { 550 AjxDispatcher.require("Startup2"); 551 this._errorDialog = new ZmErrorDialog(this._shell, ZmMsg); 552 } 553 return this._errorDialog; 554 }; 555 556 /** 557 * Gets the new tag dialog. 558 * 559 * @return {ZmNewTagDialog} the new tag dialog 560 */ 561 ZmAppCtxt.prototype.getNewTagDialog = 562 function() { 563 if (!this._newTagDialog) { 564 this._newTagDialog = new ZmNewTagDialog(this._shell); 565 } 566 return this._newTagDialog; 567 }; 568 569 /** 570 * Gets the new contact group dialog. 571 * 572 * @return {ZmNewContactGroupDialog} the new contact group dialog 573 */ 574 ZmAppCtxt.prototype.getNewContactGroupDialog = 575 function() { 576 if (!this._newContactGroupDialog) { 577 this._newContactGroupDialog = new ZmNewContactGroupDialog(this._shell); 578 } 579 return this._newContactGroupDialog; 580 }; 581 582 /** 583 * Gets the rename tag dialog. 584 * 585 * @return {ZmRenameTagDialog} the rename tag dialog 586 */ 587 ZmAppCtxt.prototype.getRenameTagDialog = 588 function() { 589 if (!this._renameTagDialog) { 590 AjxDispatcher.require("Extras"); 591 this._renameTagDialog = new ZmRenameTagDialog(this._shell); 592 } 593 return this._renameTagDialog; 594 }; 595 596 /** 597 * Gets the password update dialog. 598 * 599 * @return {ZmPasswordUpdateDialog} the rename tag dialog 600 */ 601 ZmAppCtxt.prototype.getPasswordChangeDialog = 602 function() { 603 if (!this._passwordUpdateDialog) { 604 AjxDispatcher.require("Extras"); 605 this._passwordUpdateDialog = new ZmPasswordUpdateDialog(this._shell); 606 } 607 return this._passwordUpdateDialog; 608 }; 609 610 /** 611 * Gets the new folder dialog. 612 * 613 * @return {ZmNewFolderDialog} the new folder dialog 614 */ 615 ZmAppCtxt.prototype.getNewFolderDialog = 616 function() { 617 if (!this._newFolderDialog) { 618 var title = ZmMsg.createNewFolder; 619 var type = ZmOrganizer.FOLDER; 620 this._newFolderDialog = new ZmNewOrganizerDialog(this._shell, null, title, type) 621 } 622 return this._newFolderDialog; 623 }; 624 625 /** 626 * Gets the new address book dialog. 627 * 628 * @return {ZmNewAddrBookDialog} the new address book dialog 629 */ 630 ZmAppCtxt.prototype.getNewAddrBookDialog = 631 function() { 632 if (!this._newAddrBookDialog) { 633 AjxDispatcher.require("Contacts"); 634 this._newAddrBookDialog = new ZmNewAddrBookDialog(this._shell); 635 } 636 return this._newAddrBookDialog; 637 }; 638 639 /** 640 * Gets the new calendar dialog. 641 * 642 * @return {ZmNewCalendarDialog} the new calendar dialog 643 */ 644 ZmAppCtxt.prototype.getNewCalendarDialog = 645 function() { 646 if (!this._newCalendarDialog) { 647 AjxDispatcher.require(["MailCore", "CalendarCore", "Calendar", "CalendarAppt"]); 648 this._newCalendarDialog = new ZmNewCalendarDialog(this._shell); 649 } 650 return this._newCalendarDialog; 651 }; 652 653 /** 654 * Gets the new task folder dialog. 655 * 656 * @return {ZmNewTaskFolderDialog} the new task folder dialog 657 */ 658 ZmAppCtxt.prototype.getNewTaskFolderDialog = 659 function() { 660 if (!this._newTaskFolderDialog) { 661 AjxDispatcher.require(["TasksCore", "Tasks"]); 662 this._newTaskFolderDialog = new ZmNewTaskFolderDialog(this._shell); 663 } 664 return this._newTaskFolderDialog; 665 }; 666 667 /** 668 * Gets the new suggestion Preferences dialog 669 * 670 * @return {ZmTimeSuggestionPrefDialog} 671 */ 672 ZmAppCtxt.prototype.getSuggestionPreferenceDialog = 673 function() { 674 if (!this._suggestionPrefDialog) { 675 AjxDispatcher.require(["MailCore", "CalendarCore", "Calendar"]); 676 this._suggestionPrefDialog = new ZmTimeSuggestionPrefDialog(this._shell); 677 } 678 return this._suggestionPrefDialog; 679 }; 680 681 /** 682 * Gets the dialog. 683 * 684 * @return {DwtDialog} the dialog 685 */ 686 ZmAppCtxt.prototype.getDialog = 687 function(){ 688 if(!this._dialog){ 689 this._dialog = new DwtDialog({parent:this._shell}); 690 } 691 return this._dialog; 692 }; 693 694 /** 695 * Gets the new search dialog. 696 * 697 * @return {ZmNewSearchDialog} the new search dialog 698 */ 699 ZmAppCtxt.prototype.getNewSearchDialog = 700 function() { 701 this._newSearchDialogs = this._newSearchDialogs || {}; 702 this.searchAppName = this.searchAppName || ZmApp.MAIL; 703 if (!this._newSearchDialogs[this.searchAppName]) { 704 this._newSearchDialogs[this.searchAppName] = new ZmNewSearchDialog(this._shell); 705 } 706 this._newSearchDialog = this._newSearchDialogs[this.searchAppName]; 707 return this._newSearchDialog; 708 }; 709 710 /** 711 * Gets the rename folder dialog. 712 * 713 * @return {ZmRenameFolderDialog} the rename folder dialog 714 */ 715 ZmAppCtxt.prototype.getRenameFolderDialog = 716 function() { 717 if (!this._renameFolderDialog) { 718 AjxDispatcher.require("Extras"); 719 this._renameFolderDialog = new ZmRenameFolderDialog(this._shell); 720 } 721 return this._renameFolderDialog; 722 }; 723 724 /** 725 * Gets the choose folder dialog. 726 * 727 * @return {ZmChooseFolderDialog} the choose folder dialog 728 */ 729 ZmAppCtxt.prototype.getChooseFolderDialog = 730 function(appName) { 731 var app = appName ? this.getApp(appName) : this.getCurrentApp(); 732 // this.getCurrentAppName() returns "Search" for search apps. Let's re-use dialogs from regular apps. 733 appName = app.isZmSearchApp ? this.searchAppName : app.getName(); 734 this._chooseFolderDialogs = this._chooseFolderDialogs || {}; 735 if (!this._chooseFolderDialogs[appName]) { 736 AjxDispatcher.require("Extras"); 737 this._chooseFolderDialogs[appName] = new ZmChooseFolderDialog(this._shell); 738 } 739 this._chooseFolderDialog = this._chooseFolderDialogs[appName]; 740 return this._chooseFolderDialog; 741 }; 742 743 ZmAppCtxt.prototype.getChooseAccountDialog = 744 function() { 745 if (!this._chooseAccountDialog) { 746 AjxDispatcher.require("Extras"); 747 this._chooseAccountDialog = new ZmChooseAccountDialog(this._shell); 748 } 749 return this._chooseAccountDialog; 750 }; 751 752 /** 753 * Gets the pick tag dialog. 754 * 755 * @return {ZmPickTagDialog} the pick tag dialog 756 */ 757 ZmAppCtxt.prototype.getPickTagDialog = 758 function() { 759 if (!this._pickTagDialog) { 760 AjxDispatcher.require("Extras"); 761 this._pickTagDialog = new ZmPickTagDialog(this._shell); 762 } 763 return this._pickTagDialog; 764 }; 765 766 /** 767 * Gets the folder notify dialog. 768 * 769 * @return {ZmFolderNotifyDialog} the folder notify dialog 770 */ 771 ZmAppCtxt.prototype.getFolderNotifyDialog = 772 function() { 773 if (!this._folderNotifyDialog) { 774 this._folderNotifyDialog = new ZmFolderNotifyDialog(this._shell); 775 } 776 return this._folderNotifyDialog; 777 }; 778 779 /** 780 * Gets the folder properties dialog. 781 * 782 * @return {ZmFolderPropsDialog} the folder properties dialog 783 */ 784 ZmAppCtxt.prototype.getFolderPropsDialog = 785 function() { 786 if (!this._folderPropsDialog) { 787 this._folderPropsDialog = new ZmFolderPropsDialog(this._shell); 788 } 789 return this._folderPropsDialog; 790 }; 791 792 /** 793 * Gets the share properties dialog. 794 * 795 * @return {ZmSharePropsDialog} the share properties dialog 796 */ 797 ZmAppCtxt.prototype.getSharePropsDialog = 798 function() { 799 if (!this._sharePropsDialog) { 800 AjxDispatcher.require("Share"); 801 this._sharePropsDialog = new ZmSharePropsDialog(this._shell); 802 } 803 return this._sharePropsDialog; 804 }; 805 806 ZmAppCtxt.prototype.getShareSearchDialog = function() { 807 if (!this._shareSearchDialog) { 808 AjxDispatcher.require("Share"); 809 this._shareSearchDialog = new ZmShareSearchDialog({parent:this._shell}); 810 } 811 return this._shareSearchDialog; 812 }; 813 814 /** 815 * Gets the accept share dialog. 816 * 817 * @return {ZmAcceptShareDialog} the accept share dialog 818 */ 819 ZmAppCtxt.prototype.getAcceptShareDialog = 820 function() { 821 if (!this._acceptShareDialog) { 822 AjxDispatcher.require("Share"); 823 this._acceptShareDialog = new ZmAcceptShareDialog(this._shell); 824 } 825 return this._acceptShareDialog; 826 }; 827 828 /** 829 * Gets the decline share dialog. 830 * 831 * @return {ZmDeclineShareDialog} the decline share dialog 832 */ 833 ZmAppCtxt.prototype.getDeclineShareDialog = 834 function() { 835 if (!this._declineShareDialog) { 836 AjxDispatcher.require("Share"); 837 this._declineShareDialog = new ZmDeclineShareDialog(this._shell); 838 } 839 return this._declineShareDialog; 840 }; 841 842 /** 843 * Gets the revoke share dialog. 844 * 845 * @return {ZmRevokeShareDialog} the revoke share dialog 846 */ 847 ZmAppCtxt.prototype.getRevokeShareDialog = 848 function() { 849 if (!this._revokeShareDialog) { 850 AjxDispatcher.require("Share"); 851 this._revokeShareDialog = new ZmRevokeShareDialog(this._shell); 852 } 853 return this._revokeShareDialog; 854 }; 855 856 /** 857 * Gets the timezone picker dialog. 858 * 859 * @return {ZmTimezonePicker} the timezone picker dialog 860 */ 861 ZmAppCtxt.prototype.getTimezonePickerDialog = 862 function() { 863 if (!this._timezonePickerDialog) { 864 AjxDispatcher.require("Share"); 865 this._timezonePickerDialog = new ZmTimezonePicker(this._shell); 866 } 867 return this._timezonePickerDialog; 868 }; 869 870 /** 871 * Gets the filter rule add/edit dialog. 872 * 873 * @return {ZmFilterRuleDialog} the filter rule add/edit dialog 874 */ 875 ZmAppCtxt.prototype.getFilterRuleDialog = 876 function() { 877 if (!this._filterRuleDialog) { 878 AjxDispatcher.require(["PreferencesCore", "Preferences"]); 879 this._filterRuleDialog = new ZmFilterRuleDialog(); 880 } 881 return this._filterRuleDialog; 882 }; 883 884 /** 885 * Gets the priority message filter dialog. 886 * 887 * @return {ZmPriorityMessageFilterDialog} the priority message filter dialog 888 */ 889 ZmAppCtxt.prototype.getPriorityMessageFilterDialog = 890 function() { 891 if (!this._priorityMessageFilterDialog) { 892 AjxDispatcher.require(["PreferencesCore", "Preferences"]); 893 this._priorityMessageFilterDialog = new ZmPriorityMessageFilterDialog(); 894 } 895 return this._priorityMessageFilterDialog; 896 }; 897 898 899 /** 900 * Gets the activity stream prompt dialog for running activity stream filters 901 * 902 * @return {ZmActivityStreamPromptDialog} 903 */ 904 ZmAppCtxt.prototype.getActivityStreamFilterDialog = 905 function() { 906 if (!this._activityStreamFilterDialog) { 907 AjxDispatcher.require(["PreferencesCore", "Preferences"]); 908 this._activityStreamFilterDialog = new ZmActivityStreamPromptDialog(); 909 } 910 return this._activityStreamFilterDialog; 911 }; 912 913 /** 914 * Gets the prompt for moving files from the Activity Stream to the Inbox 915 * 916 * @return {ZmActivityToInboxPromptDialog} 917 */ 918 ZmAppCtxt.prototype.getActivityToInboxFilterDialog = 919 function() { 920 if (!this._activityToInboxFilterDialog) { 921 AjxDispatcher.require(["PreferencesCore", "Preferences"]); 922 this._activityToInboxFilterDialog = new ZmActivityToInboxPromptDialog(); 923 } 924 return this._activityToInboxFilterDialog; 925 }; 926 927 /** 928 * Gets the quickadd dialog for creating a contact 929 * 930 * @return {ZmContactQuickAddDialog} 931 */ 932 ZmAppCtxt.prototype.getContactQuickAddDialog = 933 function() { 934 if (!this._contactQuickAddDialog) { 935 AjxDispatcher.require(["ContactsCore", "Contacts"]); 936 this._contactQuickAddDialog = new ZmContactQuickAddDialog(); 937 } 938 return this._contactQuickAddDialog; 939 }; 940 941 /** 942 * Gets the confirm dialog. 943 * 944 * @return {DwtConfirmDialog} the confirmation dialog 945 */ 946 ZmAppCtxt.prototype.getConfirmationDialog = 947 function(id) { 948 if (!this._confirmDialog) { 949 this._confirmDialog = new DwtConfirmDialog(this._shell, null, "CONFIRM_DIALOG"); 950 } 951 return this._confirmDialog; 952 }; 953 954 /** 955 * Gets the upload dialog. 956 * 957 * @return {ZmUploadDialog} the upload dialog 958 */ 959 ZmAppCtxt.prototype.getUploadDialog = 960 function() { 961 if (!this._uploadDialog) { 962 AjxDispatcher.require(["Extras"]); 963 this._uploadDialog = new ZmUploadDialog(this._shell); 964 } 965 return this._uploadDialog; 966 }; 967 968 /** 969 * Gets the attach dialog. 970 * 971 * @return {ZmAttachDialog} the attach dialog 972 */ 973 ZmAppCtxt.prototype.getAttachDialog = 974 function() { 975 if (!this._attachDialog) { 976 AjxDispatcher.require("Share"); 977 this._attachDialog = new ZmAttachDialog(this._shell); 978 this.runAttachDialogCallbacks(); 979 } 980 return this._attachDialog; 981 }; 982 983 ZmAppCtxt.prototype.getDumpsterDialog = 984 function() { 985 if (!this._dumpsterDialog) { 986 AjxDispatcher.require("Extras"); 987 this._dumpsterDialog = new ZmDumpsterDialog(this._shell); 988 } 989 return this._dumpsterDialog; 990 }; 991 992 993 /** 994 * Gets the mail redirect dialog. 995 * 996 * @return {ZmMailRedirectDialog} the new mail redirect dialog 997 */ 998 ZmAppCtxt.prototype.getMailRedirectDialog = 999 function() { 1000 if (!this._mailRedirectDialog) { 1001 this._mailRedirectDialog = new ZmMailRedirectDialog(this._shell); 1002 } 1003 return this._mailRedirectDialog; 1004 }; 1005 1006 /** 1007 * Gets the mail retention warning dialog. 1008 * 1009 * @return {ZmRetetionWarningDialog} the new mail retention warning dialog 1010 */ 1011 ZmAppCtxt.prototype.getRetentionWarningDialog = 1012 function() { 1013 if (!this._retentionWarningDialog) { 1014 this._retentionWarningDialog = new ZmRetentionWarningDialog(this._shell); 1015 } 1016 return this._retentionWarningDialog; 1017 }; 1018 1019 1020 /** 1021 * Runs the attach dialog callbacks. 1022 * 1023 * @private 1024 */ 1025 ZmAppCtxt.prototype.runAttachDialogCallbacks = 1026 function() { 1027 while(this._attachDialogCallback && this._attachDialogCallback.length > 0) { 1028 var callback = this._attachDialogCallback.shift(); 1029 if(callback && (callback instanceof AjxCallback)) { 1030 callback.run(this._attachDialog); 1031 } 1032 } 1033 }; 1034 1035 /** 1036 * Adds the callback to the attachment dialog callbacks. 1037 * 1038 * @param {AjxCallback} callback the callback 1039 */ 1040 ZmAppCtxt.prototype.addAttachmentDialogCallback = 1041 function(callback) { 1042 if(!this._attachDialogCallback) { 1043 this._attachDialogCallback = []; 1044 } 1045 this._attachDialogCallback.push(callback); 1046 }; 1047 1048 /** 1049 * Gets the upload conflict dialog. 1050 * 1051 * @return {ZmUploadConflictDialog} the upload conflict dialog 1052 */ 1053 ZmAppCtxt.prototype.getUploadConflictDialog = 1054 function() { 1055 if (!this._uploadConflictDialog) { 1056 AjxDispatcher.require(["Extras"]); 1057 this._uploadConflictDialog = new ZmUploadConflictDialog(this._shell); 1058 } 1059 return this._uploadConflictDialog; 1060 }; 1061 1062 /** 1063 * Gets the new briefcase dialog. 1064 * 1065 * @return {ZmNewBriefcaseDialog} the new briefcase dialog 1066 */ 1067 ZmAppCtxt.prototype.getNewBriefcaseDialog = 1068 function() { 1069 if (!this._newBriefcaseDialog) { 1070 AjxDispatcher.require(["BriefcaseCore", "Briefcase"]); 1071 this._newBriefcaseDialog = new ZmNewBriefcaseDialog(this._shell); 1072 } 1073 return this._newBriefcaseDialog; 1074 }; 1075 1076 /** 1077 * Gets the find-and-replace dialog. 1078 * 1079 * @return {ZmFindnReplaceDialog} the find-and-replace dialog 1080 */ 1081 ZmAppCtxt.prototype.getReplaceDialog = 1082 function() { 1083 if (!this._replaceDialog) { 1084 AjxDispatcher.require("Share"); 1085 this._replaceDialog = new ZmFindnReplaceDialog(this._shell); 1086 } 1087 return this._replaceDialog; 1088 }; 1089 1090 /** 1091 * Gets the debug log dialog. 1092 * 1093 * @return {ZmDebugLogDialog} the debug log dialog 1094 */ 1095 ZmAppCtxt.prototype.getDebugLogDialog = 1096 function() { 1097 if (!this._debugLogDialog) { 1098 AjxDispatcher.require("Extras"); 1099 this._debugLogDialog = new ZmDebugLogDialog(this._shell); 1100 } 1101 return this._debugLogDialog; 1102 }; 1103 1104 /** 1105 * Gets the root tab group. 1106 * 1107 * @return {DwtTabGroup} the root tab group 1108 */ 1109 ZmAppCtxt.prototype.getRootTabGroup = 1110 function() { 1111 if (this.isChildWindow) { 1112 if (!this._childWinTabGrp) { 1113 this._childWinTabGrp = new DwtTabGroup("CHILD_WINDOW"); 1114 } 1115 } else { 1116 if (!this._rootTabGrp) { 1117 this._rootTabGrp = new DwtTabGroup("ROOT"); 1118 } 1119 } 1120 return this.isChildWindow ? this._childWinTabGrp : this._rootTabGrp; 1121 }; 1122 1123 /** 1124 * Gets the shell. 1125 * 1126 * @return {DwtShell} the shell 1127 */ 1128 ZmAppCtxt.prototype.getShell = 1129 function() { 1130 return this._shell; 1131 }; 1132 1133 /** 1134 * Sets the shell. 1135 * 1136 * @param {DwtShell} the shell 1137 */ 1138 ZmAppCtxt.prototype.setShell = 1139 function(shell) { 1140 this._shell = shell; 1141 }; 1142 1143 /** 1144 * Gets the active account. 1145 * 1146 * @return {ZmZimbraAccount} the active account 1147 */ 1148 ZmAppCtxt.prototype.getActiveAccount = 1149 function() { 1150 return this.isChildWindow 1151 ? parentAppCtxt.accountList.activeAccount 1152 : this.accountList.activeAccount; 1153 }; 1154 1155 /** 1156 * Gets the active account. 1157 * 1158 * @return {ZmZimbraAccount} the active account 1159 */ 1160 ZmAppCtxt.prototype.isExternalAccount = 1161 function() { 1162 return this.get(ZmSetting.IS_EXTERNAL); 1163 }; 1164 1165 /* 1166 * This is a list of Aspell (Ver. 0.61) support locale from the result of the following command: 1167 * /opt/zimbra/aspell/bin/aspell dump dicts 1168 * (use only the items whose format is "<Primary-tag> *( "_" <Subtag> )") 1169 * When Aspell is upgraded and more locales are added, please update this list too. 1170 */ 1171 ZmAppCtxt.AVAILABLE_DICTIONARY_LOCALES = ["ar", "da", "de", "de_AT", "de_CH", "de_DE", "en", "en_CA", "en_GB", "en_US", "es", "fr", "fr_CH", "fr_FR", "hi", "hu", "it", "nl", "pl", "pt_BR", "ru", "sv"]; 1172 1173 /** 1174 * Gets the availability of the spell check feature based on the current locale and user's configuration 1175 * 1176 * @return {Boolean} <code>true</code> if the spell checker is available. 1177 */ 1178 ZmAppCtxt.prototype.isSpellCheckerAvailable = function () { 1179 1180 if (!appCtxt.get(ZmSetting.SPELL_CHECK_ENABLED)) { 1181 return false; 1182 } 1183 1184 if (typeof this._spellCheckAvailable !== 'undefined') { 1185 return this._spellCheckAvailable; 1186 } 1187 1188 this._spellCheckAvailable = false; 1189 var myLocale = appCtxt.get(ZmSetting.LOCALE_NAME); 1190 var myDefaultDictionaryName = appCtxt.get(ZmSetting.SPELL_DICTIONARY); 1191 1192 var myLanguage = myLocale.split('_')[0]; 1193 1194 var dictLocales = ZmAppCtxt.AVAILABLE_DICTIONARY_LOCALES; 1195 var ln = dictLocales.length; 1196 1197 for (var i = 0; i < ln; i++) { 1198 var dictLocale = dictLocales[i]; 1199 if (dictLocale === myLocale || 1200 dictLocale === myLanguage || 1201 dictLocale === myDefaultDictionaryName) { 1202 this._spellCheckAvailable = true; 1203 break; 1204 } 1205 } 1206 1207 return this._spellCheckAvailable; 1208 } 1209 1210 /** 1211 * Gets the identity collection. 1212 * 1213 * @param {ZmZimbraAccount} account the account 1214 * @return {ZmIdentityCollection} the identity collection 1215 */ 1216 ZmAppCtxt.prototype.getIdentityCollection = function(account) { 1217 1218 var context = this.isChildWindow ? window && window.opener : window; 1219 return context && context.AjxDispatcher.run("GetIdentityCollection", account); 1220 }; 1221 1222 /** 1223 * Gets the data source collection. 1224 * 1225 * @param {ZmZimbraAccount} account the account 1226 * @return {ZmModel} the data source collection 1227 */ 1228 ZmAppCtxt.prototype.getDataSourceCollection = function(account) { 1229 1230 var context = this.isChildWindow ? window && window.opener : window; 1231 return context && context.AjxDispatcher.run("GetDataSourceCollection", account); 1232 }; 1233 1234 /** 1235 * Gets the signature collection. 1236 * 1237 * @param {ZmZimbraAccount} account the account 1238 * @return {ZmSignatureCollection} the signature collection 1239 */ 1240 ZmAppCtxt.prototype.getSignatureCollection = function(account) { 1241 1242 var context = this.isChildWindow ? window && window.opener : window; 1243 return context && context.AjxDispatcher.run("GetSignatureCollection", account); 1244 }; 1245 1246 1247 ZmAppCtxt.prototype.killMarkReadTimer = 1248 function() { 1249 if (this.markReadActionId > 0) { 1250 AjxTimedAction.cancelAction(this.markReadActionId); 1251 this.markReadActionId = -1; 1252 } 1253 }; 1254 /** 1255 * Gets the organizer tree. 1256 * 1257 * @param {ZmOrganizer.FOLDER|ZmOrganizer.TAG|ZmOrganizer.ZIMLET} type the type 1258 * @param {ZmZimbraAccount} account the account 1259 * @return {ZmTree} the tree 1260 * @see #getFolderTree 1261 * @see #getTagTree 1262 * @see #getZimletTree 1263 */ 1264 ZmAppCtxt.prototype.getTree = 1265 function(type, account) { 1266 if (this.isChildWindow) { 1267 return parentAppCtxt.getTree(type, account); 1268 } 1269 1270 var al = this.accountList; 1271 var id = account 1272 ? account.id 1273 : al.activeAccount ? al.activeAccount.id : ZmAccountList.DEFAULT_ID; 1274 1275 var acct = al.getAccount(id); 1276 return acct && acct.trees[ZmOrganizer.TREE_TYPE[type]]; 1277 }; 1278 1279 /** 1280 * Sets the organizer tree. 1281 * 1282 * @param {ZmOrganizer.FOLDER|ZmOrganizer.TAG|ZmOrganizer.ZIMLET} type the type 1283 * @param {ZmTree} tree the tree 1284 * @param {ZmZimbraAccount} account the account 1285 * @see #getTree 1286 */ 1287 ZmAppCtxt.prototype.setTree = 1288 function(type, tree, account) { 1289 var al = this.accountList; 1290 var id = account 1291 ? account.id 1292 : al.activeAccount ? al.activeAccount.id : ZmAccountList.DEFAULT_ID; 1293 1294 1295 var acct = this.accountList.getAccount(id); 1296 if (acct) { 1297 acct.trees[type] = tree; 1298 } 1299 }; 1300 1301 /** 1302 * Gets the folder organizer tree. 1303 * 1304 * @param {ZmZimbraAccount} account the account 1305 * @return {ZmFolderTree} the tree 1306 * @see #getTree 1307 */ 1308 ZmAppCtxt.prototype.getFolderTree = 1309 function(account) { 1310 return this.getTree(ZmOrganizer.FOLDER, account); 1311 }; 1312 1313 /** 1314 * Gets the tag organizer tree. 1315 * 1316 * @param {ZmZimbraAccount} account the account 1317 * @return {ZmTagTree} the tree 1318 * @see #getTree 1319 */ 1320 ZmAppCtxt.prototype.getTagTree = 1321 function(account) { 1322 return this.getTree(ZmOrganizer.TAG, account); 1323 }; 1324 1325 /** 1326 * Gets the tag organizer tree's root. 1327 * 1328 * @param {ZmItem} item item to look up the account of for and get the account tag list. 1329 * @return {ZmTag} the root of the tree, which is also a list. 1330 */ 1331 ZmAppCtxt.prototype.getAccountTagList = 1332 function(item) { 1333 var account = (item && appCtxt.multiAccounts) ? item.getAccount() : null; 1334 1335 return this.getTagTree(account).root; 1336 }; 1337 1338 1339 /** 1340 * Gets the zimlet organizer tree. 1341 * 1342 * @param {ZmZimbraAccount} account the account 1343 * @return {ZmFolderTree} the tree 1344 * @see #getTree 1345 */ 1346 ZmAppCtxt.prototype.getZimletTree = 1347 function(account) { 1348 return this.getTree(ZmOrganizer.ZIMLET, account); 1349 }; 1350 1351 /** 1352 * Gets the username (which is an email address). 1353 * 1354 * @param {ZmZimbraAccount} account the account 1355 * @return {String} the username 1356 */ 1357 ZmAppCtxt.prototype.getUsername = 1358 function(account) { 1359 return this.get(ZmSetting.USERNAME, account); 1360 }; 1361 1362 /** 1363 * Gets the user domain. 1364 * 1365 * @param {ZmZimbraAccount} account the account 1366 * @return {String} the user domain 1367 */ 1368 ZmAppCtxt.prototype.getUserDomain = 1369 function(account) { 1370 if (!this.userDomain) { 1371 var username = this.getUsername(account); 1372 if (username) { 1373 var parts = username.split("@"); 1374 this.userDomain = (parts && parts.length) ? parts[1] : ""; 1375 } 1376 } 1377 return this.userDomain; 1378 }; 1379 1380 /** 1381 * Gets the upload frame id. 1382 * 1383 * @return {String} the frame id 1384 */ 1385 ZmAppCtxt.prototype.getUploadFrameId = 1386 function() { 1387 if (!this._uploadManagerIframeId) { 1388 var iframeId = Dwt.getNextId(); 1389 var html = [ "<iframe name='", iframeId, "' id='", iframeId, 1390 "' src='", (AjxEnv.isIE && location.protocol == "https:") ? appContextPath+"/public/blank.html" : "javascript:\"\"", 1391 "' style='position: absolute; top: 0; left: 0; visibility: hidden'></iframe>" ]; 1392 var div = document.createElement("div"); 1393 div.innerHTML = html.join(""); 1394 document.body.appendChild(div.firstChild); 1395 this._uploadManagerIframeId = iframeId; 1396 } 1397 return this._uploadManagerIframeId; 1398 }; 1399 1400 ZmAppCtxt.prototype.reloadAppCache = 1401 function(force, retryOnError) { 1402 AjxDebug.println(AjxDebug.OFFLINE, "reloadAppCache :: " + AjxDebug._getTimeStamp()); 1403 if (this.isWebClientOfflineSupported || force) { 1404 var localOfflineBrowserKey = localStorage.getItem(ZmSetting.WEBCLIENT_OFFLINE_BROWSER_KEY); 1405 //If application cache status is downloading browser is already downloading the resources mentioned in the manifest file. Resetting the cookie value will result in application cache error event "Manifest changed during update". 1406 if (localOfflineBrowserKey && AjxEnv.supported.applicationcache && applicationCache.status !== applicationCache.DOWNLOADING) { 1407 var cookieValue = localOfflineBrowserKey + "_" + new Date().getTime(); 1408 AjxCookie.setCookie(document, "ZM_OFFLINE_KEY", cookieValue, false, "/"); 1409 } 1410 var manifestURL = appContextPath + "/appcache/images,common,dwt,msgview,login,zm,spellcheck,skin.appcache?"; 1411 var urlParams = []; 1412 urlParams.push("v=" + window.cacheKillerVersion); 1413 urlParams.push("debug=" + window.appDevMode); 1414 urlParams.push("compress=" + !(window.appDevMode === true)); 1415 urlParams.push("templates=only"); 1416 manifestURL = encodeURIComponent(manifestURL + urlParams.join('&')); 1417 var offlineIframe = document.getElementById("offlineIframe"); 1418 if (!offlineIframe) { 1419 offlineIframe = document.createElement("iframe"); 1420 offlineIframe.id = "offlineIframe"; 1421 offlineIframe.style.display = "none"; 1422 document.body.appendChild(offlineIframe); 1423 } 1424 if (offlineIframe) { 1425 retryOnError = AjxUtil.isBoolean(retryOnError) ? retryOnError : true; 1426 offlineIframe.src = "public/Offline.jsp?url=" + manifestURL + "&isFirefox=" + AjxEnv.isFirefox + "&retryOnError=" + retryOnError; 1427 } 1428 } 1429 }; 1430 1431 /** 1432 * Gets the upload manager. 1433 * 1434 * @return {Object} the upload manager 1435 */ 1436 ZmAppCtxt.prototype.getUploadManager = 1437 function() { 1438 if (!this._uploadManager) { 1439 // Create upload manager (for sending attachments) 1440 this._uploadManager = new AjxPost(this.getUploadFrameId()); 1441 } 1442 return this._uploadManager; 1443 }; 1444 1445 ZmAppCtxt.prototype.getZmUploadManager = 1446 function() { 1447 if (!this._zmUploadManager) { 1448 // Create upload manager (for sending attachments) 1449 AjxDispatcher.require("Extras"); 1450 this._zmUploadManager = new ZmUploadManager(); 1451 } 1452 return this._zmUploadManager; 1453 }; 1454 1455 /** 1456 * Gets the current search. 1457 * 1458 * @return {ZmSearch} the current search 1459 */ 1460 ZmAppCtxt.prototype.getCurrentSearch = 1461 function() { 1462 var app = this.getCurrentApp(); 1463 if (app && app.currentSearch) { 1464 return app.currentSearch; 1465 } 1466 var ctlr = this.getCurrentController(); 1467 return ctlr && ctlr._currentSearch; 1468 }; 1469 1470 /** 1471 * Gets the current view id. If we're showing search results, returns the ID of the 1472 * view within the search results (rather than the ID of the search results). 1473 * 1474 * @return {String} the current view id 1475 */ 1476 ZmAppCtxt.prototype.getCurrentViewId = 1477 function() { 1478 var viewId = this.getAppViewMgr().getCurrentViewId(); 1479 if (viewId && viewId.indexOf(ZmId.VIEW_SEARCH_RESULTS) === 0) { 1480 viewId = this.getCurrentController().getCurrentViewId(); 1481 } 1482 return viewId; 1483 }; 1484 1485 /** 1486 * Gets the current view type. If we're showing search results, returns the type of the 1487 * view within the search results (rather than the type of the search results). 1488 * 1489 * @return {String} the current view type 1490 */ 1491 ZmAppCtxt.prototype.getCurrentViewType = 1492 function() { 1493 var viewType = this.getAppViewMgr().getCurrentViewType(); 1494 if (viewType && viewType.indexOf(ZmId.VIEW_SEARCH_RESULTS) === 0) { 1495 viewType = this.getCurrentController().getCurrentViewType(); 1496 } 1497 return viewType; 1498 }; 1499 1500 /** 1501 * Extracts the view type from a view ID. 1502 * 1503 * @param {string} viewId a view ID 1504 * @return {String} the view type 1505 */ 1506 ZmAppCtxt.prototype.getViewTypeFromId = 1507 function(viewId) { 1508 var array = viewId && viewId.split(ZmController.SESSION_ID_SEP); 1509 return array ? array[0] : ""; 1510 }; 1511 1512 /** 1513 * Gets the current view. 1514 * 1515 * @return {DwtComposite} the current view 1516 */ 1517 ZmAppCtxt.prototype.getCurrentView = 1518 function() { 1519 return this.getAppViewMgr().getCurrentView(); 1520 }; 1521 1522 /** 1523 * Gets the current controller. 1524 * 1525 * @return {ZmController} the current controller 1526 */ 1527 ZmAppCtxt.prototype.getCurrentController = 1528 function() { 1529 var view = this.getCurrentView(); 1530 return (view && view.getController) ? view.getController() : null; 1531 }; 1532 1533 /** 1534 * Sets the current list. 1535 * 1536 * @param {ZmList} list the current list 1537 */ 1538 ZmAppCtxt.prototype.setCurrentList = 1539 function(list) { 1540 this._list = list; 1541 }; 1542 1543 /** 1544 * Gets the current list. 1545 * 1546 * @return {ZmList} the current list 1547 */ 1548 ZmAppCtxt.prototype.getCurrentList = 1549 function() { 1550 var ctlr = this.getCurrentController(); 1551 return (ctlr && ctlr.getList) ? ctlr.getList() : this._list ? this._list : null; 1552 }; 1553 1554 ZmAppCtxt.prototype.getActionController = 1555 function() { 1556 if (!this._actionController && !this.isChildWindow) { 1557 this._actionController = new ZmActionController(); 1558 } 1559 return this._actionController; 1560 }; 1561 1562 /** 1563 * Gets a new window. 1564 * 1565 * @param {Boolean} fullView <code>true</code> to include the full version 1566 * @param {int} width the width 1567 * @param {int} height the height 1568 * @param {String} name window name 1569 */ 1570 ZmAppCtxt.prototype.getNewWindow = 1571 function(fullVersion, width, height, name) { 1572 // build url 1573 var url = []; 1574 var i = 0; 1575 url[i++] = document.location.protocol; 1576 url[i++] = "//"; 1577 url[i++] = location.hostname; 1578 url[i++] = (!location.port || location.port == "80") ? "" : (":" + location.port); 1579 url[i++] = appContextPath; 1580 url[i++] = "/public/launchNewWindow.jsp?skin="; 1581 url[i++] = appCurrentSkin; 1582 url[i++] = "&localeId="; 1583 url[i++] = AjxEnv.DEFAULT_LOCALE || ""; 1584 if (fullVersion) { 1585 url[i++] = "&full=1"; 1586 } 1587 if (window.appDevMode) { 1588 url[i++] = "&dev=1"; 1589 } 1590 if (window.appCoverageMode) { 1591 url[i++] = "&coverage=1"; 1592 } 1593 this.__childWindowId = (this.__childWindowId+1) || 0; 1594 url[i++] = "&childId=" + this.__childWindowId; 1595 1596 name = name || "_blank"; 1597 width = width || 705; 1598 height = height || 465; 1599 var args = ["height=", height, ",width=", width, ",location=no,menubar=no,resizable=yes,scrollbars=no,status=yes,toolbar=no"].join(""); 1600 if (window.appDevMode) { 1601 args = ["height=", height, ",width=", width, ",location=yes,menubar=yes,resizable=yes,scrollbars=no,status=yes,toolbar=yes"].join(""); 1602 } 1603 var newWin = window.open(url.join(""), name, args); 1604 this.handlePopupBlocker(newWin); 1605 if(newWin) { 1606 // add this new window to global list so parent can keep track of child windows! 1607 return this.getAppController().addChildWindow(newWin, this.__childWindowId); 1608 } 1609 }; 1610 1611 /** 1612 * Handle Popup bloker for a given window 1613 * @param {Object} win A Window object 1614 */ 1615 ZmAppCtxt.prototype.handlePopupBlocker = 1616 function(win) { 1617 if (!win) { 1618 this.setStatusMsg(ZmMsg.popupBlocker, ZmStatusView.LEVEL_CRITICAL); 1619 } else if (win && AjxEnv.isChrome) { 1620 setTimeout(function() { 1621 if (win.innerHeight == 0) 1622 appCtxt.setStatusMsg(ZmMsg.popupBlocker, ZmStatusView.LEVEL_CRITICAL); 1623 }, 200); 1624 }; 1625 }; 1626 1627 /** 1628 * Caches the given key/value set. 1629 * 1630 * @param {Object} key the key 1631 * @param {Object} value the value 1632 * @private 1633 */ 1634 ZmAppCtxt.prototype.cacheSet = 1635 function(key, value) { 1636 this._itemCache[key] = value; 1637 delete this._itemCacheDeferred[key]; 1638 }; 1639 1640 /** 1641 * Defers caching the given key set. 1642 * 1643 * @param {Object} key the key 1644 * @param {String} appName the application name 1645 * @private 1646 */ 1647 ZmAppCtxt.prototype.cacheSetDeferred = 1648 function(key, appName) { 1649 this._itemCache[key] = this._itemCacheDeferred; 1650 this._itemCacheDeferred[key] = appName; 1651 }; 1652 1653 /** 1654 * Gets the key from cache. 1655 * 1656 * @param {Object} key the key 1657 * @return {Object} the value 1658 * @private 1659 */ 1660 ZmAppCtxt.prototype.cacheGet = 1661 function(key) { 1662 var value = this._itemCache[key]; 1663 if (value === this._itemCacheDeferred) { 1664 var appName = this._itemCacheDeferred[key]; 1665 this.getApp(appName).createDeferred(); 1666 value = this._itemCache[key]; 1667 } 1668 return value; 1669 }; 1670 1671 /** 1672 * Removes the key from cache. 1673 * 1674 * @param {Object} key the key 1675 * @private 1676 */ 1677 ZmAppCtxt.prototype.cacheRemove = 1678 function(key) { 1679 delete this._itemCache[key]; 1680 delete this._itemCacheDeferred[key]; 1681 }; 1682 1683 /** 1684 * Gets the key from cache by id. 1685 * 1686 * @param {String} id the id 1687 * @return {Object} the value 1688 * @private 1689 */ 1690 ZmAppCtxt.prototype.getById = function(id) { 1691 1692 // Try parent cache if we're a child window and we don't have it 1693 return this.cacheGet(id) || (this.isChildWindow && window && window.opener && window.opener.appCtxt && window.opener.appCtxt.getById(id)); 1694 }; 1695 1696 /** 1697 * Gets the keyboard manager 1698 * 1699 * @return {DwtKeyboardMgr} the keyboard manager 1700 */ 1701 ZmAppCtxt.prototype.getKeyboardMgr = 1702 function() { 1703 return this._shell.getKeyboardMgr(); 1704 }; 1705 1706 /** 1707 * Gets the history manager. 1708 * 1709 * @return {AjxHistoryMgr} the history manager 1710 */ 1711 ZmAppCtxt.prototype.getHistoryMgr = 1712 function() { 1713 if (!this._historyMgr) { 1714 this._historyMgr = new AjxHistoryMgr(); 1715 } 1716 return this._historyMgr; 1717 }; 1718 1719 /** 1720 * Checks if the zimlets are present. 1721 * 1722 * @return {Boolean} <code>true</code> if zimlets are present 1723 */ 1724 ZmAppCtxt.prototype.zimletsPresent = 1725 function() { 1726 return this._zimletsPresent; 1727 }; 1728 1729 /** 1730 * Sets if the zimlets are present. 1731 * 1732 * @param {Boolean} zimletsPresent if <code>true</code>, zimlets are present 1733 */ 1734 ZmAppCtxt.prototype.setZimletsPresent = 1735 function(zimletsPresent) { 1736 this._zimletsPresent = zimletsPresent; 1737 }; 1738 1739 /** 1740 * Gets the zimlet manager 1741 * 1742 * @return {ZmZimletMgr} the zimlet manager 1743 */ 1744 ZmAppCtxt.prototype.getZimletMgr = 1745 function() { 1746 if (!this._zimletMgr) { 1747 AjxDispatcher.require("Zimlet"); 1748 if (!this._zimletMgr) // Must re-check here, because if this function is called a second time before the "Zimlet" package is loaded, both calls want to set this._zimletMgr, which must NEVER happen (Issue first located in bug #41338) 1749 this._zimletMgr = new ZmZimletMgr(); 1750 } 1751 return this._zimletMgr; 1752 }; 1753 1754 /** 1755 * Checks if zimlets are loaded. 1756 * 1757 * @return {Boolean} <code>true</code> if zimlets are loaded 1758 */ 1759 ZmAppCtxt.prototype.areZimletsLoaded = 1760 function() { 1761 return this._zimletsLoaded; 1762 }; 1763 1764 /** 1765 * Adds a listener to the zimlets loaded event. 1766 * 1767 * @param {AjxCallback} listener the listener 1768 * @param {int} index the index to where to add the listener 1769 * @return {Boolean} <code>true</code> if the listener is added; <code>false</code> otherwise 1770 */ 1771 ZmAppCtxt.prototype.addZimletsLoadedListener = 1772 function(listener, index) { 1773 if (!this._zimletsLoaded) { 1774 return this._evtMgr.addListener(ZmAppCtxt._ZIMLETS_EVENT, listener, index); 1775 } 1776 }; 1777 1778 /** 1779 * Checks is all zimlets are loaded. 1780 * 1781 * @return {Boolean} <code>true</code> if all zimlets are loaded 1782 */ 1783 ZmAppCtxt.prototype.allZimletsLoaded = 1784 function() { 1785 this._zimletsLoaded = true; 1786 if (this._zimletMgr && !this.isChildWindow && appCtxt.get(ZmSetting.PORTAL_ENABLED)) { 1787 var portletMgr = this.getApp(ZmApp.PORTAL).getPortletMgr(); 1788 if (portletMgr) { 1789 portletMgr.allZimletsLoaded(); 1790 } 1791 } 1792 1793 if (this._evtMgr.isListenerRegistered(ZmAppCtxt._ZIMLETS_EVENT)) { 1794 this._evtMgr.notifyListeners(ZmAppCtxt._ZIMLETS_EVENT, new ZmEvent()); 1795 this._evtMgr.removeAll(ZmAppCtxt._ZIMLETS_EVENT); 1796 } 1797 }; 1798 1799 /** 1800 * Notifies zimlets if they are present and loaded. 1801 * 1802 * @param {String} event the zimlet event (called as a zimlet function) 1803 * @param {Array} args a list of args to the function 1804 * @param {Hash} options a hash of options 1805 * @param {Boolean} options.noChildWindow if <code>true</code>, skip notify if we are in a child window 1806 * @param {Boolean} options.waitUntilLoaded if <code>true</code> and zimlets are not yet loaded, add a listener so that notify happens on load 1807 * @return {Boolean} Returns <code>true</code> if at least one Zimlet handles the notification 1808 */ 1809 ZmAppCtxt.prototype.notifyZimlets = 1810 function(event, args, options) { 1811 this.notifySkin(event, args, options); // Also notify skin 1812 1813 var context = this.isChildWindow ? parentAppCtxt : this; 1814 1815 if (options && options.noChildWindow && this.isChildWindow) { return false; } 1816 1817 if (!context.areZimletsLoaded()) { 1818 if (options && options.waitUntilLoaded) { 1819 context.addZimletsLoadedListener(new AjxListener(this, this.notifyZimlets, [event, args])); 1820 } 1821 return false; 1822 } 1823 1824 return this.getZimletMgr().notifyZimlets(event, args); 1825 }; 1826 1827 ZmAppCtxt.prototype.notifyZimlet = 1828 function(zimletName, event, args, options) { 1829 if (options && options.noChildWindow && this.isChildWindow) { return false; } 1830 return this.getZimletMgr().notifyZimlet(zimletName, event, args); 1831 }; 1832 1833 ZmAppCtxt.prototype.notifySkin = 1834 function(event, args, options) { 1835 var context = this.isChildWindow ? parentAppCtxt : this; 1836 if (options && options.noChildWindow && this.isChildWindow) { return; } 1837 try { 1838 return window.skin && AjxUtil.isFunction(window.skin.handleNotification) && window.skin.handleNotification(event, args); 1839 } catch (e) {} 1840 }; 1841 1842 1843 /** 1844 * Gets the calendar manager. 1845 * 1846 * @return {ZmCalMgr} the calendar manager 1847 */ 1848 ZmAppCtxt.prototype.getCalManager = 1849 function() { 1850 if (!this._calMgr) { 1851 AjxDispatcher.require("Startup2"); 1852 this._calMgr = new ZmCalMgr(this._shell); 1853 } 1854 return this._calMgr; 1855 }; 1856 1857 ZmAppCtxt.prototype.updateOfflineAppt = function(msgId, field, value, nullData, callback) { 1858 var calMgr = appCtxt.getCalManager(); 1859 if (calMgr) { 1860 var calViewController = calMgr && calMgr.getCalViewController(); 1861 if (calViewController) { 1862 var apptCache = calViewController.getApptCache(); 1863 if (apptCache) { 1864 apptCache.updateOfflineAppt(msgId, field, value, nullData, callback); 1865 } 1866 } 1867 } 1868 } 1869 1870 /** 1871 * Gets the task manager. 1872 * 1873 * @return {ZmTaskMgr} the task manager 1874 */ 1875 ZmAppCtxt.prototype.getTaskManager = 1876 function() { 1877 if (!this._taskMgr) { 1878 this._taskMgr = new ZmTaskMgr(this._shell); 1879 } 1880 return this._taskMgr; 1881 }; 1882 1883 1884 /** 1885 * Gets the ACL. 1886 * 1887 * @param {ZmZimbrAccount} account the account 1888 * @param {AjxCallback} callback the callback 1889 * @return {ZmAccessControlList} the ACL 1890 */ 1891 ZmAppCtxt.prototype.getACL = 1892 function(account, callback) { 1893 var al = this.accountList; 1894 var id = account 1895 ? account.id 1896 : al.activeAccount ? al.activeAccount.id : ZmAccountList.DEFAULT_ID; 1897 1898 var acct = al.getAccount(id); 1899 return acct && acct.acl; 1900 }; 1901 1902 /** 1903 * Gets the shortcut hint. 1904 * 1905 * @param {String} keyMap the key map 1906 * @param {String} shortcut the shortcut action 1907 * @return {String} the hint 1908 */ 1909 ZmAppCtxt.prototype.getShortcutHint = 1910 function(keyMap, shortcut) { 1911 1912 var text = null; 1913 keyMap = keyMap || "global"; 1914 while (!text && keyMap) { 1915 var scKey = [keyMap, shortcut, "display"].join("."); 1916 var text = AjxKeys[scKey] || ZmKeys[scKey]; 1917 if (text) { 1918 var list = text.split(/;\s*/); 1919 var sc = list[0]; // use first shortcut in list 1920 if (!sc) { return null; } 1921 sc = sc.replace(/\b[A-Z]\b/g, function(let) { return let.toLowerCase(); }); 1922 text = [" [", sc.replace(",", ""), "]"].join(""); 1923 } else { 1924 var key = [keyMap, "INHERIT"].join("."); 1925 keyMap = AjxKeys[key] || ZmKeys[key]; 1926 } 1927 } 1928 1929 return text; 1930 }; 1931 1932 /** 1933 * Gets the shortcuts panel. 1934 * 1935 * @return {ZmShortcutsPanel} the shortcuts panel 1936 */ 1937 ZmAppCtxt.prototype.getShortcutsPanel = 1938 function() { 1939 if (!this._shortcutsPanel) { 1940 AjxDispatcher.require(["PreferencesCore", "Preferences"]); 1941 var style = this.isChildWindow ? ZmShortcutList.WINDOW_STYLE : ZmShortcutList.PANEL_STYLE; 1942 this._shortcutsPanel = new ZmShortcutsPanel(style); 1943 } 1944 return this._shortcutsPanel; 1945 }; 1946 1947 /** 1948 * Opens a new change password window 1949 * 1950 */ 1951 ZmAppCtxt.prototype.getChangePasswordWindow = 1952 function(ev) { 1953 var url = appCtxt.get(ZmSetting.CHANGE_PASSWORD_URL); 1954 1955 if (!url) { 1956 var isHttp = appCtxt.get(ZmSetting.PROTOCOL_MODE) == ZmSetting.PROTO_HTTP; 1957 var proto = isHttp ? ZmSetting.PROTO_HTTP : ZmSetting.PROTO_HTTPS; 1958 var port = appCtxt.get(isHttp ? ZmSetting.HTTP_PORT : ZmSetting.HTTPS_PORT); 1959 var path = appContextPath+"/h/changepass"; 1960 1961 var publicUrl = appCtxt.get(ZmSetting.PUBLIC_URL); 1962 if (publicUrl) { 1963 var parts = AjxStringUtil.parseURL(publicUrl); 1964 path = parts.path + "/h/changepass"; 1965 var switchMode = (parts.protocol == "http" && proto == ZmSetting.PROTO_HTTPS); 1966 proto = switchMode ? proto : parts.protocol; 1967 port = switchMode ? port : parts.port; 1968 } 1969 var qsArgs = {skin: appCurrentSkin}; 1970 url = AjxUtil.formatUrl({protocol: proto, port: port, path: path, qsReset: true, qsArgs: qsArgs}); 1971 } 1972 1973 var args = "height=465,width=705,location=no,menubar=no,resizable=yes,scrollbars=no,status=yes,toolbar=no"; 1974 window.open(url,'ChangePasswordWindow', args); 1975 }; 1976 1977 /** 1978 * Gets the skin hint for the given argument(s), which will be used to look 1979 * successively down the properties chain. 1980 * 1981 * <p> 1982 * For example, <code>getSkinHint("a", "b")</code> will return the value of <code>skin.hints.a.b</code>. 1983 * </p> 1984 * 1985 * @return {String} the skin hint 1986 */ 1987 ZmAppCtxt.prototype.getSkinHint = 1988 function() { 1989 if (arguments.length == 0) return ""; 1990 1991 var cur = window.skin && window.skin.hints; 1992 if (!cur) { return ""; } 1993 for (var i = 0; i < arguments.length; i++) { 1994 var arg = arguments[i]; 1995 if (!cur[arg]) { return ""; } 1996 cur = cur[arg]; 1997 } 1998 return cur; 1999 }; 2000 2001 /** 2002 * Gets the auto completer. 2003 * 2004 * @return {ZmAutocomplete} the auto completer 2005 */ 2006 ZmAppCtxt.prototype.getAutocompleter = 2007 function() { 2008 if (!this._autocompleter) { 2009 this._autocompleter = new ZmAutocomplete(null); 2010 } 2011 return this._autocompleter; 2012 }; 2013 2014 /** 2015 * Checks if my address belongs to the current user (include aliases). 2016 * 2017 * @param {String} addr the address 2018 * @param {Boolean} allowLocal if <code>true</code>, domain is not required 2019 * @param {Boolean} excludeAllowFromAddress if <code>true</code>, addresses in zimbraAllowFromAddresses are ignored 2020 * @return {Boolean} <code>true</code> if the given address belongs to the current user; <code>false</code> otherwise 2021 */ 2022 ZmAppCtxt.prototype.isMyAddress = 2023 function(addr, allowLocal, excludeAllowFromAddress) { 2024 2025 if (allowLocal && (addr.indexOf('@') == -1)) { 2026 addr = [addr, this.getUserDomain()].join("@"); 2027 } 2028 2029 if (addr == this.get(ZmSetting.USERNAME)) { 2030 return true; 2031 } 2032 2033 var allAddresses; 2034 if(excludeAllowFromAddress){ 2035 allAddresses= appCtxt.get(ZmSetting.MAIL_ALIASES); 2036 } 2037 else 2038 { 2039 allAddresses= appCtxt.get(ZmSetting.MAIL_ALIASES).concat(appCtxt.get(ZmSetting.ALLOW_FROM_ADDRESSES)); 2040 } 2041 2042 if (allAddresses && allAddresses.length) { 2043 for (var i = 0; i < allAddresses.length; i++) { 2044 if (addr == allAddresses[i]) 2045 return true; 2046 } 2047 } 2048 2049 return false; 2050 }; 2051 2052 /** 2053 * Gets the overview ID, prepending account name if multi-account. 2054 * 2055 * @param {Array} parts an array of {String} id components 2056 * @param {ZmAccount} account the account 2057 * @return {String} the id 2058 */ 2059 ZmAppCtxt.prototype.getOverviewId = 2060 function(parts, account) { 2061 2062 var id = (parts instanceof Array) ? parts.join("_") : parts; 2063 2064 if (appCtxt.multiAccounts && (account !== null)) { 2065 account = account || appCtxt.getActiveAccount(); 2066 id = [account.name, id].join(":"); 2067 } 2068 2069 return id; 2070 }; 2071 2072 /** 2073 * Gets the autocomplete cache for the given parameters, optionally creating one. 2074 * 2075 * @param {String} acType the item type 2076 * @param {String} str the autocomplete string 2077 * @param {ZmAccount} account the account 2078 * @param {Boolean} create if <code>true</code>, create a cache if none found 2079 */ 2080 ZmAppCtxt.prototype.getAutocompleteCache = 2081 function(acType, str, account, create) { 2082 2083 var cache = null; 2084 var acct = account || this.getActiveAccount(); 2085 if (acct) { 2086 if (this._acCache[acct.id] && this._acCache[acct.id][acType]) { 2087 cache = this._acCache[acct.id][acType][str]; 2088 } 2089 } 2090 if (!cache && create) { 2091 if (acct && !this._acCache[acct.id]) { 2092 this._acCache[acct.id] = {}; 2093 } 2094 if (!this._acCache[acct.id][acType]) { 2095 this._acCache[acct.id][acType] = {}; 2096 } 2097 cache = this._acCache[acct.id][acType][str] = {}; 2098 } 2099 2100 return cache; 2101 }; 2102 2103 /** 2104 * Clears the autocomplete cache. 2105 * 2106 * @param {String} type item type 2107 * @param {ZmAccount} account the account 2108 */ 2109 ZmAppCtxt.prototype.clearAutocompleteCache = 2110 function(type, account) { 2111 2112 var acct = account || appCtxt.getActiveAccount(); 2113 if (this._acCache[acct.id]) { 2114 if (type) { 2115 this._acCache[acct.id][type] = {}; 2116 } else { 2117 this._acCache[acct.id][ZmAutocomplete.AC_TYPE_CONTACT] = {}; 2118 this._acCache[acct.id][ZmAutocomplete.AC_TYPE_LOCATION] = {}; 2119 this._acCache[acct.id][ZmAutocomplete.AC_TYPE_EQUIPMENT] = {}; 2120 } 2121 } 2122 }; 2123 2124 ZmAppCtxt.prototype.getOutsideMouseEventMgr = 2125 function() { 2126 return DwtOutsideMouseEventMgr.INSTANCE; 2127 }; 2128 2129 2130 /** 2131 * @return Returns language specific charset. Currently supports only Japanese. 2132 * Returns "Windows-31J" for Japanese or returns "UTF-8" for everything else 2133 */ 2134 ZmAppCtxt.prototype.getCharset = 2135 function() { 2136 var lang = AjxEnv.isIE ? window.navigator.systemLanguage : window.navigator.language; 2137 //Currently only differs for Japanese, but can extend for different languages as/if we need it. 2138 if((AjxEnv.DEFAULT_LOCALE == "ja" || lang == "ja") && AjxEnv.isWindows) { 2139 return "Windows-31J"; 2140 } else { 2141 return "UTF-8"; 2142 } 2143 }; 2144 2145 /** 2146 * Returns true if an address is an expandable DL. 2147 * 2148 * @param {string} addr email address 2149 */ 2150 ZmAppCtxt.prototype.isExpandableDL = 2151 function(addr) { 2152 return addr && this._isExpandableDL[addr] && this.get("EXPAND_DL_ENABLED"); 2153 }; 2154 2155 /** 2156 * Cache whether an address is an expandable DL. 2157 * 2158 * @param {string} addr email address 2159 * @param {boolean} isExpandableDL if true, address is expandable DL 2160 * 2161 * TODO: consider caching AjxEmailAddress objects by addr so we also save display name 2162 */ 2163 ZmAppCtxt.prototype.setIsExpandableDL = 2164 function(addr, isExpandableDL) { 2165 this._isExpandableDL[addr] = isExpandableDL; 2166 }; 2167 2168 ZmAppCtxt.prototype.getToolTipMgr = 2169 function() { 2170 if (!this._toolTipMgr) { 2171 this._toolTipMgr = new ZmToolTipMgr(); 2172 } 2173 return this._toolTipMgr; 2174 }; 2175 2176 /** 2177 * Returns true if Prism and the user is online 2178 * 2179 */ 2180 ZmAppCtxt.prototype.isZDOnline = 2181 function() { 2182 var ac = window["appCtxt"].getAppController(); 2183 return !AjxEnv.isPrism || (ac._isPrismOnline && ac._isUserOnline); 2184 }; 2185 2186 /** 2187 * When using pre-auth window.opener.appCtxt may not be accessible. This function 2188 * handles appCtxt assignment to avoid a permission denied error 2189 * @return {Object} ZmAppCtxt 2190 */ 2191 ZmAppCtxt.handleWindowOpener = 2192 function() { 2193 try { 2194 return window && window.opener && window.opener.appCtxt || appCtxt; 2195 } 2196 catch (ex) { 2197 return appCtxt; 2198 } 2199 }; 2200 2201 ZmAppCtxt.prototype.isWebClientOffline = 2202 function() { 2203 if (this.isWebClientOfflineSupported) { 2204 return ZmOffline.isServerReachable === false; 2205 } 2206 return false; 2207 }; 2208 2209 ZmAppCtxt.prototype.initWebOffline = 2210 function() { 2211 this.isWebClientOfflineSupported = false; 2212 if (!AjxEnv.isOfflineSupported || !appCtxt.get(ZmSetting.WEBCLIENT_OFFLINE_ENABLED)) { 2213 AjxDebug.println(AjxDebug.OFFLINE, "isWebClientOfflineSupported :: false"); 2214 return; 2215 } 2216 var offlineBrowserKey = appCtxt.get(ZmSetting.WEBCLIENT_OFFLINE_BROWSER_KEY); 2217 var localOfflineBrowserKey = localStorage.getItem(ZmSetting.WEBCLIENT_OFFLINE_BROWSER_KEY); 2218 if (offlineBrowserKey && offlineBrowserKey.indexOf(localOfflineBrowserKey) !== -1) { 2219 this.isWebClientOfflineSupported = true; 2220 this.webClientOfflineHandler = new ZmOffline(); 2221 } 2222 AjxDebug.println(AjxDebug.OFFLINE, "isWebClientOfflineSupported :: "+ this.isWebClientOfflineSupported); 2223 }; 2224 2225 /** 2226 * Gets the offline settings dialog. 2227 * 2228 * @return {ZmOfflineSettingsDialog} offline settings dialog 2229 */ 2230 ZmAppCtxt.prototype.getOfflineSettingsDialog = 2231 function() { 2232 if (!this._offlineSettingsDialog) { 2233 this._offlineSettingsDialog = new ZmOfflineSettingsDialog(); 2234 } 2235 return this._offlineSettingsDialog; 2236 }; 2237 2238 /** 2239 * Returns true if the given ID is not local. That's the case if the ID has 2240 * an account part that is not the active account. 2241 * 2242 * @param {String|Number} id 2243 * @returns {Boolean} true if the given ID is not local 2244 */ 2245 ZmAppCtxt.prototype.isRemoteId = function(id) { 2246 id = String(id); 2247 var acct = appCtxt.getActiveAccount(); 2248 return (id.indexOf(":") !== -1) && (id.indexOf(acct.id) !== 0); 2249 }; 2250 2251 /** 2252 * Returns the singleton AjxClipboard instance, if it is supported. 2253 * 2254 * @returns {AjxClipboard} 2255 */ 2256 ZmAppCtxt.prototype.getClipboard = function() { 2257 return AjxClipboard.isSupported() ? new AjxClipboard() : null; 2258 }; 2259 2260 /** 2261 * Checks a precondition which may be in one of several forms: a boolean, a settings constant, a function, or a list. 2262 * 2263 * @param {Boolean|String|Function|Array} precondition something that evaluates to true or false 2264 * @param {Boolean} listAny (optional) if a list is provided, whether just one (instead of all) must be true 2265 * 2266 * @return boolean false if the precondition evaluates to false or null, otherwise true 2267 */ 2268 ZmAppCtxt.prototype.checkPrecondition = function(precondition, listAny) { 2269 2270 // Lack of a precondition evaluates to true 2271 if (precondition === undefined) { 2272 return true; 2273 } 2274 2275 // A precondition of null should not happen 2276 if (precondition === null) { 2277 return false; 2278 } 2279 2280 // Boolean speaks for itself 2281 if (AjxUtil.isBoolean(precondition)) { 2282 return precondition; 2283 } 2284 2285 // Client setting: fetch value from settings 2286 if (AjxUtil.isString(precondition) && ZmSetting.hasOwnProperty(precondition)) { 2287 return appCtxt.get(precondition); 2288 } 2289 2290 // Function: evaluate and return result 2291 if (AjxUtil.isFunction(precondition)) { 2292 return precondition(); 2293 } 2294 2295 // Array can be treated in one of two modes, where all have to be true, or just one does 2296 if (AjxUtil.isArray(precondition)) { 2297 for (var i = 0; i < precondition.length; i++) { 2298 var result = this.checkPrecondition(precondition[i]); 2299 if (listAny && result) { 2300 return true; 2301 } 2302 if (!listAny && !result) { 2303 return false; 2304 } 2305 } 2306 return !listAny; 2307 } 2308 2309 return true; 2310 }; 2311 2312 /** 2313 * Displays an error message in dialog. 2314 * 2315 * @param {string} errMsg error message 2316 * @param {string} details (optional) additional info to show using Details button 2317 * @param {string} style (optional) style constant DwtMessageDialog.*_STYLE 2318 * @param {string} title (optional) title for dialog (other than one for the style) 2319 * @apram {boolean} noReport do not add a Report button/function to the dialog (defaults to true) 2320 */ 2321 ZmAppCtxt.prototype.showError = function(params) { 2322 2323 params = params || {}; 2324 var errMsg = params.errMsg || params; 2325 var dlg = this.getErrorDialog(); 2326 dlg.reset(); 2327 dlg.setMessage(errMsg, params.details, params.style || DwtMessageDialog.WARNING_STYLE, params.title); 2328 dlg.popup(null, params.noReport !== false); 2329 }; 2330