1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 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) 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * @overview 26 */ 27 28 /** 29 * Creates an overview container for a multi-account mailbox. 30 * @class 31 * Creates a header tree item for an account if mailbox has multiple accounts 32 * configured. For each account header, a {@link ZmOverview} is added a child. If mailbox 33 * only has one account configured, no account header is created and the 34 * {@link ZmOverview} is added as a child of the container. 35 * 36 * @param {Hash} params a hash of parameters 37 * 38 * @author Parag Shah 39 */ 40 ZmAccountOverviewContainer = function(params) { 41 if (arguments.length == 0) { return; } 42 43 ZmOverviewContainer.call(this, params); 44 45 this._vFolderTreeItemMap = {}; 46 this._settingChangeListener = new AjxListener(this, this._handleSettingChange); 47 48 var mouseListener = new AjxListener(this, this._mouseListener); 49 this.addListener(DwtEvent.ONMOUSEDOWN, mouseListener); 50 this.addListener(DwtEvent.ONMOUSEUP, mouseListener); 51 }; 52 53 ZmAccountOverviewContainer.prototype = new ZmOverviewContainer; 54 ZmAccountOverviewContainer.prototype.constructor = ZmAccountOverviewContainer; 55 56 57 // Consts 58 ZmAccountOverviewContainer.VIRTUAL_FOLDERS = [ 59 ZmFolder.ID_INBOX, 60 ZmFolder.ID_SENT, 61 ZmFolder.ID_DRAFTS, 62 ZmFolder.ID_SPAM, 63 ZmFolder.ID_OUTBOX, 64 ZmFolder.ID_TRASH 65 ]; 66 67 68 // Public methods 69 70 /** 71 * Returns a string representation of the object. 72 * 73 * @return {String} a string representation of the object 74 */ 75 ZmAccountOverviewContainer.prototype.toString = 76 function() { 77 return "ZmAccountOverviewContainer"; 78 }; 79 80 ZmAccountOverviewContainer.prototype.getHeaderItem = 81 function(account) { 82 return account && this._headerItems[account.id]; 83 }; 84 85 /** 86 * Expands the given account only (collapses all other accounts). 87 * 88 * @param {ZmAccount} account the account to expand 89 */ 90 ZmAccountOverviewContainer.prototype.expandAccountOnly = 91 function(account) { 92 if (!account) { 93 account = appCtxt.getActiveAccount(); 94 } 95 96 for (var i in this._headerItems) { 97 this._headerItems[i].setExpanded((i == account.id), false, false); 98 } 99 }; 100 101 ZmAccountOverviewContainer.prototype.getSelected = 102 function() { 103 var selected = ZmOverviewContainer.prototype.getSelected.call(this); 104 105 if (!selected) { 106 selected = this.getSelection()[0]; 107 var account = selected && appCtxt.accountList.getAccount(selected.getData(Dwt.KEY_ID)); 108 var tree = account && appCtxt.getFolderTree(account); 109 return tree && tree.root; 110 } 111 112 return selected; 113 }; 114 115 /** 116 * Initializes the account overview. 117 * 118 * @param {Hash} params a hash of parameters 119 * 120 */ 121 ZmAccountOverviewContainer.prototype.initialize = 122 function(params) { 123 124 var header, acct; 125 var accounts = appCtxt.accountList.visibleAccounts; 126 var showAllMailboxes = (appCtxt.isOffline && this._appName == ZmApp.MAIL && (accounts.length > 2)); 127 var showBackgroundColor = showAllMailboxes; 128 var mainAcct = appCtxt.accountList.mainAccount; 129 var origOmit = params.omit; 130 131 for (var i = 0; i < accounts.length; i++) { 132 acct = accounts[i]; 133 // skip the main account in offline mode since we'll add it at the end 134 if (appCtxt.isOffline && acct.isMain && this._appName != ZmApp.PREFERENCES) { continue; } 135 if (!acct.active) { continue; } 136 137 params.omit = {}; 138 139 if (acct.type == ZmAccount.TYPE_POP) { 140 params.omit[ZmFolder.ID_SPAM] = true; 141 params.omit[ZmFolder.ID_OUTBOX] = true; 142 } 143 144 if(params.overviewId && !params.isAppOverview) { 145 var overviewId = params.overviewId.split(":")[1]; 146 if(overviewId && (overviewId == "ZmNewOrganizerDialog")){ 147 params.omit[ZmFolder.ID_DRAFTS] = true; 148 } 149 } 150 151 this._addAccount(params, acct, showBackgroundColor, null, "account" + i); 152 153 header = this.getHeaderItem(acct); 154 if (header) { 155 this._setupHeader(header, acct); 156 } 157 158 this.updateAccountInfo(acct, true, true); 159 160 showBackgroundColor = !showBackgroundColor; 161 } 162 163 // add "All Mailboxes" 164 skip = origOmit && origOmit[ZmOrganizer.ID_ALL_MAILBOXES]; 165 if (showAllMailboxes && !skip) { 166 var text = ZmMsg[ZmFolder.MSG_KEY[ZmOrganizer.ID_ALL_MAILBOXES]]; 167 var hdrText = appCtxt.get(ZmSetting.OFFLINE_ALL_MAILBOXES_TREE_OPEN) 168 ? text : this._getFolderLabel(ZmOrganizer.ID_INBOX, text); 169 var params1 = { 170 parent: this, 171 text: hdrText, 172 imageInfo: "AccountAll" 173 }; 174 var showAllMboxes = appCtxt.get(ZmSetting.OFFLINE_SHOW_ALL_MAILBOXES); 175 var allTi = this._allMailboxesTreeHeader = new DwtTreeItem(params1); 176 allTi.setData(Dwt.KEY_ID, ZmOrganizer.ID_ALL_MAILBOXES); 177 allTi.addClassName("ZmOverviewGlobalInbox"); 178 allTi._initialize(0, true); 179 allTi.setVisible(showAllMboxes); 180 allTi.__origText = text; 181 if (showAllMboxes) { 182 this.highlightAllMboxes(); 183 } 184 185 var setting = appCtxt.getSettings(mainAcct).getSetting(ZmSetting.OFFLINE_SHOW_ALL_MAILBOXES); 186 setting.addChangeListener(this._settingChangeListener); 187 188 var folders = ZmAccountOverviewContainer.VIRTUAL_FOLDERS; 189 for (var i = 0; i < folders.length; i++) { 190 var folderId = folders[i]; 191 192 // add virtual system folders 193 params1 = { 194 parent: allTi, 195 text: this._getFolderLabel(folderId, ZmMsg[ZmFolder.MSG_KEY[folderId]]), 196 imageInfo: ZmFolder.ICON[folderId] 197 }; 198 var ti = this._vFolderTreeItemMap[folderId] = new DwtTreeItem(params1); 199 ti.setData(Dwt.KEY_ID, folderId); 200 ti.addClassName("DwtTreeItemChildDiv"); 201 ti._initialize(null, true); 202 ti.setToolTipContent(appCtxt.accountList.getTooltipForVirtualFolder(folderId)); 203 } 204 205 // add global searches 206 params1 = { 207 parent: allTi, 208 text: ZmMsg.globalSearches, 209 imageInfo: "SearchFolder" 210 }; 211 var searchTi = this._searchTreeHeader = new DwtTreeItem(params1); 212 searchTi.addClassName("DwtTreeItemChildDiv"); 213 searchTi._initialize(null, true); 214 searchTi.__isSearch = true; 215 216 var root = appCtxt.getById(ZmOrganizer.ID_ROOT, mainAcct); 217 var searchFolders = root.getByType(ZmOrganizer.SEARCH); 218 for (var i = 0; i < searchFolders.length; i++) { 219 var folder = searchFolders[i]; 220 if (folder.id != ZmOrganizer.ID_ALL_MAILBOXES && 221 folder.isOfflineGlobalSearch) 222 { 223 this.addSearchFolder(folder); 224 } 225 } 226 searchTi.setVisible(searchTi.getItemCount() > 0); 227 228 // bug #43734 - set expand/collapse state based on user's pref 229 if (appCtxt.get(ZmSetting.OFFLINE_SAVED_SEARCHES_TREE_OPEN)) { 230 searchTi.setExpanded(true, null, true); 231 } 232 if (appCtxt.get(ZmSetting.OFFLINE_ALL_MAILBOXES_TREE_OPEN)) { 233 allTi.setExpanded(true, null, true); 234 } 235 } 236 237 // add the "local" account last 238 if (appCtxt.isOffline) { 239 var params2 = AjxUtil.hashCopy(params); 240 params2.omit = {}; 241 242 if (this._appName != ZmApp.PREFERENCES) { 243 this._addAccount(params2, mainAcct, showBackgroundColor, "ZmOverviewLocalHeader", "LocalFolders"); 244 245 header = this.getHeaderItem(mainAcct); 246 header.setExpanded(appCtxt.get(ZmSetting.ACCOUNT_TREE_OPEN, null, mainAcct)); 247 248 this.updateAccountInfo(mainAcct, false, true); 249 } 250 else { 251 var params3 = { 252 parent: this, 253 text: mainAcct.getDisplayName(), 254 imageInfo: mainAcct.getIcon() 255 }; 256 var localPrefTi = new DwtTreeItem(params3); 257 localPrefTi._initialize(null, true); 258 259 var globalPrefOverviewId = appCtxt.getOverviewId(this.containerId, mainAcct); 260 var tv = this._overview[globalPrefOverviewId].getTreeView(ZmOrganizer.PREF_PAGE); 261 var importExportTi = tv.getTreeItemById("PREF_PAGE_IMPORT_EXPORT"); 262 263 tv.getHeaderItem().removeChild(importExportTi); 264 localPrefTi._addItem(importExportTi); 265 importExportTi.addClassName("DwtTreeItemChildDiv"); 266 localPrefTi.setExpanded(true, null, true); 267 } 268 } 269 270 // add zimlets at the end of all overviews 271 var skip = params.omit && params.omit[ZmOrganizer.ID_ZIMLET]; 272 273 if (!appCtxt.inStartup && !skip && appCtxt.getZimletMgr().getPanelZimlets().length == 0) { 274 skip = true; 275 } 276 277 if (!skip) { 278 AjxDispatcher.require("Zimlet"); 279 } 280 281 if (!skip && window[ZmOverviewController.CONTROLLER[ZmOrganizer.ZIMLET]] && 282 this._appName != ZmApp.PREFERENCES) 283 { 284 var headerLabel = ZmOrganizer.LABEL[ZmOrganizer.ZIMLET]; 285 var headerDataId = params.overviewId = appCtxt.getOverviewId([this.containerId, headerLabel], null); 286 var headerParams = { 287 label: ZmMsg[headerLabel], 288 icon: "Zimlet", 289 dataId: headerDataId, 290 className: "ZmOverviewZimletHeader" 291 }; 292 params.overviewTrees = [ZmOrganizer.ZIMLET]; 293 294 this._addSection(headerParams, null, params); 295 296 var header = this._headerItems[headerDataId]; 297 if (header) { 298 header.__isZimlet = true; 299 header.setExpanded(appCtxt.get(ZmSetting.ZIMLET_TREE_OPEN, null, mainAcct)); 300 } 301 } 302 303 this._initializeActionMenu(); 304 }; 305 306 /** 307 * Adds a search folder. 308 * 309 * @param {ZmFolder} folder the folder 310 */ 311 ZmAccountOverviewContainer.prototype.addSearchFolder = 312 function(folder) { 313 if (!this._searchTreeHeader) { return; } 314 315 var params = { 316 parent: this._searchTreeHeader, 317 text: folder.getName(), 318 imageInfo: folder.getIcon() 319 }; 320 var ti = new DwtTreeItem(params); 321 ti.setData(Dwt.KEY_ID, folder); 322 ti._initialize(null, true); 323 324 if (!this._searchTreeHeader.getVisible()) { 325 this._searchTreeHeader.setVisible(true); 326 } 327 }; 328 329 /** 330 * Sets/updates the account-level status icon next to account name tree header. 331 * This only applies to app-based overview containers (i.e. not dialogs). Also resets the 332 * tooltip for the account header tree item. 333 * 334 * @param {ZmZimbraAccount} account the account to update status icon for 335 * @param {Boolean} updateStatus if <code>true</code>, update the status 336 * @param {Boolean} updateTooltip if <code>true</code>, update the tool tip 337 */ 338 ZmAccountOverviewContainer.prototype.updateAccountInfo = 339 function(account, updateStatus, updateTooltip) { 340 // check if appName is a real app (and not a dialog) before setting the account status 341 var hi = appCtxt.getApp(this._appName) && this.getHeaderItem(account); 342 if (hi) { 343 if (updateStatus) { 344 var html = (account.status == ZmZimbraAccount.STATUS_RUNNING) 345 ? ("<img src='/img/animated/ImgSpinner.gif' width=16 height=16 border=0>") 346 : (AjxImg.getImageHtml(account.getStatusIcon())); 347 348 if (hi._extraCell) { 349 hi._extraCell.innerHTML = (html || ""); 350 } 351 if (appCtxt.isOffline && account.status == ZmZimbraAccount.STATUS_AUTHFAIL) { 352 var dialog = appCtxt.getPasswordChangeDialog(); 353 dialog.popup(account); 354 } 355 } 356 357 if (updateTooltip || updateStatus) { 358 hi.setToolTipContent(account.getToolTip()); 359 } 360 } 361 }; 362 363 /** 364 * Updates the label. 365 * 366 * @param {ZmOrganizer} organizer the organizer 367 */ 368 ZmAccountOverviewContainer.prototype.updateLabel = 369 function(organizer) { 370 // update account header if necessary 371 if (organizer.nId == ZmOrganizer.ID_INBOX) { 372 var acct = organizer.getAccount(); 373 var header = this.getHeaderItem(acct); 374 if (header && !header.getExpanded()) { 375 header.setText(this._getAccountHeaderLabel(acct)); 376 } 377 } 378 379 // update virtual folder label 380 var ti = this._vFolderTreeItemMap[organizer.nId]; 381 if (ti) { 382 ti.setText(this._getFolderLabel(organizer.nId, organizer.name)); 383 if (organizer.nId == ZmOrganizer.ID_INBOX && 384 !this._allMailboxesTreeHeader.getExpanded()) 385 { 386 var text = this._getFolderLabel(organizer.nId, this._allMailboxesTreeHeader.__origText); 387 this._allMailboxesTreeHeader.setText(text); 388 } 389 } 390 }; 391 392 /** 393 * Updates the tool tip. 394 * 395 * @param {String} folderId the folder id 396 * 397 */ 398 ZmAccountOverviewContainer.prototype.updateTooltip = 399 function(folderId) { 400 var ti = this._allMailboxesTreeHeader && this._vFolderTreeItemMap[folderId]; 401 if (ti) { 402 ti.setToolTipContent(appCtxt.accountList.getTooltipForVirtualFolder(folderId)); 403 } 404 }; 405 406 ZmAccountOverviewContainer.prototype.resetOperations = 407 function(parent, data) { 408 409 var emptyFolderOp = parent.getOp(ZmOperation.EMPTY_FOLDER); 410 411 if (data instanceof ZmSearchFolder) { 412 parent.getOp(ZmOperation.MARK_ALL_READ).setVisible(false); 413 emptyFolderOp.setVisible(false); 414 parent.getOp(this._newOp).setVisible(false); 415 if (appCtxt.isOffline) { 416 parent.getOp(ZmOperation.SYNC).setVisible(false); 417 } 418 parent.getOp(ZmOperation.DELETE).setVisible(true); 419 return; 420 } 421 422 var acct = appCtxt.accountList.getAccount(data); 423 var isAcctType = (acct || data == ZmOrganizer.ID_ALL_MAILBOXES); 424 425 parent.getOp(ZmOperation.MARK_ALL_READ).setVisible(!isAcctType); 426 emptyFolderOp.setVisible(false); 427 parent.getOp(this._newOp).setVisible(isAcctType && data != ZmOrganizer.ID_ALL_MAILBOXES); 428 if (appCtxt.isOffline) { 429 parent.getOp(ZmOperation.SYNC).setVisible(isAcctType && (!acct || (acct && !acct.isMain))); 430 } 431 parent.getOp(ZmOperation.DELETE).setVisible(false); 432 433 if (isAcctType) { 434 parent.enable(this._newOp, true); 435 parent.enable(ZmOperation.SYNC, (!acct || (acct && !acct.isMain))); 436 } else { 437 // reset mark all based on a "friendly" hack ;) 438 var markAllEnabled = false; 439 if (data != ZmOrganizer.ID_OUTBOX && data != ZmFolder.ID_DRAFTS && 440 this._actionedHeaderItem.getText().indexOf("bold") != -1) 441 { 442 markAllEnabled = true; 443 } 444 parent.enable(ZmOperation.MARK_ALL_READ, markAllEnabled); 445 446 // reset empty "folder" based on Trash/Junk 447 if (data == ZmOrganizer.ID_TRASH || data == ZmOrganizer.ID_SPAM) { 448 var text = (data == ZmOrganizer.ID_TRASH) ? ZmMsg.emptyTrash : ZmMsg.emptyJunk; 449 emptyFolderOp.setText(text); 450 emptyFolderOp.setVisible(true); 451 parent.enable(ZmOperation.EMPTY_FOLDER, !this._isFolderEmpty(data)); 452 } 453 } 454 }; 455 456 // HACK - when the overview container for mail is initially created, zimlet 457 // data has yet to be parsed so we remove the zimlet section after zimlet load 458 // if there are no panel zimlets. 459 ZmAccountOverviewContainer.prototype.removeZimletSection = 460 function() { 461 var headerLabel = ZmOrganizer.LABEL[ZmOrganizer.ZIMLET]; 462 var headerDataId = appCtxt.getOverviewId([this.containerId, headerLabel], null); 463 var header = this._headerItems[headerDataId]; 464 if (header) { 465 this.removeChild(header); 466 } 467 }; 468 469 ZmAccountOverviewContainer.prototype.highlightAllMboxes = 470 function() { 471 this.deselectAll(); 472 this.setSelection(this._allMailboxesTreeHeader, true); 473 }; 474 475 ZmAccountOverviewContainer.prototype._addAccount = 476 function(params, account, showBackgroundColor, headerClassName, predictableId) { 477 params.overviewId = appCtxt.getOverviewId(this.containerId, account); 478 params.account = account; // tree controller might need reference to account 479 480 // only show sections for apps that are supported unless this is prefs app 481 var app = appCtxt.getApp(this._appName); 482 var isSupported = (!app || (app && appCtxt.get(ZmApp.SETTING[this._appName], null, account))); 483 484 if (this._appName == ZmApp.PREFERENCES || isSupported) { 485 var omit = params.omitPerAcct 486 ? params.omitPerAcct[account.id] : params.omit; 487 488 var headerLabel, headerIcon; 489 if (this._appName == ZmApp.PREFERENCES && account.isMain && appCtxt.isOffline) { 490 headerLabel = ZmMsg.allAccounts; 491 headerIcon = "AccountAll"; 492 } else { 493 headerLabel = account.getDisplayName(); 494 if (!appCtxt.isFamilyMbox) { 495 headerIcon = account.getIcon() 496 } 497 } 498 499 var headerParams = { 500 label: headerLabel, 501 icon: headerIcon, 502 dataId: account.id, 503 className: headerClassName, 504 predictableId: predictableId 505 }; 506 507 this._addSection(headerParams, omit, params, showBackgroundColor); 508 } 509 510 var setting = appCtxt.getSettings(account).getSetting(ZmSetting.QUOTA_USED); 511 setting.addChangeListener(this._settingChangeListener); 512 }; 513 514 ZmAccountOverviewContainer.prototype._addSection = 515 function(headerParams, omit, overviewParams, showBackgroundColor) { 516 // create a top-level section header 517 var params = { 518 parent: this, 519 text: headerParams.label, 520 imageInfo: headerParams.icon, 521 selectable: overviewParams.selectable, 522 className: headerParams.className, 523 id: this.getHTMLElId() + "__" + (headerParams.predictableId || headerParams.dataId) + "__SECTION" 524 }; 525 var header = this._headerItems[headerParams.dataId] = new DwtTreeItem(params); 526 header.setData(Dwt.KEY_ID, headerParams.dataId); 527 header.setScrollStyle(Dwt.CLIP); 528 header._initialize(null, true, true); 529 header.addClassName(showBackgroundColor ? "ZmOverviewSectionHilite" : "ZmOverviewSection"); 530 531 // reset some params for child overviews 532 overviewParams.id = ZmId.getOverviewId(overviewParams.overviewId); 533 overviewParams.parent = header; 534 overviewParams.scroll = Dwt.CLIP; 535 overviewParams.posStyle = Dwt.STATIC_STYLE; 536 537 // next, create an overview for this account and add it to the account header 538 var ov = this._controller._overview[overviewParams.overviewId] = this._overview[overviewParams.overviewId] = new ZmOverview(overviewParams, this._controller); 539 header._dndScrollCallback = this._overview._dndScrollCallback, 540 header._dndScrollId = this._overview._scrollableContainerId, 541 header._addItem(ov, null, true); 542 543 // finally set treeviews for this overview 544 var treeIds = overviewParams.overviewTrees || overviewParams.treeIds; 545 ov.set(treeIds, omit); 546 }; 547 548 ZmAccountOverviewContainer.prototype._setupHeader = 549 function(header, acct) { 550 // always expand header in prefs app, otherwise follow implicit user pref 551 if (this._appName == ZmApp.PREFERENCES) { 552 header.setExpanded(true, false, true); 553 header.enableSelection(false); 554 } else { 555 var isExpanded = appCtxt.get(ZmSetting.ACCOUNT_TREE_OPEN, null, acct); 556 header.setExpanded(isExpanded); 557 if (!isExpanded) { 558 header.setText(this._getAccountHeaderLabel(acct)); 559 } 560 } 561 562 // add onclick support 563 if (header._extraCell) { 564 header._extraCell.onclick = AjxCallback.simpleClosure(this._handleStatusClick, this, acct); 565 } 566 567 // add DnD support 568 var dropTgt = this._controller.getTreeController(ZmOrganizer.FOLDER).getDropTarget(); 569 var root = ZmOrganizer.getSystemId(ZmOrganizer.ID_ROOT, acct); 570 header.setDropTarget(dropTgt); 571 header.setData(Dwt.KEY_OBJECT, appCtxt.getById(root)); 572 }; 573 574 /** 575 * Iterates all visible account and checks whether folder for each account is 576 * empty. Used by "All Mailboxes" to determine whether a virtual folder needs 577 * to be disabled or not when right-clicked. 578 * 579 * @param folderId [String] Normalized folder ID (should not be fully qualified) 580 */ 581 ZmAccountOverviewContainer.prototype._isFolderEmpty = 582 function(folderId) { 583 var accounts = appCtxt.accountList.visibleAccounts; 584 for (var i = 0; i < accounts.length; i++) { 585 var folder = appCtxt.getById(ZmOrganizer.getSystemId(folderId, accounts[i])); 586 if (folder && folder.numTotal > 0) { 587 return false; 588 } 589 } 590 591 return true; 592 }; 593 594 ZmAccountOverviewContainer.prototype._syncAccount = 595 function(dialog, account) { 596 dialog.popdown(); 597 account.sync(); 598 }; 599 600 ZmAccountOverviewContainer.prototype._treeViewListener = 601 function(ev) { 602 if (ev.detail != DwtTree.ITEM_ACTIONED && 603 ev.detail != DwtTree.ITEM_SELECTED && 604 ev.detail != DwtTree.ITEM_DBL_CLICKED) 605 { 606 return; 607 } 608 609 var item = this._actionedHeaderItem = ev.item; 610 611 // do nothing if zimlet/search is clicked 612 if (item && (item.__isZimlet || item.__isSearch)) { return; } 613 614 var data = item && item.getData(Dwt.KEY_ID); 615 616 if (ev.detail == DwtTree.ITEM_ACTIONED && appCtxt.getApp(this._appName)) { // right click 617 var actionMenu = this._getActionMenu(data); 618 if (actionMenu) { 619 this.resetOperations(actionMenu, data); 620 actionMenu.popup(0, ev.docX, ev.docY); 621 } 622 } 623 else if ((ev.detail == DwtTree.ITEM_SELECTED) && item) { // left click 624 // if calendar/prefs app, do nothing 625 if (this._appName == ZmApp.CALENDAR || 626 this._appName == ZmApp.PREFERENCES) 627 { 628 return; 629 } 630 631 this._deselectAllTreeViews(); 632 633 // this avoids processing clicks in dialogs etc. 634 if (!ZmApp.NAME[this._appName]) { return; } 635 636 // if an account header item was clicked, run the default search for it 637 if (data) { 638 var sc = appCtxt.getSearchController(); 639 640 var account = appCtxt.accountList.getAccount(data); 641 if (account) { 642 // bug 41196 - turn off new mail notifier if inactive account header clicked 643 if (appCtxt.isOffline && account.inNewMailMode) { 644 account.inNewMailMode = false; 645 var allContainers = appCtxt.getOverviewController()._overviewContainer; 646 for (var i in allContainers) { 647 allContainers[i].updateAccountInfo(account, true, true); 648 } 649 } 650 651 // don't process click if user clicked on error status icon 652 if ((ev.target.parentNode == ev.item._extraCell) && account.isError()) { 653 return; 654 } 655 656 sc.searchAllAccounts = false; 657 appCtxt.accountList.setActiveAccount(account); 658 659 if (appCtxt.isOffline && account.hasNotSynced() && !account.__syncAsked) { 660 account.__syncAsked = true; 661 662 var dialog = appCtxt.getYesNoMsgDialog(); 663 dialog.registerCallback(DwtDialog.YES_BUTTON, this._syncAccount, this, [dialog, account]); 664 dialog.setMessage(ZmMsg.neverSyncedAsk, DwtMessageDialog.INFO_STYLE); 665 dialog.popup(); 666 } 667 668 var fid = ZmOrganizer.DEFAULT_FOLDER[ZmApp.ORGANIZER[this._appName]]; 669 var folder = appCtxt.getById(ZmOrganizer.getSystemId(fid, account)); 670 671 // briefcase is not a ZmFolder so let's skip for now 672 if (!(folder instanceof ZmFolder)) { return; } 673 674 var defaultSortBy = (this._appName == ZmApp.TASKS) 675 ? ZmSearch.DUE_DATE_DESC : ZmSearch.DATE_DESC; 676 677 var params = { 678 query: folder.createQuery(), 679 getHtml: appCtxt.get(ZmSetting.VIEW_AS_HTML), 680 searchFor: (ZmApp.DEFAULT_SEARCH[this._appName]), 681 sortBy: ((sc.currentSearch && folder.nId == sc.currentSearch.folderId) ? null : defaultSortBy), 682 accountName: (account && account.name), 683 noUpdateOverview: true 684 }; 685 } else { 686 var main = appCtxt.accountList.mainAccount; 687 sc.resetSearchAllAccounts(); 688 sc.searchAllAccounts = true; 689 690 if (data instanceof ZmSearchFolder) { 691 params = { 692 searchAllAccounts: true, 693 accountName: main.name, 694 getHtml: appCtxt.get(ZmSetting.VIEW_AS_HTML), 695 noUpdateOverview: true 696 }; 697 sc.redoSearch(data.search, false, params); 698 return; 699 } 700 if (data == ZmOrganizer.ID_ALL_MAILBOXES) { 701 data = ZmFolder.ID_INBOX; 702 } 703 704 params = { 705 queryHint: appCtxt.accountList.generateQuery(data), 706 folderId: null, 707 getHtml: appCtxt.get(ZmSetting.VIEW_AS_HTML, null, main), 708 searchFor: (ZmApp.DEFAULT_SEARCH[this._appName]), 709 sortBy: ZmSearch.DATE_DESC, 710 accountName: main.name, 711 noUpdateOverview: true 712 }; 713 } 714 715 sc.search(params); 716 } 717 } else { // double click 718 // handle double click? 719 } 720 }; 721 722 ZmAccountOverviewContainer.prototype._treeListener = 723 function(ev) { 724 if (ev.detail != DwtTree.ITEM_COLLAPSED && 725 ev.detail != DwtTree.ITEM_EXPANDED) 726 { 727 return; 728 } 729 730 var header = ev.item; 731 var expanded = ev.detail == DwtTree.ITEM_EXPANDED; 732 733 var acct; 734 if (header) { 735 if (header.__isSearch) { 736 appCtxt.set(ZmSetting.OFFLINE_SAVED_SEARCHES_TREE_OPEN, expanded); 737 return; 738 } 739 740 var data = header.getData(Dwt.KEY_ID); 741 if (data == ZmOrganizer.ID_ALL_MAILBOXES) { 742 var text = expanded 743 ? header.__origText 744 : this._getFolderLabel(ZmOrganizer.ID_INBOX, header.__origText); 745 header.setText(text); 746 747 // bug #43734 - remember expand/collapse state 748 appCtxt.set(ZmSetting.OFFLINE_ALL_MAILBOXES_TREE_OPEN, expanded); 749 750 return; 751 } 752 753 acct = header.__isZimlet 754 ? appCtxt.accountList.mainAccount 755 : appCtxt.accountList.getAccount(data); 756 } 757 758 if (acct && appCtxt.getCurrentAppName() != ZmApp.PREFERENCES) { 759 if (!appCtxt.inStartup) { 760 // set account as active when account tree is opened/closed 761 appCtxt.accountList.setActiveAccount(acct); 762 appCtxt.set(ZmSetting.ACCOUNT_TREE_OPEN, expanded, null, null, null, acct); 763 } 764 765 if (!header.__isZimlet) { 766 var text = expanded 767 ? acct.getDisplayName() 768 : this._getAccountHeaderLabel(acct); 769 header.setText(text); 770 } 771 } 772 }; 773 774 ZmAccountOverviewContainer.prototype._mouseListener = 775 function(ev) { 776 return !Dwt.ffScrollbarCheck(ev); 777 }; 778 779 ZmAccountOverviewContainer.prototype._handleSettingChange = 780 function(ev) { 781 if (ev.type != ZmEvent.S_SETTING) { return; } 782 783 var setting = ev.source; 784 785 if (setting.id == ZmSetting.OFFLINE_SHOW_ALL_MAILBOXES) { 786 var isVisible = setting.getValue(); 787 this._allMailboxesTreeHeader.setVisible(isVisible); 788 if (!isVisible) { 789 if (appCtxt.getActiveAccount().isMain) { 790 appCtxt.accountList.setActiveAccount(appCtxt.accountList.defaultAccount); 791 } 792 appCtxt.getSearchController().searchAllAccounts = false; 793 appCtxt.getApp(ZmApp.MAIL).mailSearch(); 794 } else { 795 this._deselect(this._allMailboxesTreeHeader); 796 } 797 } 798 else if (setting.id == ZmSetting.QUOTA_USED) { 799 this.updateAccountInfo(ev.getDetails().account, false, true); 800 } 801 }; 802 803 ZmAccountOverviewContainer.prototype._getAccountHeaderLabel = 804 function(acct, header) { 805 var inboxId = (this._appName == ZmApp.MAIL) 806 ? ZmOrganizer.getSystemId(ZmOrganizer.ID_INBOX, acct, true) : null; 807 var inbox = inboxId && appCtxt.getById(inboxId); 808 if (inbox && inbox.numUnread > 0) { 809 var name = AjxMessageFormat.format(ZmMsg.folderUnread, [acct.getDisplayName(), inbox.numUnread]); 810 return (["<span style='font-weight:bold;'>", name, "</span>"].join("")); 811 } 812 813 return acct.getDisplayName(); 814 }; 815 816 ZmAccountOverviewContainer.prototype._getFolderLabel = 817 function(folderId, label) { 818 var checkUnread = (folderId != ZmFolder.ID_DRAFTS && folderId != ZmFolder.ID_OUTBOX); 819 var count = appCtxt.accountList.getItemCount(folderId, checkUnread); 820 if (count > 0) { 821 var name = AjxMessageFormat.format(ZmMsg.folderUnread, [label, count]); 822 return (["<span style='font-weight:bold;'>", name, "</span>"].join("")); 823 } 824 825 return label; 826 }; 827 828 ZmAccountOverviewContainer.prototype._initializeActionMenu = 829 function() { 830 if (!this._actionMenu) { 831 var orgType = ZmApp.ORGANIZER[this._appName]; 832 this._newOp = ZmOrganizer.NEW_OP[orgType]; 833 834 var ops = [this._newOp]; 835 if (appCtxt.isOffline) { 836 ops.push(ZmOperation.SYNC); 837 } 838 ops.push(ZmOperation.MARK_ALL_READ, 839 ZmOperation.EMPTY_FOLDER, 840 ZmOperation.DELETE); 841 842 this._actionMenu = new AjxCallback(this, this._createActionMenu, [ops]); 843 } 844 }; 845 846 ZmAccountOverviewContainer.prototype._createActionMenu = 847 function(menuItems) { 848 var listener = new AjxListener(this, this._actionMenuListener); 849 var actionMenu = new ZmActionMenu({parent:appCtxt.getShell(), menuItems:menuItems}); 850 menuItems = actionMenu.opList; 851 for (var i = 0; i < menuItems.length; i++) { 852 var mi = actionMenu.getItem(i); 853 var op = menuItems[i]; 854 if (op == ZmOperation.SYNC) { 855 mi.setText(ZmMsg.sendReceive); 856 } 857 actionMenu.addSelectionListener(op, listener); 858 } 859 860 actionMenu.addPopdownListener(new AjxListener(this, this._menuPopdownActionListener)); 861 862 return actionMenu; 863 }; 864 865 ZmAccountOverviewContainer.prototype._menuPopdownActionListener = 866 function() { 867 this._actionedHeaderItem._setActioned(false); 868 }; 869 870 ZmAccountOverviewContainer.prototype._actionMenuListener = 871 function(ev) { 872 var opId = ev.item.getData(ZmOperation.KEY_ID); 873 var data = this._actionedHeaderItem.getData(Dwt.KEY_ID); 874 875 if (opId == this._newOp) { 876 var treeId = ZmApp.ORGANIZER[this._appName]; 877 var tc = this._controller.getTreeController(treeId, true); 878 if (tc) { 879 tc._actionedOrganizer = null; 880 var account = appCtxt.accountList.getAccount(data); 881 tc._actionedOrganizer = appCtxt.getFolderTree(account).root; 882 tc._newListener(ev, account); 883 } 884 } 885 else if (opId == ZmOperation.SYNC) { 886 if (data == ZmOrganizer.ID_ALL_MAILBOXES) { 887 appCtxt.accountList.syncAll(); 888 } else { 889 var account = appCtxt.accountList.getAccount(data); 890 if (account) { 891 account.sync(); 892 } 893 } 894 } 895 else if (opId == ZmOperation.MARK_ALL_READ) { 896 this._doAction(data, opId); 897 } 898 else if (opId == ZmOperation.EMPTY_FOLDER) { 899 this._confirmEmptyAction(data, opId); 900 } 901 else if (opId == ZmOperation.DELETE) { 902 data._delete(); 903 var parent = this._actionedHeaderItem.parent; 904 parent.removeChild(this._actionedHeaderItem); // HACK: just nuke it 905 parent.setVisible(parent.getItemCount() > 0); 906 } 907 }; 908 909 ZmAccountOverviewContainer.prototype._confirmEmptyAction = 910 function(data, opId) { 911 var dialog = appCtxt.getOkCancelMsgDialog(); 912 dialog.reset(); 913 dialog.registerCallback(DwtDialog.OK_BUTTON, this._emptyCallback, this, [dialog, data, opId]); 914 915 var msg = (data == ZmFolder.ID_TRASH) 916 ? ZmMsg.confirmEmptyTrashFolder 917 : (AjxMessageFormat.format(ZmMsg.confirmEmptyFolder, ZmMsg[ZmFolder.MSG_KEY[data]])); 918 919 dialog.setMessage(msg, DwtMessageDialog.WARNING_STYLE); 920 dialog.popup(); 921 }; 922 923 ZmAccountOverviewContainer.prototype._emptyCallback = 924 function(dialog, folderId, opId) { 925 dialog.popdown(); 926 this._doAction(folderId, opId); 927 }; 928 929 ZmAccountOverviewContainer.prototype._doAction = 930 function(folderId, opId) { 931 var bc = new ZmBatchCommand(true, appCtxt.accountList.mainAccount.name); 932 933 var accounts = appCtxt.accountList.visibleAccounts; 934 for (var i = 0; i < accounts.length; i++) { 935 var account = accounts[i]; 936 if (account.isMain) { continue; } 937 938 var fid = ZmOrganizer.getSystemId(folderId, account); 939 var folder = appCtxt.getById(fid); 940 if (folder) { 941 if (opId == ZmOperation.MARK_ALL_READ) { 942 folder.markAllRead(bc); 943 } else { 944 folder.empty(null, bc); 945 } 946 bc.curId++; 947 } 948 } 949 950 bc.run(); 951 if (appCtxt.isOffline) { 952 appCtxt.getApp(ZmApp.MAIL).clearNewMailBadge(); 953 } 954 }; 955 956 ZmAccountOverviewContainer.prototype._handleStatusClick = 957 function(account, ev) { 958 959 if(!account.isError()) { 960 return; 961 } else if (appCtxt.isOffline && (account.status == ZmZimbraAccount.STATUS_AUTHFAIL)) { 962 var dialog = appCtxt.getPasswordChangeDialog(); 963 dialog.popup(account); 964 } else { 965 account.showErrorMessage(); 966 } 967 }; 968