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 * Creates a new, empty mail list controller. 26 * @constructor 27 * @class 28 * This class encapsulates controller behavior that is common to lists of mail items. 29 * Operations such as replying and marking read/unread are supported. 30 * 31 * @author Conrad Damon 32 * 33 * @param {DwtControl} container the containing shell 34 * @param {ZmApp} mailApp the containing application 35 * @param {constant} type type of controller 36 * @param {string} sessionId the session id 37 * @param {ZmSearchResultsController} searchResultsController containing controller 38 * 39 * @extends ZmListController 40 */ 41 ZmMailListController = function(container, mailApp, type, sessionId, searchResultsController) { 42 43 if (arguments.length == 0) { return; } 44 ZmListController.apply(this, arguments); 45 46 this._setStatics(); 47 48 this._listeners[ZmOperation.SHOW_ORIG] = this._showOrigListener.bind(this); 49 50 this._listeners[ZmOperation.MARK_READ] = this._markReadListener.bind(this); 51 this._listeners[ZmOperation.MARK_UNREAD] = this._markUnreadListener.bind(this); 52 this._listeners[ZmOperation.FLAG] = this._flagListener.bind(this, true); 53 this._listeners[ZmOperation.UNFLAG] = this._flagListener.bind(this, false); 54 //fixed bug:15460 removed reply and forward menu. 55 if (appCtxt.get(ZmSetting.REPLY_MENU_ENABLED)) { 56 this._listeners[ZmOperation.REPLY] = this._replyListener.bind(this); 57 this._listeners[ZmOperation.REPLY_ALL] = this._replyListener.bind(this); 58 } 59 60 if (appCtxt.get(ZmSetting.FORWARD_MENU_ENABLED)) { 61 this._listeners[ZmOperation.FORWARD] = this._forwardListener.bind(this); 62 this._listeners[ZmOperation.FORWARD_CONV] = this._forwardConvListener.bind(this); 63 } 64 this._listeners[ZmOperation.REDIRECT] = new AjxListener(this, this._redirectListener); 65 this._listeners[ZmOperation.EDIT] = this._editListener.bind(this, false); 66 this._listeners[ZmOperation.EDIT_AS_NEW] = this._editListener.bind(this, true); 67 this._listeners[ZmOperation.MUTE_CONV] = this._muteConvListener.bind(this); 68 this._listeners[ZmOperation.UNMUTE_CONV] = this._unmuteConvListener.bind(this); 69 70 if (appCtxt.get(ZmSetting.SPAM_ENABLED)) { 71 this._listeners[ZmOperation.SPAM] = this._spamListener.bind(this); 72 } 73 74 this._listeners[ZmOperation.DETACH] = this._detachListener.bind(this); 75 this._inviteReplyListener = this._inviteReplyHandler.bind(this); 76 this._shareListener = this._shareHandler.bind(this); 77 this._subscribeListener = this._subscribeHandler.bind(this); 78 79 this._acceptShareListener = this._acceptShareHandler.bind(this); 80 this._declineShareListener = this._declineShareHandler.bind(this); 81 82 this._listeners[ZmOperation.ADD_FILTER_RULE] = this._filterListener.bind(this, false, null); 83 this._listeners[ZmOperation.CREATE_APPT] = this._createApptListener.bind(this); 84 this._listeners[ZmOperation.CREATE_TASK] = this._createTaskListener.bind(this); 85 86 }; 87 88 ZmMailListController.prototype = new ZmListController; 89 ZmMailListController.prototype.constructor = ZmMailListController; 90 91 ZmMailListController.prototype.isZmMailListController = true; 92 ZmMailListController.prototype.toString = function() { return "ZmMailListController"; }; 93 94 ZmMailListController.GROUP_BY_ITEM = {}; // item type to search for 95 ZmMailListController.GROUP_BY_SETTING = {}; // associated setting on server 96 97 // Stuff for the View menu 98 ZmMailListController.GROUP_BY_ICON = {}; 99 ZmMailListController.GROUP_BY_MSG_KEY = {}; 100 ZmMailListController.GROUP_BY_SHORTCUT = {}; 101 ZmMailListController.GROUP_BY_VIEWS = []; 102 103 // reading pane options 104 ZmMailListController.READING_PANE_TEXT = {}; 105 ZmMailListController.READING_PANE_TEXT[ZmSetting.RP_OFF] = ZmMsg.readingPaneOff; 106 ZmMailListController.READING_PANE_TEXT[ZmSetting.RP_BOTTOM] = ZmMsg.readingPaneAtBottom; 107 ZmMailListController.READING_PANE_TEXT[ZmSetting.RP_RIGHT] = ZmMsg.readingPaneOnRight; 108 109 ZmMailListController.READING_PANE_ICON = {}; 110 ZmMailListController.READING_PANE_ICON[ZmSetting.RP_OFF] = "SplitPaneOff"; 111 ZmMailListController.READING_PANE_ICON[ZmSetting.RP_BOTTOM] = "SplitPane"; 112 ZmMailListController.READING_PANE_ICON[ZmSetting.RP_RIGHT] = "SplitPaneVertical"; 113 114 // conv order options 115 ZmMailListController.CONV_ORDER_DESC = ZmSearch.DATE_DESC; 116 ZmMailListController.CONV_ORDER_ASC = ZmSearch.DATE_ASC; 117 118 ZmMailListController.CONV_ORDER_TEXT = {}; 119 ZmMailListController.CONV_ORDER_TEXT[ZmMailListController.CONV_ORDER_DESC] = ZmMsg.convOrderDescending; 120 ZmMailListController.CONV_ORDER_TEXT[ZmMailListController.CONV_ORDER_ASC] = ZmMsg.convOrderAscending; 121 122 // convert key mapping to folder to search 123 ZmMailListController.ACTION_CODE_TO_FOLDER = {}; 124 ZmMailListController.ACTION_CODE_TO_FOLDER[ZmKeyMap.GOTO_INBOX] = ZmFolder.ID_INBOX; 125 ZmMailListController.ACTION_CODE_TO_FOLDER[ZmKeyMap.GOTO_DRAFTS] = ZmFolder.ID_DRAFTS; 126 ZmMailListController.ACTION_CODE_TO_FOLDER[ZmKeyMap.GOTO_JUNK] = ZmFolder.ID_SPAM; 127 ZmMailListController.ACTION_CODE_TO_FOLDER[ZmKeyMap.GOTO_SENT] = ZmFolder.ID_SENT; 128 ZmMailListController.ACTION_CODE_TO_FOLDER[ZmKeyMap.GOTO_TRASH] = ZmFolder.ID_TRASH; 129 130 // convert key mapping to folder to move to 131 ZmMailListController.ACTION_CODE_TO_FOLDER_MOVE = {}; 132 ZmMailListController.ACTION_CODE_TO_FOLDER_MOVE[ZmKeyMap.MOVE_TO_INBOX] = ZmFolder.ID_INBOX; 133 ZmMailListController.ACTION_CODE_TO_FOLDER_MOVE[ZmKeyMap.MOVE_TO_TRASH] = ZmFolder.ID_TRASH; 134 ZmMailListController.ACTION_CODE_TO_FOLDER_MOVE[ZmKeyMap.MOVE_TO_JUNK] = ZmFolder.ID_SPAM; 135 136 // convert key mapping to view menu item 137 ZmMailListController.ACTION_CODE_TO_MENU_ID = {}; 138 ZmMailListController.ACTION_CODE_TO_MENU_ID[ZmKeyMap.READING_PANE_OFF] = ZmSetting.RP_OFF; 139 ZmMailListController.ACTION_CODE_TO_MENU_ID[ZmKeyMap.READING_PANE_BOTTOM] = ZmSetting.RP_BOTTOM; 140 ZmMailListController.ACTION_CODE_TO_MENU_ID[ZmKeyMap.READING_PANE_RIGHT] = ZmSetting.RP_RIGHT; 141 142 ZmMailListController.ACTION_CODE_WHICH = {}; 143 ZmMailListController.ACTION_CODE_WHICH[ZmKeyMap.FIRST_UNREAD] = DwtKeyMap.SELECT_FIRST; 144 ZmMailListController.ACTION_CODE_WHICH[ZmKeyMap.LAST_UNREAD] = DwtKeyMap.SELECT_LAST; 145 ZmMailListController.ACTION_CODE_WHICH[ZmKeyMap.NEXT_UNREAD] = DwtKeyMap.SELECT_NEXT; 146 ZmMailListController.ACTION_CODE_WHICH[ZmKeyMap.PREV_UNREAD] = DwtKeyMap.SELECT_PREV; 147 148 ZmMailListController.viewToTab = {}; 149 150 151 // Public methods 152 153 /** 154 * Handles switching views based on action from view menu. 155 * 156 * @param {constant} view the id of the new view 157 * @param {Boolean} force if <code>true</code>, always redraw view 158 */ 159 ZmMailListController.prototype.switchView = function(view, force) { 160 161 if ((view == ZmId.VIEW_TRAD || view == ZmId.VIEW_CONVLIST) && view != this.getCurrentViewType()) { 162 if (appCtxt.multiAccounts) { 163 delete this._showingAccountColumn; 164 } 165 166 var groupBy = ZmMailListController.GROUP_BY_SETTING[view]; 167 if (this.isSearchResults) { 168 appCtxt.getApp(ZmApp.SEARCH).setGroupMailBy(groupBy); 169 } 170 else { 171 this._app.setGroupMailBy(groupBy); 172 } 173 174 var folderId = this._currentSearch && this._currentSearch.folderId; 175 176 var sortBy = appCtxt.get(ZmSetting.SORTING_PREF, folderId || view); 177 if (view == ZmId.VIEW_CONVLIST && (sortBy == ZmSearch.NAME_DESC || sortBy == ZmSearch.NAME_ASC)) { 178 sortBy = appCtxt.get(ZmSetting.SORTING_PREF, view); //go back to sortBy for view 179 appCtxt.set(ZmSetting.SORTING_PREF, sortBy, folderId); //force folderId sorting 180 } 181 if (this._mailListView && !appCtxt.isExternalAccount()) { 182 //clear the groups to address "from" grouping for conversation 183 if (folderId) { 184 var currentGroup = this._mailListView.getGroup(folderId); 185 if (currentGroup && currentGroup.id == ZmId.GROUPBY_FROM) { 186 this._mailListView.setGroup(ZmId.GROUPBY_NONE); 187 } 188 } 189 } 190 191 this._currentSearch.clearCursor(); 192 var limit = this._listView[this._currentViewId].getLimit(); 193 var getHtml = appCtxt.get(ZmSetting.VIEW_AS_HTML); 194 var groupByItem = (view == ZmId.VIEW_TRAD) ? ZmItem.MSG : ZmItem.CONV; 195 var params = { 196 types: [groupByItem], 197 offset: 0, 198 limit: limit, 199 sortBy: sortBy, 200 getHtml: getHtml, 201 isViewSwitch: true 202 }; 203 appCtxt.getSearchController().redoSearch(this._currentSearch, null, params); 204 } 205 }; 206 207 // override if reading pane is supported 208 ZmMailListController.prototype._setupReadingPaneMenu = function() {}; 209 ZmMailListController.prototype._setupConvOrderMenu = function() {}; 210 211 /** 212 * Checks if the reading pane is "on". 213 * 214 * @return {Boolean} <code>true</code> if the reading pane is "on" 215 */ 216 ZmMailListController.prototype.isReadingPaneOn = 217 function() { 218 return (this._getReadingPanePref() != ZmSetting.RP_OFF); 219 }; 220 221 /** 222 * Checks if the reading pane is "on" right. 223 * 224 * @return {Boolean} <code>true</code> if the reading pane is "on" right. 225 */ 226 ZmMailListController.prototype.isReadingPaneOnRight = 227 function() { 228 return (this._getReadingPanePref() == ZmSetting.RP_RIGHT); 229 }; 230 231 ZmMailListController.prototype._getReadingPanePref = 232 function() { 233 return (this._readingPaneLoc || appCtxt.get(ZmSetting.READING_PANE_LOCATION)); 234 }; 235 236 ZmMailListController.prototype._setReadingPanePref = 237 function(value) { 238 if (this.isSearchResults || appCtxt.isExternalAccount()) { 239 this._readingPaneLoc = value; 240 } 241 else { 242 appCtxt.set(ZmSetting.READING_PANE_LOCATION, value); 243 } 244 }; 245 246 ZmMailListController.prototype.getKeyMapName = 247 function() { 248 return ZmKeyMap.MAP_MAIL; 249 }; 250 251 // We need to stay in sync with what's allowed by _resetOperations 252 // TODO: we should just find out if an operation was enabled via _resetOperations 253 ZmMailListController.prototype.handleKeyAction = 254 function(actionCode, ev) { 255 DBG.println(AjxDebug.DBG3, "ZmMailListController.handleKeyAction"); 256 257 var lv = this._listView[this._currentViewId]; 258 var num = lv.getSelectionCount(); 259 260 var item; 261 if (num == 1 && !this.isDraftsFolder()) { 262 var sel = this._listView[this._currentViewId].getSelection(); 263 if (sel && sel.length) { 264 item = sel[0]; 265 } 266 } 267 268 var folder = this._getSearchFolder(); 269 var isSyncFailures = this.isSyncFailuresFolder(); 270 var isDrafts = (item && item.isDraft && (item.type != ZmId.ITEM_CONV || item.numMsgs == 1)) || this.isDraftsFolder(); 271 var isFeed = (folder && folder.isFeed()); 272 var isExternalAccount = appCtxt.isExternalAccount(); 273 274 switch (actionCode) { 275 276 case ZmKeyMap.FORWARD: 277 if (!isDrafts && !isExternalAccount) { 278 this._doAction({action:ZmOperation.FORWARD, foldersToOmit:ZmMailApp.getFoldersToOmit()}); 279 } 280 break; 281 282 case ZmKeyMap.GET_MAIL: 283 this._checkMailListener(); 284 break; 285 286 case ZmKeyMap.GOTO_INBOX: 287 case ZmKeyMap.GOTO_DRAFTS: 288 case ZmKeyMap.GOTO_JUNK: 289 case ZmKeyMap.GOTO_SENT: 290 case ZmKeyMap.GOTO_TRASH: 291 if (isExternalAccount) { break; } 292 if (actionCode == ZmKeyMap.GOTO_JUNK && !appCtxt.get(ZmSetting.SPAM_ENABLED)) { break; } 293 this._folderSearch(ZmMailListController.ACTION_CODE_TO_FOLDER[actionCode]); 294 break; 295 296 case ZmKeyMap.MOVE_TO_INBOX: 297 case ZmKeyMap.MOVE_TO_TRASH: 298 case ZmKeyMap.MOVE_TO_JUNK: 299 if (isSyncFailures || isExternalAccount) { break; } 300 if (actionCode == ZmKeyMap.MOVE_TO_JUNK && !appCtxt.get(ZmSetting.SPAM_ENABLED)) { break; } 301 if (num && !(isDrafts && actionCode != ZmKeyMap.MOVE_TO_TRASH)) { 302 var folderId = ZmMailListController.ACTION_CODE_TO_FOLDER_MOVE[actionCode]; 303 folder = appCtxt.getById(folderId); 304 var items = lv.getSelection(); 305 this._doMove(items, folder); 306 } 307 break; 308 309 case ZmKeyMap.REPLY: 310 case ZmKeyMap.REPLY_ALL: 311 if (!isDrafts && !isExternalAccount && (num == 1) && !isSyncFailures && !isFeed) { 312 this._doAction({action:ZmMailListController.ACTION_CODE_TO_OP[actionCode], foldersToOmit:ZmMailApp.getFoldersToOmit()}); 313 } 314 break; 315 316 case ZmKeyMap.SELECT_ALL: 317 lv.selectAll(true); 318 this._resetToolbarOperations(); 319 break; 320 321 case ZmKeyMap.SPAM: 322 if (isExternalAccount) { break; } 323 if (num && !isDrafts && !isExternalAccount && !isSyncFailures && appCtxt.get(ZmSetting.SPAM_ENABLED) && (folder && !folder.isReadOnly())) { 324 this._spamListener(); 325 } 326 break; 327 328 case ZmKeyMap.MUTE_UNMUTE_CONV: 329 // Mute/Unmute Code removed for IM will be added for JP 330 break; 331 332 case ZmKeyMap.MARK_READ: 333 if (this._isPermissionDenied(folder)) { 334 break; 335 } 336 this._markReadListener(); 337 break; 338 339 case ZmKeyMap.MARK_UNREAD: 340 if (this._isPermissionDenied(folder)) { 341 break; 342 } 343 this._markUnreadListener(); 344 break; 345 346 case ZmKeyMap.FLAG: 347 if (this._isPermissionDenied(folder)) { 348 break; 349 } 350 this._doFlag(this.getItems()); 351 break; 352 353 case ZmKeyMap.VIEW_BY_CONV: 354 if (!isSyncFailures && appCtxt.get(ZmSetting.CONVERSATIONS_ENABLED)) { 355 this.switchView(ZmId.VIEW_CONVLIST); 356 } 357 break; 358 359 case ZmKeyMap.VIEW_BY_MSG: 360 if (!isSyncFailures) { 361 this.switchView(ZmId.VIEW_TRAD); 362 } 363 break; 364 365 case ZmKeyMap.READING_PANE_BOTTOM: 366 case ZmKeyMap.READING_PANE_RIGHT: 367 case ZmKeyMap.READING_PANE_OFF: 368 var menuId = ZmMailListController.ACTION_CODE_TO_MENU_ID[actionCode]; 369 this.switchView(menuId, true); 370 this._updateViewMenu(menuId, this._readingPaneViewMenu); 371 break; 372 373 case ZmKeyMap.SHOW_FRAGMENT: 374 if (num == 1) { 375 var item = lv.getSelection()[0]; 376 if (!item) { break; } 377 var id = lv._getFieldId(item, ZmItem.F_SUBJECT); 378 var subjectField = document.getElementById(id); 379 if (subjectField) { 380 var loc = Dwt.getLocation(subjectField); 381 var frag; 382 // TODO: refactor / clean up 383 if ((item.type == ZmItem.MSG && item.isInvite() && item.needsRsvp()) || 384 (item.type == ZmId.ITEM_CONV && this.getMsg() && this.getMsg().isInvite() && this.getMsg().needsRsvp())) 385 { 386 frag = item.invite ? item.invite.getToolTip() : this.getMsg().invite.getToolTip(); 387 } else { 388 frag = item.fragment ? item.fragment : ZmMsg.fragmentIsEmpty; 389 if (frag != "") { lv.setToolTipContent(AjxStringUtil.htmlEncode(frag), true); } 390 } 391 var tooltip = this._shell.getToolTip(); 392 tooltip.popdown(); 393 if (frag != "") { 394 tooltip.setContent(frag); 395 tooltip.popup(loc.x, loc.y); 396 } 397 } 398 } 399 break; 400 401 case ZmKeyMap.NEXT_UNREAD: 402 case ZmKeyMap.PREV_UNREAD: 403 this.lastListAction = actionCode; 404 405 case ZmKeyMap.FIRST_UNREAD: 406 case ZmKeyMap.LAST_UNREAD: 407 var unreadItem = this._getUnreadItem(ZmMailListController.ACTION_CODE_WHICH[actionCode]); 408 if (unreadItem) { 409 this._selectItem(lv, unreadItem); 410 } 411 break; 412 413 default: 414 return ZmListController.prototype.handleKeyAction.apply(this, arguments); 415 } 416 return true; 417 }; 418 419 ZmMailListController.prototype._isPermissionDenied = 420 function(folder) { 421 var isExternalAccount = appCtxt.isExternalAccount(); 422 423 if (isExternalAccount || (folder && folder.isReadOnly())) { 424 appCtxt.setStatusMsg(ZmMsg.errorPermission); 425 return true; 426 } 427 return false; 428 }; 429 430 ZmMailListController.prototype._selectItem = 431 function(listView, item) { 432 listView._unmarkKbAnchorElement(true); 433 listView.setSelection(item); 434 var el = listView._getElFromItem(item); 435 if (el) { 436 listView._scrollList(el); 437 } 438 }; 439 440 ZmMailListController.prototype.mapSupported = 441 function(map) { 442 return (map == "list"); 443 }; 444 445 /** 446 * Sends the read receipt. 447 * 448 * @param {ZmMailMsg} msg the message 449 */ 450 ZmMailListController.prototype.sendReadReceipt = 451 function(msg) { 452 453 if (!appCtxt.get(ZmSetting.MAIL_READ_RECEIPT_ENABLED) || msg.readReceiptSent || msg.isSent) { 454 return; 455 } 456 457 var rrPref = appCtxt.get(ZmSetting.MAIL_SEND_READ_RECEIPTS); 458 459 if (rrPref == ZmMailApp.SEND_RECEIPT_PROMPT) { 460 var dlg = appCtxt.getYesNoMsgDialog(); 461 dlg.registerCallback(DwtDialog.YES_BUTTON, this._sendReadReceipt, this, [msg, dlg]); 462 dlg.registerCallback(DwtDialog.NO_BUTTON, this._sendReadReceiptNotified, this, [msg, dlg]); 463 dlg.setMessage(ZmMsg.readReceiptSend, DwtMessageDialog.WARNING_STYLE); 464 dlg.popup(); 465 } else if (rrPref == ZmMailApp.SEND_RECEIPT_ALWAYS) { 466 msg.readReceiptSent = true; 467 this._sendReadReceipt(msg); 468 } 469 }; 470 471 ZmMailListController.prototype._sendReadReceipt = 472 function(msg, dlg) { 473 if (dlg) { 474 dlg.popdown(); 475 } 476 msg.sendReadReceipt(this._handleSendReadReceipt.bind(this, msg)); 477 }; 478 479 ZmMailListController.prototype._handleSendReadReceipt = 480 function(msg) { 481 appCtxt.setStatusMsg(ZmMsg.readReceiptSent); 482 this._sendReadReceiptNotified(msg); 483 }; 484 485 ZmMailListController.prototype._sendReadReceiptNotified = 486 function(msg, dlg) { 487 var callback = dlg ? (new AjxCallback(dlg, dlg.popdown)) : null; 488 var flags = msg.setFlag(ZmItem.FLAG_READ_RECEIPT_SENT, true); 489 msg.list.flagItems({items:[msg], op:"update", value:flags, callback:callback}); 490 }; 491 492 ZmMailListController.prototype._updateViewMenu = function(id, menu) { 493 494 var viewBtn = this.getCurrentToolbar().getButton(ZmOperation.VIEW_MENU); 495 menu = menu || (viewBtn && viewBtn.getMenu()); 496 if (menu) { 497 var mi = menu.getItemById(ZmOperation.MENUITEM_ID, id); 498 if (mi) { 499 mi.setChecked(true, true); 500 } 501 } 502 503 // Create "Display" submenu here since it's only needed for multi-column 504 if (!this._colHeaderViewMenu && this._mailListView.isMultiColumn()) { 505 this._colHeaderViewMenu = this._setupColHeaderViewMenu(this._currentView, this._viewMenu); 506 } 507 508 if (this._colHeaderMenuItem && (id === ZmSetting.RP_OFF || id === ZmSetting.RP_BOTTOM || id === ZmSetting.RP_RIGHT)) { 509 this._colHeaderMenuItem.setVisible(this._mailListView.isMultiColumn()); 510 } 511 }; 512 513 // Private and protected methods 514 515 ZmMailListController.prototype._initialize = 516 function(view) { 517 this._setActiveSearch(view); 518 519 // call base class 520 ZmListController.prototype._initialize.call(this, view); 521 }; 522 523 ZmMailListController.prototype._initializeParticipantActionMenu = 524 function() { 525 if (!this._participantActionMenu) { 526 var menuItems = this._participantOps(); 527 menuItems.push(ZmOperation.SEP); 528 var ops = this._getActionMenuOps(); 529 if (ops && ops.length) { 530 menuItems = menuItems.concat(ops); 531 } 532 533 this._participantActionMenu = new ZmActionMenu({parent:this._shell, menuItems:menuItems, controller:this, 534 context:this._currentViewId, menuType:ZmId.MENU_PARTICIPANT}); 535 if (appCtxt.get(ZmSetting.SEARCH_ENABLED)) { 536 this._setSearchMenu(this._participantActionMenu); 537 } 538 this._addMenuListeners(this._participantActionMenu); 539 this._participantActionMenu.addPopdownListener(this._menuPopdownListener); 540 this._setupTagMenu(this._participantActionMenu); 541 542 //notify Zimlet before showing 543 appCtxt.notifyZimlets("onParticipantActionMenuInitialized", [this, this._participantActionMenu]); 544 } 545 return this._participantActionMenu; 546 }; 547 548 ZmMailListController.prototype._initializeDraftsActionMenu = 549 function() { 550 if (!this._draftsActionMenu) { 551 var menuItems = [ 552 ZmOperation.EDIT, 553 ZmOperation.SEP, 554 ZmOperation.TAG_MENU, ZmOperation.DELETE, ZmOperation.PRINT, 555 ZmOperation.SEP, 556 ZmOperation.SHOW_ORIG 557 ]; 558 this._draftsActionMenu = new ZmActionMenu({parent:this._shell, menuItems:menuItems, 559 context:this._currentViewId, menuType:ZmId.MENU_DRAFTS}); 560 if (appCtxt.get(ZmSetting.SEARCH_ENABLED)) { 561 this._setSearchMenu(this._draftsActionMenu); 562 } 563 this._addMenuListeners(this._draftsActionMenu); 564 this._draftsActionMenu.addPopdownListener(this._menuPopdownListener); 565 this._setupTagMenu(this._draftsActionMenu); 566 appCtxt.notifyZimlets("onDraftsActionMenuInitialized", [this, this._draftsActionMenu]); 567 } 568 }; 569 570 ZmMailListController.prototype._setDraftSearchMenu = 571 function(address, item, ev){ 572 if (address && appCtxt.get(ZmSetting.SEARCH_ENABLED) && (ev.field == ZmItem.F_PARTICIPANT || ev.field == ZmItem.F_FROM)){ 573 if (!this._draftsActionMenu.getOp(ZmOperation.SEARCH_MENU)) { 574 ZmOperation.addOperation(this._draftsActionMenu, ZmOperation.SEARCH_MENU, [ZmOperation.SEARCH_MENU, ZmOperation.SEP], 0); 575 this._setSearchMenu(this._draftsActionMenu); 576 } 577 if (item && (item.getAddresses(AjxEmailAddress.TO).getArray().length + item.getAddresses(AjxEmailAddress.CC).getArray().length) > 1){ 578 ZmOperation.setOperation(this._draftsActionMenu.getSearchMenu(), ZmOperation.SEARCH_TO, ZmOperation.SEARCH_TO, ZmMsg.findEmailToRecipients); 579 ZmOperation.setOperation(this._draftsActionMenu.getSearchMenu(), ZmOperation.SEARCH, ZmOperation.SEARCH, ZmMsg.findEmailFromRecipients); 580 } 581 else{ 582 ZmOperation.setOperation(this._draftsActionMenu.getSearchMenu(), ZmOperation.SEARCH_TO, ZmOperation.SEARCH_TO, ZmMsg.findEmailToRecipient); 583 ZmOperation.setOperation(this._draftsActionMenu.getSearchMenu(), ZmOperation.SEARCH, ZmOperation.SEARCH, ZmMsg.findEmailFromRecipient); 584 } 585 } 586 else if (this._draftsActionMenu.getOp(ZmOperation.SEARCH_MENU)) { 587 this._draftsActionMenu = null; 588 this._initializeDraftsActionMenu(); 589 } 590 }; 591 592 ZmMailListController.prototype._getToolBarOps = 593 function() { 594 var list = []; 595 list.push(ZmOperation.SEP); 596 list = list.concat(this._msgOps()); 597 list.push(ZmOperation.SEP); 598 list = list.concat(this._deleteOps()); 599 list.push(ZmOperation.SEP); 600 list.push(ZmOperation.MOVE_MENU); 601 list.push(ZmOperation.TAG_MENU); 602 return list; 603 }; 604 605 ZmMailListController.prototype._getRightSideToolBarOps = 606 function() { 607 if (appCtxt.isChildWindow) { 608 return []; 609 } 610 return [ZmOperation.VIEW_MENU]; 611 }; 612 613 614 ZmMailListController.prototype._showDetachInSecondary = 615 function() { 616 return true; 617 }; 618 619 ZmMailListController.prototype._getSecondaryToolBarOps = 620 function() { 621 622 var list = [], 623 viewType = this.getCurrentViewType(); 624 625 list.push(ZmOperation.PRINT); 626 list.push(ZmOperation.SEP); 627 list = list.concat(this._flagOps()); 628 list.push(ZmOperation.SEP); 629 list.push(ZmOperation.REDIRECT); 630 list.push(ZmOperation.EDIT_AS_NEW); 631 list.push(ZmOperation.SEP); 632 list = list.concat(this._createOps()); 633 list.push(ZmOperation.SEP); 634 list = list.concat(this._otherOps(true)); 635 if (viewType === ZmId.VIEW_TRAD) { 636 list.push(ZmOperation.SHOW_CONV); 637 } 638 639 return list; 640 }; 641 642 643 ZmMailListController.prototype._initializeToolBar = 644 function(view, className) { 645 646 if (!this._toolbar[view]) { 647 ZmListController.prototype._initializeToolBar.call(this, view, className); 648 this._createViewMenu(view); 649 this._setReplyText(this._toolbar[view]); 650 // this._toolbar[view].addOp(ZmOperation.FILLER); 651 if (!appCtxt.isChildWindow) { 652 this._initializeNavToolBar(view); 653 } 654 } 655 656 if (!appCtxt.isChildWindow) { 657 this._setupViewMenu(view); 658 } 659 this._setupDeleteButton(this._toolbar[view]); 660 if (appCtxt.get(ZmSetting.SPAM_ENABLED)) { 661 this._setupSpamButton(this._toolbar[view]); 662 } 663 this._setupPrintButton(this._toolbar[view]); 664 }; 665 666 ZmMailListController.prototype._getNumTotal = 667 function(){ 668 // Yuck, remove "of Total" from Nav toolbar at lower resolutions 669 if (AjxEnv.is1024x768orLower) { 670 return null; 671 } 672 return ZmListController.prototype._getNumTotal.call(this); 673 }; 674 675 ZmMailListController.prototype._initializeActionMenu = 676 function() { 677 var isInitialized = (this._actionMenu != null); 678 ZmListController.prototype._initializeActionMenu.call(this); 679 680 if (this._actionMenu) { 681 this._setupSpamButton(this._actionMenu); 682 } 683 //notify Zimlet before showing 684 appCtxt.notifyZimlets("onActionMenuInitialized", [this, this._actionMenu]); 685 }; 686 687 // Groups of mail-related operations 688 689 ZmMailListController.prototype._flagOps = 690 function() { 691 return [ZmOperation.MARK_READ, ZmOperation.MARK_UNREAD, ZmOperation.FLAG, ZmOperation.UNFLAG]; 692 }; 693 694 ZmMailListController.prototype._msgOps = 695 function() { 696 var list = []; 697 698 list.push(ZmOperation.EDIT); // hidden except for Drafts 699 700 if (appCtxt.get(ZmSetting.REPLY_MENU_ENABLED)) { 701 list.push(ZmOperation.REPLY, ZmOperation.REPLY_ALL); 702 } 703 704 if (appCtxt.get(ZmSetting.FORWARD_MENU_ENABLED)) { 705 list.push(ZmOperation.FORWARD); 706 } 707 return list; 708 }; 709 710 ZmMailListController.prototype._deleteOps = 711 function() { 712 return [this.getDeleteOperation(), ZmOperation.SPAM]; 713 }; 714 715 ZmMailListController.prototype._createOps = 716 function() { 717 var list = []; 718 if (appCtxt.get(ZmSetting.FILTERS_ENABLED)) { 719 list.push(ZmOperation.ADD_FILTER_RULE); 720 } 721 if (appCtxt.get(ZmSetting.CALENDAR_ENABLED)) { 722 list.push(ZmOperation.CREATE_APPT); 723 } 724 if (appCtxt.get(ZmSetting.TASKS_ENABLED)) { 725 list.push(ZmOperation.CREATE_TASK); 726 } 727 //list.push(ZmOperation.QUICK_COMMANDS); 728 return list; 729 }; 730 731 ZmMailListController.prototype._otherOps = 732 function(isSecondary) { 733 var list = []; 734 if (!appCtxt.isChildWindow && (!isSecondary || this._showDetachInSecondary()) && appCtxt.get(ZmSetting.DETACH_MAILVIEW_ENABLED) && !appCtxt.isExternalAccount()) { 735 list.push(ZmOperation.DETACH); 736 } 737 list.push(ZmOperation.SHOW_ORIG); 738 return list; 739 }; 740 741 742 743 ZmMailListController.prototype.getDeleteOperation = 744 function() { 745 return ZmOperation.DELETE; 746 }; 747 748 ZmMailListController.prototype._setActiveSearch = 749 function(view) { 750 // save info. returned by search result 751 if (this._activeSearch) { 752 if (this._list) { 753 this._list.setHasMore(this._activeSearch.getAttribute("more")); 754 } 755 if (this._listView[view]) { 756 this._listView[view].offset = parseInt(this._activeSearch.getAttribute("offset")); 757 } 758 } 759 }; 760 761 762 /** 763 * checks whether some of the selected messages are read and unread. returns it as a 2 flag object with "hasRead" and "hasUnread" attributes. 764 * 765 * @private 766 */ 767 ZmMailListController.prototype._getReadStatus = 768 function() { 769 770 var status = {hasRead : false, hasUnread : false} 771 772 // dont bother checking for read/unread state for read-only folders 773 var folder = this._getSearchFolder(); 774 if (folder && folder.isReadOnly()) { 775 return status; 776 } 777 778 var items = this.getItems(); 779 780 for (var i = 0; i < items.length; i++) { 781 var item = items[i]; 782 // TODO: refactor / clean up 783 if (item.type == ZmItem.MSG) { 784 status[item.isUnread ? "hasUnread" : "hasRead"] = true; 785 status[item.isFlagged ? "hasFlagged" : "hasUnflagged"] = true; 786 } 787 else if (item.type == ZmItem.CONV) { 788 status.hasUnread = status.hasUnread || item.hasFlag(ZmItem.FLAG_UNREAD, true); 789 status.hasRead = status.hasRead || item.hasFlag(ZmItem.FLAG_UNREAD, false); 790 status.hasUnflagged = status.hasUnflagged || item.hasFlag(ZmItem.FLAG_FLAGGED, false); 791 status.hasFlagged = status.hasFlagged || item.hasFlag(ZmItem.FLAG_FLAGGED, true); 792 } 793 if (status.hasUnread && status.hasRead) { 794 break; 795 } 796 } 797 798 return status; 799 }; 800 801 802 ZmMailListController.prototype._getConvMuteStatus = 803 function() { 804 var items = this.getItems(); 805 var status = { 806 hasMuteConv: false, 807 hasUnmuteConv: false 808 }, 809 item, 810 i; 811 for (i=0; i<items.length; i++) { 812 item = items[i]; 813 if (item.isMute) { 814 status.hasMuteConv = true; 815 } 816 else { 817 status.hasUnmuteConv = true; 818 } 819 } 820 return status; 821 }; 822 823 /** 824 * Dynamically enable/disable the mark read/unread menu items. 825 * 826 * @private 827 */ 828 ZmMailListController.prototype._enableReadUnreadToolbarActions = 829 function() { 830 var menu = this.getCurrentToolbar().getActionsMenu(); 831 this._enableFlags(menu); 832 }; 833 834 ZmMailListController.prototype._enableMuteUnmuteToolbarActions = 835 function() { 836 var menu = this.getCurrentToolbar().getActionsMenu(); 837 this._enableMuteUnmute(menu); 838 }; 839 840 ZmMailListController.prototype._actionsButtonListener = 841 function(ev) { 842 this._enableReadUnreadToolbarActions(); 843 this._enableMuteUnmuteToolbarActions(); 844 ZmBaseController.prototype._actionsButtonListener.call(this, ev); 845 }; 846 847 // List listeners 848 849 ZmMailListController.prototype._listSelectionListener = 850 function(ev) { 851 // offline: when opening a message in Outbox, move it to the appropriate 852 // account's Drafts folder first 853 var search = appCtxt.getCurrentSearch(); 854 if (appCtxt.isOffline && 855 ev.detail == DwtListView.ITEM_DBL_CLICKED && 856 ev.item && ev.item.isDraft && 857 search && search.folderId == ZmFolder.ID_OUTBOX) 858 { 859 var account = ev.item.account || ZmOrganizer.parseId(ev.item.id).account; 860 var folder = appCtxt.getById(ZmOrganizer.getSystemId(ZmFolder.ID_DRAFTS, account)); 861 this._list.moveItems({items:[ev.item], folder:folder}); 862 return false; 863 } 864 var folderId = ev.item.folderId || (search && search.folderId); 865 var folder = folderId && appCtxt.getById(folderId); 866 867 if (ev.field === ZmItem.F_FLAG && this._isPermissionDenied(folder)) { 868 return true; 869 } 870 if (ev.field === ZmItem.F_READ) { 871 if (!this._isPermissionDenied(folder)) { 872 this._doMarkRead([ev.item], ev.item.isUnread); 873 } 874 return true; 875 } 876 else { 877 return ZmListController.prototype._listSelectionListener.apply(this, arguments); 878 } 879 }; 880 881 // Based on context, enable read/unread operation, add/edit contact. 882 ZmMailListController.prototype._listActionListener = 883 function(ev) { 884 885 ZmListController.prototype._listActionListener.call(this, ev); 886 887 var items = this._listView[this._currentViewId].getSelection(); 888 var folder = this._getSearchFolder(); 889 890 // bug fix #3602 891 var address = (appCtxt.get(ZmSetting.CONTACTS_ENABLED) && ev.field == ZmItem.F_PARTICIPANT) 892 ? ev.detail 893 : ((ev.item && ev.item.isZmMailMsg) ? ev.item.getAddress(AjxEmailAddress.FROM) : null); 894 895 var email = address && address.getAddress(); 896 897 var item = (items && items.length == 1) ? items[0] : null; 898 if (this.isDraftsFolder() || (item && item.isDraft && item.type != ZmId.ITEM_CONV)) { //note that we never treat a conversation as a draft for actions. See also bug 64494 899 // show drafts menu 900 this._initializeDraftsActionMenu(); 901 this._setDraftSearchMenu(address, item, ev); 902 if (address) 903 this._actionEv.address = address; 904 this._setTagMenu(this._draftsActionMenu); 905 this._resetOperations(this._draftsActionMenu, items.length); 906 appCtxt.notifyZimlets("onMailActionMenuResetOperations", [this, this._draftsActionMenu]); 907 this._draftsActionMenu.popup(0, ev.docX, ev.docY); 908 } 909 else if (!appCtxt.isExternalAccount() && email && items.length == 1 && 910 (appCtxt.get(ZmSetting.CONTACTS_ENABLED) && (ev.field == ZmItem.F_PARTICIPANT || ev.field == ZmItem.F_FROM))) 911 { 912 // show participant menu 913 this._initializeParticipantActionMenu(); 914 this._setTagMenu(this._participantActionMenu); 915 this._actionEv.address = address; 916 this._setupSpamButton(this._participantActionMenu); 917 this._resetOperations(this._participantActionMenu, items.length); 918 appCtxt.notifyZimlets("onMailActionMenuResetOperations", [this, this._participantActionMenu]); 919 this._enableFlags(this._participantActionMenu); 920 this._enableMuteUnmute(this._participantActionMenu); 921 var imItem = this._participantActionMenu.getOp(ZmOperation.IM); 922 var contactsApp = appCtxt.getApp(ZmApp.CONTACTS); 923 if (contactsApp) { 924 this._loadContactForMenu(this._participantActionMenu, address, ev, imItem); 925 } 926 else if (imItem) { 927 // since contacts app is disabled, we won't be making a server call 928 ZmImApp.updateImMenuItemByAddress(imItem, address, true); 929 this._participantActionMenu.popup(0, ev.docX, ev.docY); 930 } 931 } 932 else if (this.isOutboxFolder()) { 933 // show drafts menu 934 //this._initializeOutboxsActionMenu(); 935 } else { 936 var actionMenu = this.getActionMenu(); 937 this._setupSpamButton(actionMenu); 938 this._enableFlags(actionMenu); 939 this._enableMuteUnmute(actionMenu); 940 appCtxt.notifyZimlets("onMailActionMenuResetOperations", [this, actionMenu]); 941 actionMenu.popup(0, ev.docX, ev.docY); 942 if (ev.ersatz) { 943 // menu popped up via keyboard nav 944 actionMenu.setSelectedItem(0); 945 } 946 } 947 948 if (!folder) { 949 //might have come from searching on sent items and want to stay in search sent view (i.e. recipient instead of sender) 950 folder = this._getActiveSearchFolder(); 951 } 952 953 if (folder && (folder.nId == ZmFolder.ID_SENT && 954 (this._participantActionMenu && this._participantActionMenu.getOp(ZmOperation.SEARCH_MENU)))) { 955 if (item && (item.getAddresses(AjxEmailAddress.TO).getArray().length + item.getAddresses(AjxEmailAddress.CC).getArray().length) > 1){ 956 ZmOperation.setOperation(this._participantActionMenu.getSearchMenu(), ZmOperation.SEARCH_TO, ZmOperation.SEARCH_TO, ZmMsg.findEmailToRecipients); 957 ZmOperation.setOperation(this._participantActionMenu.getSearchMenu(), ZmOperation.SEARCH, ZmOperation.SEARCH, ZmMsg.findEmailFromRecipients); 958 } 959 else{ 960 ZmOperation.setOperation(this._participantActionMenu.getSearchMenu(), ZmOperation.SEARCH_TO, ZmOperation.SEARCH_TO, ZmMsg.findEmailToRecipient); 961 ZmOperation.setOperation(this._participantActionMenu.getSearchMenu(), ZmOperation.SEARCH, ZmOperation.SEARCH, ZmMsg.findEmailFromRecipient); 962 } 963 } 964 else if (this._participantActionMenu && this._participantActionMenu.getOp(ZmOperation.SEARCH_MENU)) { 965 ZmOperation.setOperation(this._participantActionMenu.getSearchMenu(), ZmOperation.SEARCH_TO, ZmOperation.SEARCH_TO, ZmMsg.findEmailToSender); 966 ZmOperation.setOperation(this._participantActionMenu.getSearchMenu(), ZmOperation.SEARCH, ZmOperation.SEARCH, ZmMsg.findEmailFromSender); 967 } 968 }; 969 970 // Operation listeners 971 972 ZmMailListController.prototype._markReadListener = 973 function(ev) { 974 var callback = this._getMarkReadCallback(); 975 this._doMarkRead(this._listView[this._currentViewId].getSelection(), true, callback); 976 }; 977 978 ZmMailListController.prototype._showOrigListener = 979 function() { 980 var msg = this.getMsg(); 981 if (!msg) { return; } 982 983 setTimeout(this._showMsgSource.bind(this, msg), 100); // Other listeners are focusing the main window, so delay the window opening for just a bit 984 }; 985 986 ZmMailListController.prototype._showMsgSource = 987 function(msg) { 988 var msgFetchUrl = appCtxt.get(ZmSetting.CSFE_MSG_FETCHER_URI) + "&view=text&id=" + msg.id + (msg.partId ? "&part=" + msg.partId : ""); 989 990 // create a new window w/ generated msg based on msg id 991 window.open(msgFetchUrl, "_blank", "menubar=yes,resizable=yes,scrollbars=yes"); 992 }; 993 994 995 /** 996 * Per bug #7257, read receipt must be sent if user explicitly marks a message 997 * read under the following conditions: 998 * 999 * 0. read receipt is requested, user agrees to send it 1000 * 1. reading pane is on 1001 * 2. mark as read preference is set to "never" 1002 * 3. the message currently being read in the reading pane is in the list of 1003 * convs/msgs selected for mark as read 1004 * 1005 * If all these conditions are met, a callback to run sendReadReceipt() is returned. 1006 * 1007 * @private 1008 */ 1009 ZmMailListController.prototype._getMarkReadCallback = 1010 function() { 1011 var view = this._listView[this._currentViewId]; 1012 var items = view.getSelection(); 1013 1014 if (this.isReadingPaneOn() && appCtxt.get(ZmSetting.MARK_MSG_READ) == -1) { 1015 // check if current message being read is the message in the selection list 1016 var msg = view.parent.getItemView && view.parent.getItemView().getItem(); 1017 if (msg && msg.readReceiptRequested) { 1018 for (var i = 0; i < items.length; i++) { 1019 var item = items[i]; 1020 var itemId = (item.id < 0) ? (item.id * (-1)) : item.id; 1021 if (itemId == msg.id) { 1022 return this.sendReadReceipt.bind(this, msg); 1023 } 1024 } 1025 } 1026 } 1027 return null; 1028 }; 1029 1030 ZmMailListController.prototype._markUnreadListener = 1031 function(ev) { 1032 1033 appCtxt.killMarkReadTimer(); 1034 1035 this._doMarkRead(this._listView[this._currentViewId].getSelection(), false); 1036 }; 1037 1038 /** 1039 * flags or unflags (based on the status of the first item. See doFlag) 1040 * @param ev 1041 * @private 1042 */ 1043 ZmMailListController.prototype._flagListener = 1044 function(on) { 1045 this._doFlag(this._listView[this._currentViewId].getSelection(), on); 1046 }; 1047 1048 1049 ZmMailListController.prototype._replyListener = 1050 function(ev) { 1051 var action = ev.item.getData(ZmOperation.KEY_ID); 1052 if (!action || action == ZmOperation.REPLY_MENU) { 1053 action = ZmOperation.REPLY; 1054 } 1055 1056 this._doAction({ev: ev, action: action, foldersToOmit: ZmMailApp.getReplyFoldersToOmit()}); 1057 }; 1058 1059 ZmMailListController.prototype._forwardListener = 1060 function(ev) { 1061 var action = ev.item.getData(ZmOperation.KEY_ID); 1062 this._doAction({ev: ev, action: action, foldersToOmit: ZmMailApp.getReplyFoldersToOmit()}); 1063 }; 1064 1065 ZmMailListController.prototype._forwardConvListener = function(ev) { 1066 this._doAction({ev: ev, action: ZmOperation.FORWARD_CONV, foldersToOmit: ZmMailApp.getReplyFoldersToOmit()}); 1067 }; 1068 1069 // This method may be called with a null ev parameter 1070 ZmMailListController.prototype._doAction = 1071 function(params) { 1072 1073 // get msg w/ addrs to select identity from - don't load it yet (no callback) 1074 // for special handling of multiple forwarded messages, see _handleResponseDoAction 1075 var msg = params.msg || this.getMsg(params); 1076 if (!msg) { 1077 return; 1078 } 1079 1080 // use resolved msg to figure out identity/persona to use for compose 1081 var collection = appCtxt.getIdentityCollection(); 1082 var identity = collection.selectIdentity(msg); 1083 1084 var action = params.action; 1085 if (!action || action == ZmOperation.FORWARD_MENU || action == ZmOperation.FORWARD) { 1086 params.origAction = action; 1087 action = params.action = (appCtxt.get(ZmSetting.FORWARD_INCLUDE_ORIG) == ZmSetting.INC_ATTACH) 1088 ? ZmOperation.FORWARD_ATT : ZmOperation.FORWARD_INLINE; 1089 1090 if (msg.isInvite()) { 1091 action = params.action = ZmOperation.FORWARD_ATT; 1092 } 1093 } 1094 if (action === ZmOperation.FORWARD_CONV) { 1095 params.origAction = action; 1096 // need to remember conv since a single right-clicked item has its selection cleared when 1097 // the action menu is popped down during the request to load the conv 1098 var selection = this.getSelection(); 1099 params.conv = selection && selection.length === 1 ? selection[0] : null; 1100 action = params.action = ZmOperation.FORWARD_ATT; 1101 } 1102 1103 // if html compose is allowed and if opening draft always request html 1104 // otherwise check if user prefers html or 1105 // msg hasn't been loaded yet and user prefers format of orig msg 1106 var htmlEnabled = appCtxt.get(ZmSetting.HTML_COMPOSE_ENABLED); 1107 var prefersHtml = (appCtxt.get(ZmSetting.COMPOSE_AS_FORMAT) == ZmSetting.COMPOSE_HTML); 1108 var sameFormat = appCtxt.get(ZmSetting.COMPOSE_SAME_FORMAT); 1109 params.getHtml = (htmlEnabled && (action == ZmOperation.DRAFT || (prefersHtml || (!msg._loaded && sameFormat)))); 1110 if (action == ZmOperation.DRAFT) { 1111 params.listController = this; 1112 //always reload the draft msg except offline created msg 1113 if (!msg.isOfflineCreated) { 1114 params.forceLoad = true; 1115 } 1116 } 1117 1118 // bug: 38928 - if user viewed entire truncated message, fetch the whole 1119 // thing when replying/forwarding 1120 if (action != ZmOperation.NEW_MESSAGE && action != ZmOperation.DRAFT && msg.viewEntireMessage) { 1121 params.noTruncate = true; 1122 params.forceLoad = true; 1123 } 1124 1125 if (action == ZmOperation.DRAFT || action == ZmOperation.FORWARD_INLINE || 1126 action == ZmOperation.REPLY || action == ZmOperation.REPLY_ALL) { 1127 var bp = msg.getBodyPart(); 1128 if ((bp && bp.isTruncated) || !msg._loaded) { 1129 params.noTruncate = true; 1130 params.forceLoad = true; 1131 } 1132 } 1133 1134 if (params.msg) { 1135 this._handleResponseDoAction(params, params.msg); 1136 } 1137 else { 1138 var respCallback = this._handleResponseDoAction.bind(this, params); 1139 // TODO: pointless to load msg when forwarding as att 1140 this._getLoadedMsg(params, respCallback); 1141 } 1142 }; 1143 1144 ZmMailListController.prototype._handleResponseDoAction = 1145 function(params, msg, finalChoice) { 1146 1147 if (!msg) { return; } 1148 1149 msg._instanceDate = params.instanceDate; 1150 1151 params.inNewWindow = (!appCtxt.isChildWindow && this._app._inNewWindow(params.ev)); 1152 1153 if (msg.list && msg.isUnread && !appCtxt.getById(msg.folderId).isReadOnly()) { 1154 msg.list.markRead({items:[msg], value:true}); 1155 } 1156 1157 // check to see if we're forwarding multiple msgs, in which case we do them as attachments; 1158 // also check to see if we're forwarding an invite; if so, go to appt compose 1159 var action = params.action; 1160 if (action == ZmOperation.FORWARD_ATT || action == ZmOperation.FORWARD_INLINE) { 1161 var selection, selCount; 1162 if (params.msg) { 1163 selCount = 1 1164 } 1165 else { 1166 var cview = this._listView[this._currentViewId]; 1167 if (cview) { 1168 selection = params.conv ? [ params.conv ] : cview.getSelection(); 1169 selCount = params.conv ? 1 : selection.length; 1170 } 1171 } 1172 // bug 43428 - invitation should be forwarded using appt forward view 1173 if (selCount == 1 && msg.forwardAsInvite()) { 1174 var ac = window.parentAppCtxt || window.appCtxt; 1175 if (ac.get(ZmSetting.CALENDAR_ENABLED)) { 1176 var controller = ac.getApp(ZmApp.CALENDAR).getCalController(); 1177 controller.forwardInvite(msg); 1178 if (appCtxt.isChildWindow) { 1179 window.close(); 1180 } 1181 return; 1182 } 1183 } 1184 1185 // forward multiple msgs as attachments 1186 if (selCount > 1 || params.origAction === ZmOperation.FORWARD_CONV) { 1187 action = params.action = ZmOperation.FORWARD_ATT; 1188 this._handleLoadMsgs(params, selection); 1189 return; 1190 } 1191 } 1192 else if (appCtxt.isOffline && action == ZmOperation.DRAFT) { 1193 var folder = appCtxt.getById(msg.folderId); 1194 params.accountName = folder && folder.getAccount().name; 1195 } 1196 else if (action == ZmOperation.DECLINE_PROPOSAL) { 1197 params.subjOverride = this._getInviteReplySubject(action) + msg.subject; 1198 } 1199 1200 params.msg = msg; 1201 AjxDispatcher.run("Compose", params); 1202 }; 1203 1204 1205 ZmMailListController.prototype._redirectListener = 1206 function(ev) { 1207 1208 var action = ev.item.getData(ZmOperation.KEY_ID); 1209 var msg = this.getMsg({ev:ev, action:action}); 1210 if (!msg) return; 1211 1212 var redirectDialog = appCtxt.getMailRedirectDialog(); 1213 var redirectDialogCB = this._redirectCallback.bind(this, msg); 1214 ZmController.showDialog(redirectDialog, redirectDialogCB); 1215 }; 1216 1217 1218 ZmMailListController.prototype._redirectCallback = 1219 function(msg) { 1220 if (!msg) return; 1221 1222 var redirectDialog = appCtxt.getMailRedirectDialog(); 1223 var addrs = redirectDialog.getAddrs(); 1224 // Code copied from ZmComposeView. Should consolidate along with the 1225 // ZmRecipient code, i.e. the corresponding recipient controller code. 1226 if (addrs.gotAddress) { 1227 if (addrs[ZmRecipients.BAD].size()) { 1228 // Any bad addresses? If there are bad ones, ask the user if they want to send anyway. 1229 var bad = AjxStringUtil.htmlEncode(addrs[ZmRecipients.BAD].toString(AjxEmailAddress.SEPARATOR)); 1230 var badMsg = AjxMessageFormat.format(ZmMsg.compBadAddresses, bad); 1231 var cd = appCtxt.getOkCancelMsgDialog(); 1232 cd.reset(); 1233 cd.setMessage(badMsg, DwtMessageDialog.WARNING_STYLE); 1234 cd.registerCallback(DwtDialog.OK_BUTTON, this._badRedirectAddrsOkCallback, this, [addrs, cd, msg]); 1235 cd.registerCallback(DwtDialog.CANCEL_BUTTON, this._badRedirectAddrsCancelCallback, this, cd); 1236 cd.setVisible(true); // per fix for bug 3209 1237 cd.popup(); 1238 } else { 1239 redirectDialog.popdown(); 1240 msg.redirect(addrs, this._handleSendRedirect.bind(this)); 1241 } 1242 } else { 1243 redirectDialog.popdown(); 1244 } 1245 }; 1246 1247 // User has agreed to send message with bad addresses 1248 ZmMailListController.prototype._badRedirectAddrsOkCallback = 1249 function(addrs, dialog, msg) { 1250 dialog.popdown(); 1251 appCtxt.getMailRedirectDialog().popdown(); 1252 msg.redirect(addrs, this._handleSendRedirect.bind(this)); 1253 }; 1254 1255 1256 // User has declined to send message with bad addresses - popdown the bad addr dialog, 1257 // returning to the redirect dialog 1258 ZmMailListController.prototype._badRedirectAddrsCancelCallback = 1259 function(dialog) { 1260 dialog.popdown(); 1261 }; 1262 1263 ZmMailListController.prototype._handleLoadMsgs = 1264 function(params, selection) { 1265 1266 var msgIds = new AjxVector(), 1267 foldersToOmit = params.foldersToOmit || {}; 1268 1269 for (var i = 0; i < selection.length; i++) { 1270 var item = selection[i]; 1271 if (item.type == ZmItem.CONV) { 1272 for (var j = 0; j < item.msgIds.length; j++) { 1273 var msgId = item.msgIds[j]; 1274 if (!foldersToOmit[item.msgFolder[msgId]]) { 1275 msgIds.add(msgId); 1276 } 1277 } 1278 } 1279 else { 1280 if (!msgIds.contains(item.id)) { 1281 msgIds.add(item.id); 1282 } 1283 } 1284 } 1285 params.msgIds = msgIds.getArray(); 1286 params.selectedMessages = selection; 1287 1288 AjxDispatcher.run("Compose", params); 1289 }; 1290 1291 ZmMailListController.prototype._handleSendRedirect = 1292 function() { 1293 appCtxt.setStatusMsg(ZmMsg.redirectSent, ZmStatusView.LEVEL_INFO, null); 1294 }; 1295 1296 /** 1297 * Marks a mail item read if appropriate, possibly after a delay. An arg can be passed so that this function 1298 * just returns whether the item can be marked read now, which typically results in the setting of the "read" 1299 * flag in a retrieval request to the server. After that, it can be called without that arg in order to mark 1300 * the item read after a delay if necessary. 1301 * 1302 * @param {ZmMailItem} item msg or conv 1303 * @param {boolean} check if true, return true if msg can be marked read now, without marking it read 1304 */ 1305 ZmMailListController.prototype._handleMarkRead = 1306 function(item, check) { 1307 1308 var convView = this._convView; 1309 var waitOnMarkRead = convView && convView.isWaitOnMarkRead(); 1310 if (item && item.isUnread && !waitOnMarkRead) { 1311 if (!item.isReadOnly() && !appCtxt.isExternalAccount()) { 1312 var markRead = appCtxt.get(ZmSetting.MARK_MSG_READ); 1313 if (markRead == ZmSetting.MARK_READ_NOW) { 1314 if (check) { 1315 return true; 1316 } 1317 else { 1318 // msg was cached as unread, mark it read now 1319 this._doMarkRead([item], true); 1320 } 1321 } else if (markRead > 0) { 1322 if (!appCtxt.markReadAction) { 1323 appCtxt.markReadAction = new AjxTimedAction(this, this._markReadAction); 1324 } 1325 appCtxt.markReadAction.args = [item]; 1326 appCtxt.markReadActionId = AjxTimedAction.scheduleAction(appCtxt.markReadAction, markRead * 1000); 1327 } 1328 } 1329 } 1330 return false; 1331 }; 1332 1333 ZmMailListController.prototype._markReadAction = 1334 function(msg) { 1335 this._doMarkRead([msg], true); 1336 }; 1337 1338 ZmMailListController.prototype._doMarkRead = 1339 function(items, on, callback, forceCallback) { 1340 1341 var params = {items:items, value:on, callback:callback, noBusyOverlay: true}; 1342 var list = params.list = this._getList(params.items); 1343 params.forceCallback = forceCallback; 1344 this._setupContinuation(this._doMarkRead, [on, callback], params); 1345 list.markRead(params); 1346 }; 1347 1348 ZmMailListController.prototype._doMarkMute = 1349 function(items, on, callback, forceCallback) { 1350 1351 var params = {items:items, value:on, callback:callback}; 1352 var list = params.list = this._getList(params.items); 1353 params.forceCallback = forceCallback; 1354 this._setupContinuation(this._doMarkMute, [on, callback], params); 1355 list.markMute(params); 1356 }; 1357 1358 /** 1359 * Marks the given items as "spam" or "not spam". Items marked as spam are moved to 1360 * the Junk folder. If items are being moved out of the Junk folder, they will be 1361 * marked "not spam", and the destination folder may be provided. It defaults to Inbox 1362 * if not present. 1363 * 1364 * @param items [Array] a list of items to move 1365 * @param markAsSpam [boolean] spam or not spam 1366 * @param folder [ZmFolder] destination folder 1367 */ 1368 ZmMailListController.prototype._doSpam = 1369 function(items, markAsSpam, folder) { 1370 1371 this._listView[this._currentViewId]._itemToSelect = this._getNextItemToSelect(); 1372 items = AjxUtil.toArray(items); 1373 1374 var params = {items:items, 1375 markAsSpam:markAsSpam, 1376 folder:folder, 1377 childWin:appCtxt.isChildWindow && window, 1378 closeChildWin: appCtxt.isChildWindow}; 1379 1380 var allDoneCallback = this._getAllDoneCallback(); 1381 var list = params.list = this._getList(params.items); 1382 this._setupContinuation(this._doSpam, [markAsSpam, folder], params, allDoneCallback); 1383 list.spamItems(params); 1384 }; 1385 1386 ZmMailListController.prototype._inviteReplyHandler = 1387 function(ev) { 1388 var ac = window.parentAppCtxt || window.appCtxt; 1389 1390 this._listView[this._currentViewId]._itemToSelect = this._getNextItemToSelect(); 1391 ac.getAppController().focusContentPane(); 1392 1393 var type = ev._inviteReplyType; 1394 if (type == ZmOperation.PROPOSE_NEW_TIME ) { 1395 ac.getApp(ZmApp.CALENDAR).getCalController().proposeNewTime(ev._msg); 1396 if (appCtxt.isChildWindow) { 1397 window.close(); 1398 } 1399 } 1400 else if (type == ZmOperation.ACCEPT_PROPOSAL) { 1401 this._acceptProposedTime(ev._inviteComponentId, ev._msg); 1402 } 1403 else if (type == ZmOperation.DECLINE_PROPOSAL) { 1404 this._declineProposedTime(ev._inviteComponentId, ev._msg); 1405 } 1406 else if (type == ZmOperation.INVITE_REPLY_ACCEPT || 1407 type == ZmOperation.EDIT_REPLY_CANCEL || 1408 type == ZmOperation.INVITE_REPLY_DECLINE || 1409 type == ZmOperation.INVITE_REPLY_TENTATIVE) 1410 { 1411 this._editInviteReply(ZmMailListController.INVITE_REPLY_MAP[type], ev._inviteComponentId, null, null, ev._inviteReplyFolderId); 1412 } 1413 else { 1414 var callback = new AjxCallback(this, this._handleInviteReplySent); 1415 var accountName = ac.multiAccounts && ac.accountList.mainAccount.name; 1416 this._sendInviteReply(type, ev._inviteComponentId, null, accountName, null, ev._msg, ev._inviteReplyFolderId, callback); 1417 } 1418 return false; 1419 }; 1420 1421 ZmMailListController.prototype._handleInviteReplySent = 1422 function(result, newPtst) { 1423 var inviteMsgView = this.getCurrentView().getInviteMsgView(); 1424 if (!inviteMsgView || !newPtst) { 1425 return; 1426 } 1427 inviteMsgView.enableToolbarButtons(newPtst); 1428 inviteMsgView.updatePtstMsg(newPtst); 1429 }; 1430 1431 ZmMailListController.prototype._shareHandler = 1432 function(ev) { 1433 var msg = this.getMsg(); 1434 var fromAddr = msg ? msg.getAddress(AjxEmailAddress.FROM).address : null; 1435 1436 if (ev._buttonId == ZmOperation.SHARE_ACCEPT) { 1437 var acceptDialog = appCtxt.getAcceptShareDialog(); 1438 acceptDialog.setAcceptListener(this._acceptShareListener); 1439 acceptDialog.popup(ev._share, fromAddr); 1440 } else if (ev._buttonId == ZmOperation.SHARE_DECLINE) { 1441 var declineDialog = appCtxt.getDeclineShareDialog(); 1442 declineDialog.setDeclineListener(this._declineShareListener); 1443 declineDialog.popup(ev._share, fromAddr); 1444 } 1445 }; 1446 1447 ZmMailListController.prototype._subscribeHandler = 1448 function(ev) { 1449 var req = ev._subscribeReq; 1450 var statusMsg; 1451 var approve = false; 1452 if (ev._buttonId == ZmOperation.SUBSCRIBE_APPROVE) { 1453 statusMsg = req.subscribe ? ZmMsg.dlSubscribeRequestApproved : ZmMsg.dlUnsubscribeRequestApproved; 1454 approve = true; 1455 } 1456 else if (ev._buttonId == ZmOperation.SUBSCRIBE_REJECT) { 1457 statusMsg = req.subscribe ? ZmMsg.dlSubscribeRequestRejected : ZmMsg.dlUnsubscribeRequestRejected; 1458 } 1459 1460 var jsonObj = { 1461 DistributionListActionRequest: { 1462 _jsns: "urn:zimbraAccount", 1463 dl: {by: "name", 1464 _content: req.dl.email 1465 }, 1466 action: {op: approve ? "acceptSubsReq" : "rejectSubsReq", 1467 subsReq: {op: req.subscribe ? "subscribe" : "unsubscribe", 1468 _content: req.user.email 1469 } 1470 } 1471 } 1472 }; 1473 var respCallback = this._subscribeResponseHandler.bind(this, statusMsg); 1474 appCtxt.getAppController().sendRequest({jsonObj: jsonObj, asyncMode: true, callback: respCallback}); 1475 1476 1477 1478 }; 1479 1480 ZmMailListController.prototype._subscribeResponseHandler = 1481 function(statusMsg, ev) { 1482 var msg = this.getMsg(); 1483 this._removeActionMsg(msg); 1484 appCtxt.setStatusMsg(statusMsg); 1485 }; 1486 1487 1488 ZmMailListController.prototype._acceptShareHandler = 1489 function(ev) { 1490 var msg = appCtxt.getById(ev._share._msgId); 1491 this._removeActionMsg(msg); 1492 }; 1493 1494 ZmMailListController.prototype._removeActionMsg = 1495 function(msg) { 1496 var folder = appCtxt.getById(ZmFolder.ID_TRASH); 1497 1498 this._listView[this._currentViewId]._itemToSelect = this._getNextItemToSelect(); 1499 var list = msg.list || this.getList(); 1500 var callback = (appCtxt.isChildWindow) 1501 ? (new AjxCallback(this, this._handleAcceptShareInNewWindow)) : null; 1502 list.moveItems({items: [msg], folder: folder, callback: callback, closeChildWin: appCtxt.isChildWindow}); 1503 }; 1504 1505 ZmMailListController.prototype._declineShareHandler = ZmMailListController.prototype._acceptShareHandler; 1506 1507 ZmMailListController.prototype._handleAcceptShareInNewWindow = 1508 function() { 1509 window.close(); 1510 }; 1511 1512 ZmMailListController.prototype._createViewMenu = 1513 function(view) { 1514 var btn = this._toolbar[view].getButton(ZmOperation.VIEW_MENU); 1515 if (!btn) { return; } 1516 1517 btn.setMenu(new AjxCallback(this, this._setupViewMenuItems, [view, btn])); 1518 btn.noMenuBar = true; 1519 }; 1520 1521 1522 ZmMailListController.prototype._setupViewMenu = 1523 function(view) { 1524 1525 // always reset the view menu button icon to reflect the current view 1526 var btn = this._toolbar[view].getButton(ZmOperation.VIEW_MENU); 1527 if (btn) { 1528 var viewType = appCtxt.getViewTypeFromId(view); 1529 btn.setImage(ZmMailListController.GROUP_BY_ICON[viewType]); 1530 } 1531 }; 1532 1533 ZmMailListController.prototype._setupViewMenuItems = 1534 function(view, btn) { 1535 1536 var menu = this._viewMenu = new ZmPopupMenu(btn, null, null, this); 1537 btn.setMenu(menu); 1538 var isExternalAccount = appCtxt.isExternalAccount(), 1539 convsEnabled = appCtxt.get(ZmSetting.CONVERSATIONS_ENABLED); 1540 1541 if (convsEnabled && this.supportsGrouping()) { 1542 this._setupGroupByMenuItems(view, menu); 1543 } 1544 if (menu.getItemCount() > 0) { 1545 new DwtMenuItem({ 1546 parent: menu, 1547 style: DwtMenuItem.SEPARATOR_STYLE 1548 }); 1549 } 1550 this._readingPaneViewMenu = this._setupReadingPaneMenu(view, menu); 1551 if (!isExternalAccount && convsEnabled) { 1552 this._convOrderViewMenu = this._setupConvOrderMenu(view, menu); 1553 } 1554 1555 // add sort and group by menus only if we have headers (not in standalone conv view) 1556 if (this.supportsSorting()) { 1557 this._sortViewMenu = this._setupSortViewMenu(view, menu); 1558 this._groupByViewMenu = this._mailListView._getGroupByActionMenu(menu, true, true); 1559 } 1560 1561 return menu; 1562 }; 1563 1564 ZmMailListController.prototype._setupSortViewMenu = function(view, menu) { 1565 1566 var sortMenuItem = menu.createMenuItem(Dwt.getNextId("SORT_"), { 1567 text: ZmMsg.sortBy, 1568 style: DwtMenuItem.NO_STYLE 1569 }), 1570 sortMenu = this._mailListView._setupSortMenu(sortMenuItem, false); 1571 1572 sortMenuItem.setMenu(sortMenu); 1573 1574 return sortMenu; 1575 }; 1576 1577 ZmMailListController.prototype._setupColHeaderViewMenu = function(view, menu) { 1578 1579 var colHeaderMenuItem = this._colHeaderMenuItem = menu.createMenuItem(Dwt.getNextId("COL_HEADER_"), { 1580 text: ZmMsg.display, 1581 style: DwtMenuItem.NO_STYLE 1582 }), 1583 colHeaderMenu = ZmListView.prototype._getActionMenuForColHeader.call(this._mailListView, true, colHeaderMenuItem, "view"); 1584 1585 colHeaderMenuItem.setMenu(colHeaderMenu); 1586 1587 return colHeaderMenu; 1588 }; 1589 1590 // If we're in the Trash or Junk folder, change the "Delete" button tooltip 1591 ZmMailListController.prototype._setupDeleteButton = 1592 function(parent) { 1593 var folder = this._getSearchFolder(); 1594 var inTrashFolder = (folder && folder.nId == ZmFolder.ID_TRASH); 1595 var inJunkFolder = (folder && folder.nId == ZmFolder.ID_SPAM); 1596 var deleteButton = parent.getButton(ZmOperation.DELETE); 1597 var deleteMenuButton = parent.getButton(ZmOperation.DELETE_MENU); 1598 var tooltip = (inTrashFolder || inJunkFolder) ? ZmMsg.deletePermanentTooltip : ZmMsg.deleteTooltip; 1599 if (deleteButton) { 1600 deleteButton.setToolTipContent(ZmOperation.getToolTip(ZmOperation.DELETE, this.getKeyMapName(), tooltip), true); 1601 } 1602 if (deleteMenuButton) { 1603 deleteMenuButton.setToolTipContent(ZmOperation.getToolTip(ZmOperation.DELETE_MENU, this.getKeyMapName(), tooltip), true); 1604 } 1605 }; 1606 1607 // If we're in the Spam folder, the "Spam" button becomes the "Not Spam" button 1608 ZmMailListController.prototype._setupSpamButton = 1609 function(parent) { 1610 if (!parent) { return; } 1611 1612 var item = parent.getOp(ZmOperation.SPAM); 1613 if (item) { 1614 var folderId = this._getSearchFolderId(); 1615 var folder = appCtxt.getById(folderId); 1616 var inSpamFolder = ((folder && folder.nId == ZmFolder.ID_SPAM) || 1617 (!folder && folderId == ZmFolder.ID_SPAM) || 1618 (this._currentSearch && this._currentSearch.folderId == ZmFolder.ID_SPAM)); // fall back 1619 var inPopupMenu = (parent.isZmActionMenu); 1620 if (parent.isZmButtonToolBar) { 1621 //might still be in a popup if it's in the Actions menu. That's the case now but I do this generically so it works if one day we move it as a main button (might want to do that in the spam folder at least) 1622 inPopupMenu = parent.getActionsMenu() && parent.getActionsMenu().getOp(ZmOperation.SPAM); 1623 } 1624 1625 if (inPopupMenu) { 1626 item.setText(inSpamFolder ? ZmMsg.notJunkMarkLabel : ZmMsg.junkMarkLabel); 1627 } else { 1628 item.setText(inSpamFolder ? ZmMsg.notJunkLabel : ZmMsg.junkLabel); 1629 } 1630 item.setImage(inSpamFolder ? 'NotJunk' : 'JunkMail'); 1631 if (item.setToolTipContent) { 1632 var tooltip = inSpamFolder ? ZmMsg.notJunkTooltip : ZmMsg.junkTooltip; 1633 item.setToolTipContent(ZmOperation.getToolTip(ZmOperation.SPAM, this.getKeyMapName(), tooltip), true); 1634 } 1635 item.isMarkAsSpam = !inSpamFolder; 1636 } 1637 }; 1638 1639 // set tooltip for print button 1640 ZmMailListController.prototype._setupPrintButton = 1641 function(parent) { 1642 if (!parent) { return; } 1643 1644 var item = parent.getOp(ZmOperation.PRINT); 1645 if (item) { 1646 item.setToolTipContent(ZmMsg.printMultiTooltip, true); 1647 } 1648 }; 1649 1650 1651 1652 /** 1653 * Gets the selected message. 1654 * 1655 * @param {Hash} params a hash of parameters 1656 * @return {ZmMailMsg|ZmConv} the selected message 1657 */ 1658 ZmMailListController.prototype.getMsg = 1659 function(params) { 1660 var sel = this._listView[this._currentViewId].getSelection(); 1661 return (sel && sel.length) ? sel[0] : null; 1662 }; 1663 1664 ZmMailListController.prototype._filterListener = 1665 function(isAddress, rule) { 1666 1667 if (isAddress) { 1668 this._handleResponseFilterListener(rule, this._actionEv.address); 1669 } 1670 else { 1671 this._getLoadedMsg(null, this._handleResponseFilterListener.bind(this, rule)); 1672 } 1673 }; 1674 1675 1676 ZmMailListController.prototype._setAddToFilterMenu = 1677 function(parent) { 1678 if (this._filterMenu) { 1679 return; 1680 } 1681 1682 var menuItem = parent.getOp(ZmOperation.ADD_TO_FILTER_RULE); 1683 this._filterMenu = new ZmPopupMenu(menuItem); 1684 menuItem.setMenu(this._filterMenu); 1685 1686 this._rules = AjxDispatcher.run("GetFilterRules"); 1687 this._rules.addChangeListener(this._rulesChangeListener.bind(this)); 1688 this._resetFilterMenu(); 1689 }; 1690 1691 ZmMailListController.prototype._resetFilterMenu = 1692 function() { 1693 var filterItems = this._filterMenu.getItems(); 1694 while (filterItems.length > 0) { 1695 this._filterMenu.removeChild(filterItems[0]); 1696 } 1697 this._rules.loadRules(false, this._populateFiltersMenu.bind(this)); 1698 }; 1699 1700 ZmMailListController.prototype._populateFiltersMenu = 1701 function(results){ 1702 var filters = results.getResponse(); 1703 var menu = this._filterMenu; 1704 1705 var newItem = new DwtMenuItem({parent: menu}); 1706 newItem.setText(ZmMsg.newFilter); 1707 newItem.setImage("Plus"); 1708 newItem.addSelectionListener(this._filterListener.bind(this, true, null)); 1709 1710 if (!filters.size()) { 1711 return; 1712 } 1713 menu.createSeparator(); 1714 1715 for (var i = 0; i < filters.size(); i++) { 1716 var rule = filters.get(i); 1717 var mi = new DwtMenuItem({parent: menu}); 1718 mi.setText(AjxStringUtil.clipByLength(rule.name, 20)); 1719 mi.addSelectionListener(this._filterListener.bind(this, true, rule)); 1720 } 1721 }; 1722 1723 ZmMailListController.prototype._rulesChangeListener = 1724 function(ev){ 1725 if (ev.handled || ev.type !== ZmEvent.S_FILTER) { 1726 return; 1727 } 1728 1729 this._resetFilterMenu(); 1730 ev.handled = true; 1731 }; 1732 1733 ZmMailListController.prototype._createApptListener = 1734 function() { 1735 this._getLoadedMsg(null, this._handleResponseNewApptListener.bind(this)); 1736 }; 1737 1738 ZmMailListController.prototype._createTaskListener = 1739 function() { 1740 this._getLoadedMsg(null, this._handleResponseNewTaskListener.bind(this)); 1741 }; 1742 1743 ZmMailListController.prototype._handleResponseNewApptListener = 1744 function(msg) { 1745 if (!msg) { return; } 1746 if (msg.cloneOf) { 1747 msg = msg.cloneOf; 1748 } 1749 var w = appCtxt.isChildWindow ? window.opener : window; 1750 var calController = w.AjxDispatcher.run("GetCalController"); 1751 calController.newApptFromMailItem(msg, new Date()); 1752 if (appCtxt.isChildWindow) { 1753 window.close(); 1754 } 1755 }; 1756 1757 ZmMailListController.prototype._handleResponseNewTaskListener = 1758 function(msg) { 1759 if (!msg) { return; } 1760 if (msg.cloneOf) { 1761 msg = msg.cloneOf; 1762 } 1763 var w = appCtxt.isChildWindow ? window.opener : window; 1764 var aCtxt = appCtxt.isChildWindow ? parentAppCtxt : appCtxt; 1765 w.AjxDispatcher.require(["TasksCore", "Tasks"]); 1766 aCtxt.getApp(ZmApp.TASKS).newTaskFromMailItem(msg, new Date()); 1767 if (appCtxt.isChildWindow) { 1768 window.close(); 1769 } 1770 }; 1771 1772 ZmMailListController.prototype._handleResponseFilterListener = 1773 function(rule, msgOrAddr) { 1774 1775 if (!msgOrAddr) { 1776 return; 1777 } 1778 1779 // arg can be ZmMailMsg or String (address) 1780 var msg = msgOrAddr.isZmMailMsg ? msgOrAddr : null; 1781 1782 if (msg && msg.cloneOf) { 1783 msg = msg.cloneOf; 1784 } 1785 if (appCtxt.isChildWindow) { 1786 var mailListController = window.opener.AjxDispatcher.run("GetMailListController"); 1787 mailListController._handleResponseFilterListener(rule, msgOrAddr); 1788 window.close(); 1789 return; 1790 } 1791 1792 AjxDispatcher.require(["PreferencesCore", "Preferences"]); 1793 var editMode = !!rule; 1794 if (rule) { 1795 //this is important, without this, in case the user goes to the Filters page, things get messed up and trying to save an 1796 // edited filter complains about the existence of a filter with the same name. 1797 rule = this._rules.getRuleByName(rule.name) || rule; 1798 } 1799 else { 1800 rule = new ZmFilterRule(); 1801 } 1802 1803 if (msg) { 1804 var listId = msg.getListIdHeader(); 1805 if (listId) { 1806 rule.addCondition(ZmFilterRule.TEST_HEADER, ZmFilterRule.OP_CONTAINS, listId, ZmMailMsg.HDR_LISTID); 1807 } 1808 else { 1809 var from = msg.getAddress(AjxEmailAddress.FROM); 1810 if (from) { 1811 var subjMod = ZmFilterRule.C_ADDRESS_VALUE[ZmFilterRule.C_FROM]; 1812 rule.addCondition(ZmFilterRule.TEST_ADDRESS, ZmFilterRule.OP_CONTAINS, from.address, subjMod); 1813 } 1814 var cc = msg.getAddress(AjxEmailAddress.CC); 1815 if (cc) { 1816 var subjMod = ZmFilterRule.C_ADDRESS_VALUE[ZmFilterRule.C_CC]; 1817 rule.addCondition(ZmFilterRule.TEST_ADDRESS, ZmFilterRule.OP_CONTAINS, cc.address, subjMod); 1818 } 1819 var xZimbraDL = msg.getXZimbraDLHeader(); 1820 if (xZimbraDL && xZimbraDL.good) { 1821 var arr = xZimbraDL.good.getArray(); 1822 var max = arr.length < 5 ? arr.length : 5; //limit number of X-Zimbra-DL ids 1823 for (var i=0; i < max; i++) { 1824 rule.addCondition(ZmFilterRule.TEST_HEADER, ZmFilterRule.OP_CONTAINS, arr[i].address, ZmMailMsg.HDR_XZIMBRADL); 1825 } 1826 } 1827 var subj = msg.subject; 1828 if (subj) { 1829 var subjMod = ZmFilterRule.C_HEADER_VALUE[ZmFilterRule.C_SUBJECT]; 1830 rule.addCondition(ZmFilterRule.TEST_HEADER, ZmFilterRule.OP_IS, subj, subjMod); 1831 } 1832 rule.setGroupOp(ZmFilterRule.GROUP_ALL); 1833 } 1834 } 1835 else { 1836 var subjMod = ZmFilterRule.C_ADDRESS_VALUE[ZmFilterRule.C_FROM]; 1837 rule.addCondition(ZmFilterRule.TEST_ADDRESS, ZmFilterRule.OP_CONTAINS, msgOrAddr.isAjxEmailAddress ? msgOrAddr.address : msgOrAddr, subjMod); 1838 } 1839 1840 if (!editMode) { 1841 rule.addAction(ZmFilterRule.A_KEEP); 1842 } 1843 1844 var accountName = appCtxt.multiAccounts && msg && msg.getAccount().name, 1845 folder = msg && appCtxt.getById(msg.getFolderId()), 1846 outgoing = !!(folder && folder.isOutbound()); 1847 1848 appCtxt.getFilterRuleDialog().popup(rule, editMode, null, accountName, outgoing); 1849 }; 1850 1851 /** 1852 * Returns the selected msg, ensuring that it's loaded. 1853 * 1854 * @private 1855 */ 1856 ZmMailListController.prototype._getLoadedMsg = 1857 function(params, callback) { 1858 params = params || {}; 1859 var msg = this.getMsg(params); 1860 if (!msg) { 1861 callback.run(); 1862 } 1863 if (msg._loaded && !params.forceLoad) { 1864 callback.run(msg); 1865 } else { 1866 if (msg.id == this._pendingMsg) { return; } 1867 msg._loadPending = true; 1868 this._pendingMsg = msg.id; 1869 params.markRead = (params.markRead != null) ? params.markRead : this._handleMarkRead(msg, true); 1870 // use prototype in callback because these functions are overridden by ZmConvListController 1871 var respCallback = new AjxCallback(this, ZmMailListController.prototype._handleResponseGetLoadedMsg, [callback, msg]); 1872 msg.load({getHtml:params.getHtml, markRead:params.markRead, callback:respCallback, noBusyOverlay:false, forceLoad: params.forceLoad, noTruncate: params.noTruncate}); 1873 } 1874 }; 1875 1876 ZmMailListController.prototype._handleResponseGetLoadedMsg = 1877 function(callback, msg) { 1878 if (this._pendingMsg && (msg.id != this._pendingMsg)) { return; } 1879 msg._loadPending = false; 1880 this._pendingMsg = null; 1881 callback.run(msg); 1882 }; 1883 1884 ZmMailListController.prototype._getInviteReplyBody = 1885 function(type, instanceDate, isResourceInvite) { 1886 var replyBody; 1887 1888 if (instanceDate) { 1889 switch (type) { 1890 case ZmOperation.REPLY_ACCEPT: replyBody = ZmMsg.defaultInviteReplyAcceptInstanceMessage; break; 1891 case ZmOperation.REPLY_CANCEL: replyBody = ZmMsg.apptInstanceCanceled; break; 1892 case ZmOperation.REPLY_DECLINE: replyBody = ZmMsg.defaultInviteReplyDeclineInstanceMessage; break; 1893 case ZmOperation.REPLY_TENTATIVE: replyBody = ZmMsg.defaultInviteReplyTentativeInstanceMessage; break; 1894 } 1895 1896 if (isResourceInvite) { 1897 switch (type) { 1898 case ZmOperation.REPLY_ACCEPT: replyBody = ZmMsg.defaultInviteReplyResourceAcceptInstanceMessage; break; 1899 case ZmOperation.REPLY_CANCEL: replyBody = ZmMsg.apptInstanceCanceled; break; 1900 case ZmOperation.REPLY_DECLINE: replyBody = ZmMsg.defaultInviteReplyResourceDeclineInstanceMessage; break; 1901 case ZmOperation.REPLY_TENTATIVE: replyBody = ZmMsg.defaultInviteReplyResourceTentativeInstanceMessage; break; 1902 } 1903 } 1904 1905 if (replyBody) { 1906 return AjxMessageFormat.format(replyBody, instanceDate); 1907 } 1908 } 1909 switch (type) { 1910 case ZmOperation.REPLY_ACCEPT: replyBody = ZmMsg.defaultInviteReplyAcceptMessage; break; 1911 case ZmOperation.REPLY_CANCEL: replyBody = ZmMsg.apptCanceled; break; 1912 case ZmOperation.DECLINE_PROPOSAL: replyBody = ""; break; 1913 case ZmOperation.REPLY_DECLINE: replyBody = ZmMsg.defaultInviteReplyDeclineMessage; break; 1914 case ZmOperation.REPLY_TENTATIVE: replyBody = ZmMsg.defaultInviteReplyTentativeMessage; break; 1915 case ZmOperation.REPLY_NEW_TIME: replyBody = ZmMsg.defaultInviteReplyNewTimeMessage; break; 1916 } 1917 1918 if (isResourceInvite) { 1919 switch (type) { 1920 case ZmOperation.REPLY_ACCEPT: replyBody = ZmMsg.defaultInviteReplyResourceAcceptMessage; break; 1921 case ZmOperation.REPLY_CANCEL: replyBody = ZmMsg.apptCanceled; break; 1922 case ZmOperation.REPLY_DECLINE: replyBody = ZmMsg.defaultInviteReplyResourceDeclineMessage; break; 1923 case ZmOperation.REPLY_TENTATIVE: replyBody = ZmMsg.defaultInviteReplyResourceTentativeMessage; break; 1924 case ZmOperation.REPLY_NEW_TIME: replyBody = ZmMsg.defaultInviteReplyNewTimeMessage; break; 1925 } 1926 } 1927 1928 //format the escaped apostrophe in ZmMsg entry 1929 if (replyBody) { 1930 replyBody = AjxMessageFormat.format(replyBody, []); 1931 } 1932 return replyBody; 1933 }; 1934 1935 ZmMailListController.prototype._getInviteReplySubject = 1936 function(type) { 1937 var replySubject = null; 1938 switch (type) { 1939 case ZmOperation.REPLY_ACCEPT: replySubject = ZmMsg.subjectAccept + ": "; break; 1940 case ZmOperation.DECLINE_PROPOSAL: replySubject = ZmMsg.subjectDecline + " - "; break; 1941 case ZmOperation.REPLY_DECLINE: replySubject = ZmMsg.subjectDecline + ": "; break; 1942 case ZmOperation.REPLY_TENTATIVE: replySubject = ZmMsg.subjectTentative + ": "; break; 1943 case ZmOperation.REPLY_NEW_TIME: replySubject = ZmMsg.subjectNewTime + ": "; break; 1944 } 1945 return replySubject; 1946 }; 1947 1948 ZmMailListController.prototype._editInviteReply = 1949 function(action, componentId, instanceDate, accountName, acceptFolderId) { 1950 var replyBody = this._getInviteReplyBody(action, instanceDate); 1951 this._doAction({action:action, extraBodyText:replyBody, instanceDate:instanceDate, accountName:accountName, acceptFolderId: acceptFolderId}); 1952 }; 1953 1954 ZmMailListController.prototype._acceptProposedTime = 1955 function(componentId, origMsg) { 1956 var invite = origMsg.invite; 1957 var apptId = invite.getAppointmentId(); 1958 var ac = window.parentAppCtxt || window.appCtxt; 1959 var controller = ac.getApp(ZmApp.CALENDAR).getCalController(); 1960 var callback = new AjxCallback(this, this._handleAcceptDeclineProposedTime, [origMsg]); 1961 controller.acceptProposedTime(apptId, invite, appCtxt.isChildWindow ? null : callback); 1962 if (appCtxt.isChildWindow) { 1963 window.close(); 1964 } 1965 }; 1966 1967 ZmMailListController.prototype._declineProposedTime = 1968 function(componentId, origMsg) { 1969 var replyBody = this._getInviteReplyBody(ZmOperation.DECLINE_PROPOSAL); 1970 var callback = new AjxCallback(this, this._handleAcceptDeclineProposedTime, [origMsg]); 1971 this._doAction({action:ZmOperation.DECLINE_PROPOSAL, extraBodyText:replyBody, instanceDate:null, sendMsgCallback: callback}); 1972 }; 1973 1974 ZmMailListController.prototype._handleAcceptDeclineProposedTime = 1975 function(origMsg) { 1976 this._doDelete([origMsg]); 1977 }; 1978 1979 ZmMailListController.prototype._sendInviteReply = 1980 function(type, componentId, instanceDate, accountName, ignoreNotify, origMsg, acceptFolderId, callback) { 1981 var msg = new ZmMailMsg(); 1982 AjxDispatcher.require(["MailCore", "CalendarCore"]); 1983 1984 msg._origMsg = origMsg || this.getMsg(); 1985 msg.inviteMode = type; 1986 msg.isReplied = true; 1987 msg.isForwarded = false; 1988 msg.isInviteReply = true; 1989 msg.acceptFolderId = acceptFolderId; 1990 msg.folderId = msg._origMsg.folderId; 1991 1992 var replyActionMode = ZmMailListController.REPLY_ACTION_MAP[type] ? ZmMailListController.REPLY_ACTION_MAP[type] : type; 1993 var replyBody = this._getInviteReplyBody(replyActionMode, instanceDate, msg._origMsg.isResourceInvite()); 1994 if (replyBody != null) { 1995 var dummyAppt = new ZmAppt(); 1996 dummyAppt.setFromMessage(msg._origMsg); 1997 1998 var tcontent = dummyAppt.getTextSummary() + "\n" + replyBody; 1999 var textPart = new ZmMimePart(); 2000 textPart.setContentType(ZmMimeTable.TEXT_PLAIN); 2001 textPart.setContent(tcontent); 2002 2003 var hcontent = dummyAppt.getHtmlSummary() + "<p>" + replyBody + "</p>"; 2004 var htmlPart = new ZmMimePart(); 2005 htmlPart.setContentType(ZmMimeTable.TEXT_HTML); 2006 htmlPart.setContent(hcontent); 2007 2008 var topPart = new ZmMimePart(); 2009 topPart.setContentType(ZmMimeTable.MULTI_ALT); 2010 topPart.children.add(textPart); 2011 topPart.children.add(htmlPart); 2012 2013 msg.setTopPart(topPart); 2014 } 2015 var subject = this._getInviteReplySubject(replyActionMode) + msg._origMsg.invite.getEventName(); 2016 if (subject != null) { 2017 msg.setSubject(subject); 2018 } 2019 var errorCallback = new AjxCallback(this, this._handleErrorInviteReply); 2020 msg.sendInviteReply(true, componentId, callback, errorCallback, instanceDate, accountName, ignoreNotify); 2021 }; 2022 2023 ZmMailListController.prototype._handleErrorInviteReply = 2024 function(result) { 2025 if (result.code == ZmCsfeException.MAIL_NO_SUCH_ITEM) { 2026 var dialog = appCtxt.getErrorDialog(); 2027 dialog.setMessage(ZmMsg.inviteOutOfDate); 2028 dialog.popup(null, true); 2029 return true; 2030 } 2031 }; 2032 2033 ZmMailListController.prototype._spamListener = 2034 function(ev) { 2035 var items = this._listView[this._currentViewId].getSelection(); 2036 var button = this.getCurrentToolbar().getButton(ZmOperation.SPAM); 2037 2038 this._doSpam(items, button.isMarkAsSpam); 2039 }; 2040 2041 ZmMailListController.prototype._detachListener = 2042 function(ev, callback) { 2043 var msg = this.getMsg(); 2044 if (msg) { 2045 if (msg._loaded) { 2046 ZmMailMsgView.detachMsgInNewWindow(msg, false, this); 2047 // always mark a msg read if it is displayed in its own window 2048 if (msg.isUnread && !appCtxt.getById(msg.folderId).isReadOnly()) { 2049 msg.list.markRead({items:[msg], value:true}); 2050 } 2051 } else { 2052 ZmMailMsgView.rfc822Callback(msg.id, null, this); 2053 } 2054 } 2055 if (callback) { callback.run(); } 2056 }; 2057 2058 ZmMailListController.prototype._printListener = 2059 function(ev) { 2060 var listView = this._listView[this._currentViewId]; 2061 var items = listView.getSelection(); 2062 items = AjxUtil.toArray(items); 2063 var ids = []; 2064 var showImages = false; 2065 for (var i = 0; i < items.length; i++) { 2066 var item = items[i]; 2067 // always extract out the msg ids from the conv 2068 if (item.toString() == "ZmConv") { 2069 // get msg ID in case of virtual conv. 2070 // item.msgIds.length is inconsistent, so checking if conv id is negative. 2071 if (appCtxt.isOffline && item.id.split(":")[1]<0) { 2072 ids.push(item.msgIds[0]); 2073 } else { 2074 ids.push("C:"+item.id); 2075 } 2076 if (item.isZmConv) { 2077 var msgList = item.getMsgList(); 2078 for(var j=0; j<msgList.length; j++) { 2079 if(msgList[j].showImages) { 2080 showImages = true; 2081 break; 2082 } 2083 } 2084 } 2085 } else { 2086 ids.push(item.id); 2087 if (item.showImages) { 2088 showImages = true; 2089 } 2090 } 2091 } 2092 var url = ("/h/printmessage?id=" + ids.join(",")) + "&tz=" + AjxTimezone.getServerId(AjxTimezone.DEFAULT); 2093 if (appCtxt.get(ZmSetting.DISPLAY_EXTERNAL_IMAGES) || showImages) { 2094 url = url+"&xim=1"; 2095 } 2096 if (appCtxt.isOffline) { 2097 var acctName = items[0].getAccount().name; 2098 url+="&acct=" + acctName ; 2099 } 2100 window.open(appContextPath+url, "_blank"); 2101 }; 2102 2103 ZmMailListController.prototype._editListener = 2104 function(isEditAsNew, ev) { 2105 this._doAction({ev:ev, action:ZmOperation.DRAFT, isEditAsNew:isEditAsNew}); 2106 }; 2107 2108 ZmMailListController.prototype._muteUnmuteConvListener = 2109 function(ev) { 2110 var status = this._getConvMuteStatus(); 2111 if (status.hasUnmuteConv) { 2112 this._muteConvListener(); 2113 } 2114 else { 2115 this._unmuteConvListener(); 2116 } 2117 }; 2118 2119 ZmMailListController.prototype._muteConvListener = 2120 function(ev) { 2121 var listView = this._listView[this._currentView]; 2122 var items = listView.getSelection(); 2123 items = AjxUtil.toArray(items); 2124 var markReadcallback = this._getMarkReadCallback(); 2125 var callback = new AjxCallback(this, this._handleMuteUnmuteConvResponse, [markReadcallback, ZmId.OP_MUTE_CONV]); 2126 this._doMarkMute(items, true, callback, true); 2127 }; 2128 2129 ZmMailListController.prototype._unmuteConvListener = 2130 function(ev) { 2131 var listView = this._listView[this._currentView]; 2132 var items = listView.getSelection(); 2133 items = AjxUtil.toArray(items); 2134 var convListView = this._mailListView || this._parentController._mailListView; 2135 //When a conv is unmuted it needs to be rearranged in the list as per its sorting order. convListCallback will handle it. 2136 var convListCallback = null; 2137 if(convListView && convListView.toString() == "ZmConvListView") { 2138 convListCallback = new AjxCallback(convListView, convListView.handleUnmuteConv, items); 2139 } 2140 var callback = new AjxCallback(this, this._handleMuteUnmuteConvResponse, [convListCallback, ZmId.OP_UNMUTE_CONV]); 2141 this._doMarkMute(items, false, callback, true); 2142 }; 2143 2144 ZmMailListController.prototype._handleMuteUnmuteConvResponse = 2145 function(callback, actionId, result) { 2146 if(callback != null) { 2147 callback.run(); 2148 } 2149 }; 2150 2151 ZmMailListController.prototype._checkMailListener = 2152 function() { 2153 if (appCtxt.isOffline) { 2154 var callback = new AjxCallback(this, this._handleSyncAll); 2155 appCtxt.accountList.syncAll(callback); 2156 } 2157 2158 var folder = this._getSearchFolder(); 2159 var isFeed = (folder && folder.isFeed()); 2160 if (isFeed) { 2161 folder.sync(); 2162 } else { 2163 var hasExternalAccounts = false; 2164 if (!appCtxt.isOffline) { 2165 var isEnabled = appCtxt.get(ZmSetting.POP_ACCOUNTS_ENABLED) || appCtxt.get(ZmSetting.IMAP_ACCOUNTS_ENABLED); 2166 if (folder && !isFeed && isEnabled) { 2167 var dataSources = folder.getDataSources(null, true); 2168 if (dataSources) { 2169 hasExternalAccounts = true; 2170 var dsCollection = AjxDispatcher.run("GetDataSourceCollection"); 2171 dsCollection.importMail(dataSources); 2172 } 2173 } 2174 } 2175 2176 if ((folder && folder.nId == ZmFolder.ID_INBOX) || !hasExternalAccounts) { 2177 appCtxt.getAppController().sendNoOp(); 2178 } 2179 } 2180 }; 2181 2182 ZmMailListController.prototype._handleSyncAll = 2183 function() { 2184 //doesn't do anything now after I removed the appCtxt.get(ZmSetting.GET_MAIL_ACTION) == ZmSetting.GETMAIL_ACTION_DEFAULT preference stuff 2185 }; 2186 2187 ZmMailListController.prototype.runRefresh = 2188 function() { 2189 this._checkMailListener(); 2190 }; 2191 2192 ZmMailListController.prototype._sendReceiveListener = 2193 function(ev) { 2194 var account = appCtxt.accountList.getAccount(ev.item.getData(ZmOperation.MENUITEM_ID)); 2195 if (account) { 2196 account.sync(); 2197 } 2198 }; 2199 2200 ZmMailListController.prototype._folderSearch = 2201 function(folderId) { 2202 appCtxt.getSearchController().search({query:"in:" + ZmFolder.QUERY_NAME[folderId]}); 2203 }; 2204 2205 // Miscellaneous 2206 2207 // Adds "By Conversation" and "By Message" to a view menu 2208 ZmMailListController.prototype._setupGroupByMenuItems = 2209 function(view, menu) { 2210 2211 for (var i = 0; i < ZmMailListController.GROUP_BY_VIEWS.length; i++) { 2212 var id = ZmMailListController.GROUP_BY_VIEWS[i]; 2213 var mi = menu.createMenuItem(id, {image: ZmMailListController.GROUP_BY_ICON[id], 2214 text: ZmMsg[ZmMailListController.GROUP_BY_MSG_KEY[id]], 2215 shortcut: ZmMailListController.GROUP_BY_SHORTCUT[id], 2216 style: DwtMenuItem.RADIO_STYLE}); 2217 mi.setData(ZmOperation.MENUITEM_ID, id); 2218 mi.addSelectionListener(this._listeners[ZmOperation.VIEW]); 2219 if (id == this.getDefaultViewType()) { 2220 mi.setChecked(true, true); 2221 } 2222 } 2223 }; 2224 2225 ZmMailListController.prototype._setReplyText = 2226 function(parent) { 2227 if (parent && appCtxt.get(ZmSetting.REPLY_MENU_ENABLED)) { 2228 var op = parent.getOp(ZmOperation.REPLY_MENU); 2229 if (op) { 2230 var menu = op.getMenu(); 2231 var replyOp = menu.getOp(ZmOperation.REPLY); 2232 replyOp.setText(ZmMsg.replySender); 2233 } 2234 } 2235 }; 2236 2237 ZmMailListController.prototype._resetOperations = 2238 function(parent, num) { 2239 2240 ZmListController.prototype._resetOperations.call(this, parent, num); 2241 2242 var isWebClientOffline = appCtxt.isWebClientOffline(); 2243 parent.enable(ZmOperation.PRINT, (num > 0) && !isWebClientOffline ); 2244 parent.enable(ZmOperation.SHOW_ORIG, !isWebClientOffline); 2245 2246 if (this.isSyncFailuresFolder()) { 2247 parent.enableAll(false); 2248 parent.enable([ZmOperation.NEW_MENU], true); 2249 parent.enable([ZmOperation.DELETE, ZmOperation.FORWARD], num > 0); 2250 return; 2251 } 2252 2253 var item; 2254 if (num == 1 && !this.isDraftsFolder()) { 2255 var sel = this._listView[this._currentViewId].getSelection(); 2256 if (sel && sel.length) { 2257 item = sel[0]; 2258 } 2259 } 2260 2261 // If one item is selected, use its folder; otherwise check if search was constrained to a folder 2262 var itemFolder = item && item.folderId && appCtxt.getById(item.folderId); 2263 var folder = itemFolder; 2264 if (!folder) { 2265 var folderId = this._getSearchFolderId(true); 2266 folder = folderId && appCtxt.getById(folderId); 2267 } 2268 2269 var isDrafts = (item && item.isDraft && (item.type != ZmId.ITEM_CONV || item.numMsgs == 1)) || this.isDraftsFolder(); 2270 var isFeed = (folder && folder.isFeed()); 2271 var isReadOnly = (folder && folder.isReadOnly()); 2272 var isOutboxFolder = this.isOutboxFolder(); 2273 parent.setItemVisible(ZmOperation.EDIT, (isDrafts || isOutboxFolder) && (!folder || !folder.isReadOnly())); 2274 parent.setItemVisible(ZmOperation.EDIT_AS_NEW, !(isDrafts || isOutboxFolder)); 2275 2276 parent.setItemVisible(ZmOperation.REDIRECT, !(isDrafts || isOutboxFolder)); 2277 parent.enable(ZmOperation.REDIRECT, !(isDrafts || isOutboxFolder || isWebClientOffline)); 2278 2279 parent.setItemVisible(ZmOperation.MARK_READ, !(isDrafts || isOutboxFolder)); 2280 parent.setItemVisible(ZmOperation.MARK_UNREAD, !(isDrafts || isOutboxFolder)); 2281 parent.setItemVisible(ZmOperation.FLAG, !(isDrafts || isOutboxFolder)); 2282 parent.setItemVisible(ZmOperation.UNFLAG, !(isDrafts || isOutboxFolder)); 2283 parent.setItemVisible(ZmOperation.SPAM, !(isDrafts || isOutboxFolder)); 2284 parent.setItemVisible(ZmOperation.DETACH, !(isDrafts || isOutboxFolder)); 2285 2286 parent.enable(ZmOperation.MOVE_MENU, !(isDrafts || isOutboxFolder) && num > 0); 2287 2288 parent.enable(ZmOperation.DETACH, (appCtxt.get(ZmSetting.DETACH_MAILVIEW_ENABLED) && !(isDrafts || isOutboxFolder || isWebClientOffline) && num == 1)); 2289 2290 /*if (parent.isZmActionMenu) { 2291 parent.setItemVisible(ZmOperation.QUICK_COMMANDS, !isDrafts && parent._hasQuickCommands); 2292 } else { 2293 parent.setItemVisible(ZmOperation.QUICK_COMMANDS, !isDrafts); 2294 } */ 2295 2296 parent.setItemVisible(ZmOperation.ADD_FILTER_RULE, !(isDrafts || isOutboxFolder)); 2297 parent.setItemVisible(ZmOperation.CREATE_APPT, !(isDrafts || isOutboxFolder)); 2298 parent.setItemVisible(ZmOperation.CREATE_TASK, !(isDrafts || isOutboxFolder)); 2299 parent.setItemVisible(ZmOperation.ACTIONS_MENU, !isOutboxFolder); 2300 2301 // bug fix #37154 - disable non-applicable buttons if rfc/822 message 2302 var isRfc822 = appCtxt.isChildWindow && window.newWindowParams && window.newWindowParams.isRfc822; 2303 if (isRfc822 || (isReadOnly && num > 0)) { 2304 parent.enable([ZmOperation.DELETE, ZmOperation.MOVE, ZmOperation.MOVE_MENU, ZmOperation.SPAM, ZmOperation.TAG_MENU], false); 2305 } else { 2306 parent.enable([ZmOperation.REPLY, ZmOperation.REPLY_ALL], (!(isDrafts || isOutboxFolder) && !isFeed && num == 1)); 2307 parent.enable([ZmOperation.VIEW_MENU], true); 2308 parent.enable([ZmOperation.FORWARD, ZmOperation.SPAM], (!(isDrafts || isOutboxFolder) && num > 0)); 2309 } 2310 2311 if (this._draftsActionMenu) { 2312 var editMenu = this._draftsActionMenu.getOp(ZmOperation.EDIT); 2313 if (editMenu) { 2314 // Enable|disable 'edit' context menu item based on selection count 2315 editMenu.setEnabled(num == 1 && (this.isDraftsFolder() || !isReadOnly)); 2316 } 2317 } 2318 2319 var search = appCtxt.getCurrentSearch(); 2320 if (appCtxt.multiAccounts && num > 1 && search && search.isMultiAccount()) { 2321 parent.enable(ZmOperation.TAG_MENU, false); 2322 } 2323 2324 if (appCtxt.isExternalAccount()) { 2325 parent.enable( 2326 [ 2327 ZmOperation.REPLY, 2328 ZmOperation.REPLY_ALL, 2329 ZmOperation.FORWARD, 2330 ZmOperation.EDIT_AS_NEW, 2331 ZmOperation.REDIRECT, 2332 ZmOperation.MARK_READ, 2333 ZmOperation.MARK_UNREAD, 2334 ZmOperation.SPAM, 2335 ZmOperation.MOVE, 2336 ZmOperation.MOVE_MENU, 2337 ZmOperation.DELETE, 2338 ZmOperation.DETACH, 2339 ZmOperation.ADD_FILTER_RULE, 2340 ZmOperation.CREATE_APPT, 2341 ZmOperation.SEARCH_TO, 2342 ZmOperation.SEARCH, 2343 ZmOperation.CREATE_TASK 2344 ], 2345 false 2346 ); 2347 parent.setItemVisible(ZmOperation.TAG_MENU, false); 2348 } 2349 2350 this._cleanupToolbar(parent); 2351 }; 2352 2353 ZmMailListController.prototype._showMailItem = 2354 function() { 2355 var avm = appCtxt.getAppViewMgr(); 2356 this._setup(this._currentViewId); 2357 var elements = this.getViewElements(this._currentViewId, this._view[this._currentViewId]); 2358 2359 var curView = avm.getCurrentViewId(); 2360 var tabId = ZmMailListController.viewToTab[curView] || Dwt.getNextId(); 2361 ZmMailListController.viewToTab[this._currentViewId] = tabId; 2362 var viewParams = { 2363 view: this._currentViewId, 2364 viewType: this._currentViewType, 2365 elements: elements, 2366 hide: this._elementsToHide, 2367 clear: appCtxt.isChildWindow, 2368 tabParams: this._getTabParams(tabId, this._tabCallback.bind(this)) 2369 }; 2370 var buttonText = (this._conv && this._conv.subject) ? this._conv.subject.substr(0, ZmAppViewMgr.TAB_BUTTON_MAX_TEXT) : (this._msg && this._msg.subject && this._msg.subject.substr(0, ZmAppViewMgr.TAB_BUTTON_MAX_TEXT)) || ZmMsgController.DEFAULT_TAB_TEXT; 2371 2372 this._setView(viewParams); 2373 avm.setTabTitle(this._currentViewId, buttonText); 2374 this._resetOperations(this._toolbar[this._currentViewId], 1); // enable all buttons 2375 this._resetNavToolBarButtons(); 2376 }; 2377 2378 2379 /** 2380 * if parent is a toolbar, it might have an actionsMenu. If it does, we can clean up the separators in that menu. 2381 * (to prevent multiple consecutive separators, etc) 2382 * @param parent 2383 */ 2384 ZmMailListController.prototype._cleanupToolbar = 2385 function(parent) { 2386 //cleanup the separators of the toolbar Actions menu 2387 if (!parent.getActionsMenu) { 2388 return; 2389 } 2390 var actionsMenu = parent.getActionsMenu(); 2391 if (!actionsMenu) { 2392 return; 2393 } 2394 actionsMenu.cleanupSeparators(); 2395 }; 2396 2397 2398 2399 // Enable mark read/unread as appropriate. 2400 ZmMailListController.prototype._enableFlags = 2401 function(menu) { 2402 if(appCtxt.isExternalAccount()) { 2403 menu.enable([ZmOperation.MARK_READ, ZmOperation.MARK_UNREAD, ZmOperation.FLAG, ZmOperation.UNFLAG], false); 2404 return; 2405 } 2406 var status = this._getReadStatus(); 2407 menu.enable(ZmOperation.MARK_READ, status.hasUnread); 2408 menu.enable(ZmOperation.MARK_UNREAD, status.hasRead); 2409 menu.enable(ZmOperation.FLAG, status.hasUnflagged); 2410 menu.enable(ZmOperation.UNFLAG, status.hasFlagged); 2411 2412 if (appCtxt.isWebClientOffline()) { 2413 menu.enable([ZmOperation.ADD_FILTER_RULE,ZmOperation.CREATE_APPT, ZmOperation.CREATE_TASK], false); 2414 } 2415 }; 2416 2417 // Enable mark read/unread as appropriate. 2418 ZmMailListController.prototype._enableMuteUnmute = 2419 function(menu) { 2420 menu.enable([ZmOperation.UNMUTE_CONV, ZmOperation.MUTE_CONV], false); 2421 if (appCtxt.isExternalAccount() || appCtxt.isChildWindow || this._app.getGroupMailBy() === ZmItem.MSG) { 2422 return; 2423 } 2424 var status = this._getConvMuteStatus(); 2425 if (status.hasMuteConv && status.hasUnmuteConv) { 2426 menu.enable(ZmOperation.UNMUTE_CONV, true); 2427 menu.enable(ZmOperation.MUTE_CONV, true); 2428 } 2429 else if (status.hasMuteConv) { 2430 menu.enable(ZmOperation.UNMUTE_CONV, true); 2431 } 2432 else { 2433 menu.enable(ZmOperation.MUTE_CONV, true); 2434 } 2435 }; 2436 2437 /** 2438 * This method is actually called by a pushed view's controller when a user 2439 * attempts to page conversations (from CV) or messages (from MV ala TV). 2440 * We want the underlying view (CLV or MLV) to update itself silently as it 2441 * feeds the next/prev conv/msg to its respective controller. 2442 * 2443 * @param {ZmItem} currentItem the current item 2444 * @param {Boolean} forward if <code>true</code>, get next item rather than previous 2445 * 2446 * @private 2447 */ 2448 ZmMailListController.prototype.pageItemSilently = 2449 function(currentItem, forward, msgController) { 2450 2451 var newItem = this._getNextItem(currentItem, forward); 2452 if (newItem) { 2453 if (msgController) { 2454 msgController.inactive = true; //make it inactive so it can be reused instead of creating a new one for each item paged. 2455 } 2456 var lv = this._listView[this._currentViewId]; 2457 lv.emulateDblClick(newItem); 2458 } 2459 }; 2460 2461 ZmMailListController.prototype._getNextItem = 2462 function(currentItem, forward) { 2463 2464 var list = this._list.getArray(); 2465 var len = list.length; 2466 for (var i = 0; i < len; i++) { 2467 if (currentItem == list[i]) { 2468 break; 2469 } 2470 } 2471 if (i == len) { return; } 2472 2473 var itemIdx = forward ? i + 1 : i - 1; 2474 2475 if (itemIdx >= len) { 2476 //we are looking for the next item after the current list, not yet loaded 2477 if (!this._list.hasMore()) { 2478 return; 2479 } 2480 this._paginate(this._currentViewId, true, itemIdx); 2481 return; 2482 } 2483 return list[itemIdx]; 2484 }; 2485 2486 /** 2487 * Selects and displays an item that has been loaded into a page that's 2488 * not visible (eg getting the next conv from within the last conv on a page). 2489 * 2490 * @param view [constant] current view 2491 * @param saveSelection [boolean] if true, maintain current selection 2492 * @param loadIndex [int] index of item to show 2493 * @param result [ZmCsfeResult] result of SOAP request 2494 * 2495 * @private 2496 */ 2497 ZmMailListController.prototype._handleResponsePaginate = 2498 function(view, saveSelection, loadIndex, offset, result) { 2499 ZmListController.prototype._handleResponsePaginate.apply(this, arguments); 2500 2501 var newItem = loadIndex ? this._list.getVector().get(loadIndex) : null; 2502 if (newItem) { 2503 this._listView[this._currentViewId].emulateDblClick(newItem); 2504 } 2505 }; 2506 2507 ZmMailListController.prototype._getMenuContext = 2508 function() { 2509 return this.getCurrentViewId(); 2510 }; 2511 2512 // Flag mail items(override ZmListController to add hook to zimletMgr 2513 ZmMailListController.prototype._doFlag = 2514 function(items, on) { 2515 ZmListController.prototype._doFlag.call(this, items, on); 2516 appCtxt.notifyZimlets("onMailFlagClick", [items, on]); 2517 }; 2518 2519 // Tag/untag items(override ZmListController to add hook to zimletMgr 2520 ZmMailListController.prototype._doTag = 2521 function(items, tag, doTag) { 2522 ZmListController.prototype._doTag.call(this, items, tag, doTag); 2523 appCtxt.notifyZimlets("onTagAction", [items, tag, doTag]); 2524 }; 2525 2526 2527 /** 2528 * Returns the next/previous/first/last unread item in the list, based on what's 2529 * currently selected. 2530 * 2531 * @param which [constant] DwtKeyMap constant for selecting next/previous/first/last 2532 * @param type [constant]* if present, only return this type of item 2533 * @param noBump [boolean]* if true, start with currently selected item 2534 * 2535 * @private 2536 */ 2537 ZmMailListController.prototype._getUnreadItem = 2538 function(which, type, noBump) { 2539 2540 var lv = this._listView[this._currentViewId]; 2541 var vec = lv.getList(true); 2542 var list = vec && vec.getArray(); 2543 var size = list && list.length; 2544 if (!size) { return; } 2545 2546 var start, index; 2547 if (which == DwtKeyMap.SELECT_FIRST) { 2548 index = 0; 2549 } else if (which == DwtKeyMap.SELECT_LAST) { 2550 index = list.length - 1; 2551 } else { 2552 var sel = lv.getSelection(); 2553 var start, index; 2554 if (sel && sel.length) { 2555 start = (which == DwtKeyMap.SELECT_NEXT) ? sel[sel.length - 1] : sel[0]; 2556 } else { 2557 start = (which == DwtKeyMap.SELECT_NEXT) ? list[0] : list[list.length - 1]; 2558 } 2559 if (start) { 2560 var startIndex = lv.getItemIndex(start, true); 2561 if (sel && sel.length && !noBump) { 2562 index = (which == DwtKeyMap.SELECT_NEXT) ? startIndex + 1 : startIndex - 1; 2563 } else { 2564 index = startIndex; 2565 } 2566 } 2567 } 2568 2569 var unreadItem = null; 2570 while ((index >= 0 && index < size) && !unreadItem) { 2571 var item = list[index]; 2572 if (item.isUnread && (!type || item.type == type)) { 2573 unreadItem = item; 2574 } else { 2575 index = (which == DwtKeyMap.SELECT_NEXT || which == DwtKeyMap.SELECT_FIRST) ? index + 1 : index - 1; 2576 } 2577 } 2578 2579 return unreadItem; 2580 }; 2581 2582 ZmMailListController.prototype._getNextItemToSelect = function() {}; 2583 2584 ZmMailListController.prototype.addTrustedAddr = 2585 function(value, callback, errorCallback) { 2586 var soapDoc = AjxSoapDoc.create("ModifyPrefsRequest", "urn:zimbraAccount"), 2587 node, 2588 i; 2589 2590 for(i=0; i<value.length;i++) { 2591 node = soapDoc.set("pref", AjxStringUtil.trim(value[i])); 2592 node.setAttribute("name", "zimbraPrefMailTrustedSenderList"); 2593 } 2594 2595 return appCtxt.getAppController().sendRequest({ 2596 soapDoc: soapDoc, 2597 asyncMode: true, 2598 callback: callback, 2599 errorCallback: errorCallback 2600 }); 2601 }; 2602 2603 /** 2604 * @private 2605 */ 2606 ZmMailListController.prototype._getActiveSearchFolderId = 2607 function() { 2608 var s = this._activeSearch && this._activeSearch.search; 2609 return s && s.folderId; 2610 }; 2611 2612 /** 2613 * @private 2614 */ 2615 ZmMailListController.prototype._getActiveSearchFolder = 2616 function() { 2617 var id = this._getActiveSearchFolderId(); 2618 return id && appCtxt.getById(id); 2619 }; 2620 2621 /* ZmMailListController.prototype._quickCommandMenuHandler = function(evt, batchCmd) { 2622 var selectedItems = this.getItems(); 2623 2624 ZmListController.prototype._quickCommandMenuHandler.call(this, evt); 2625 2626 if (!selectedItems || !selectedItems.length) {return;} 2627 2628 var menuItem = evt.dwtObj; 2629 var quickCommand = menuItem.getData(Dwt.KEY_OBJECT); 2630 if (!quickCommand) {return;} 2631 2632 var actions = quickCommand.actions; 2633 var len = actions.length; 2634 for (var i = 0; i < len; i++) { 2635 var action = actions[i]; 2636 if (!action.isActive) {continue;} 2637 var actionValue = action.value; 2638 if (action.type == ZmQuickCommandAction[ZmFilterRule.A_NAME_FLAG]) { 2639 if (actionValue == "read" || actionValue == "unread") { 2640 this._doMarkRead(selectedItems, (actionValue == "read")); 2641 } 2642 } 2643 } 2644 }; 2645 */ 2646 2647 /** 2648 * Deletes one or more items from the list. 2649 * 2650 * @param items [Array] list of items to delete 2651 * @param hardDelete [boolean]* if true, physically delete items 2652 * @param attrs [Object]* additional attrs for SOAP command 2653 * @param confirmDelete [Boolean] user already confirmed hard delete (see ZmBriefcaseController.prototype._doDelete and ZmBriefcaseController.prototype._doDelete2) 2654 * 2655 * @private 2656 */ 2657 ZmMailListController.prototype._doDelete = 2658 function(items, hardDelete, attrs, confirmDelete) { 2659 2660 var messages = AjxUtil.toArray(items); 2661 if (!messages.length) { return; } 2662 2663 // Check if need to warn the user about violating the keep retention policy. If a warning 2664 // dialog is displayed, the callback from that dialog allows the user to delete messages 2665 var warningIssued = this._doRetentionPolicyWarning(messages, 2666 ZmListController.prototype._doDelete, [hardDelete, attrs, false]); 2667 if (!warningIssued) { 2668 // No retention policy, or all the chosen messages fall outside the retention period. 2669 ZmListController.prototype._doDelete.call(this, messages, hardDelete, attrs, confirmDelete); 2670 } 2671 }; 2672 2673 ZmMailListController.prototype._doMove = 2674 function(items, destinationFolder, attrs, isShiftKey) { 2675 var messages = AjxUtil.toArray(items); 2676 if (!messages.length) { return; } 2677 2678 var warningIssued = false; 2679 2680 if (destinationFolder && (destinationFolder.nId == ZmFolder.ID_TRASH)) { 2681 // Check if need to warn the user about violating the keep retention policy. If a warning 2682 // dialog is displayed, the callback from that dialog allows the user to trash messages 2683 warningIssued = this._doRetentionPolicyWarning(messages, 2684 ZmListController.prototype._doMove, [destinationFolder, attrs, isShiftKey]); 2685 } 2686 if (!warningIssued) { 2687 // No retention policy, or all the chosen messages fall outside the retention period. 2688 ZmListController.prototype._doMove.call(this, items, destinationFolder, attrs, isShiftKey); 2689 } 2690 } 2691 2692 ZmMailListController.prototype._doRetentionPolicyWarning = 2693 function(messages, callbackFunc, args) { 2694 var numWithinRetention = 0; 2695 var folder; 2696 var keepPolicy; 2697 var now = new Date(); 2698 var validMessages = []; 2699 var policyStartMsec = {}; 2700 for (var i = 0; i < messages.length; i++) { 2701 var folderId = messages[i].folderId; 2702 if (!policyStartMsec[folderId]) { 2703 policyStartMsec[folderId] = -1; 2704 folder = appCtxt.getById(folderId); 2705 keepPolicy = (folder ? folder.getRetentionPolicy(ZmOrganizer.RETENTION_KEEP) : null); 2706 if (keepPolicy) { 2707 // Calculate the current start of this folder's keep (retention) period 2708 var keepLifetimeMsec = folder.getRetentionPolicyLifetimeMsec(keepPolicy); 2709 policyStartMsec[folderId] = now.getTime() - keepLifetimeMsec; 2710 } 2711 } 2712 if (policyStartMsec[folderId] > 0) { 2713 // Determine which messages are not affected by the retention policy (i.e. 2714 // their age exceeds that mandated by the policy) 2715 if (messages[i].date < policyStartMsec[folderId]) { 2716 validMessages.push(messages[i]); 2717 } 2718 } else { 2719 // The message's folder does not have a retention policy 2720 validMessages.push(messages[i]); 2721 } 2722 } 2723 2724 numWithinRetention = messages.length - validMessages.length; 2725 if (numWithinRetention > 0) { 2726 // Create the base warning text 2727 var warningMsg = ((numWithinRetention == 1) ? 2728 ZmMsg.retentionKeepWarning : 2729 AjxMessageFormat.format(ZmMsg.retentionKeepWarnings,[numWithinRetention.toString()])) + 2730 "<BR><BR>"; 2731 2732 if (validMessages.length == 0) { 2733 // All the chosen messages fall within the retention period 2734 this._showSimpleRetentionWarning(warningMsg, messages, callbackFunc, args); 2735 } else { 2736 // A mix of messages - some outside the retention period, some within. 2737 warningMsg += ZmMsg.retentionDeleteAllExplanation + "<BR><BR>" + 2738 ((validMessages.length == 1) ? 2739 ZmMsg.retentionDeleteValidExplanation : 2740 AjxMessageFormat.format(ZmMsg.retentionDeleteValidExplanations,[validMessages.length.toString()])); 2741 this._showRetentionWarningDialog(warningMsg, messages, validMessages, callbackFunc, args); 2742 } 2743 } 2744 2745 return numWithinRetention != 0; 2746 } 2747 2748 ZmMailListController.prototype._showSimpleRetentionWarning = 2749 function(warningMsg, messages, callbackFunc, args) { 2750 warningMsg += (messages.length == 1) ? ZmMsg.retentionDeleteOne : 2751 ZmMsg.retentionDeleteMultiple; 2752 // This assumes that the first parameter of the OK function is the messages 2753 // to be processed, followed by other arbitrary parameters 2754 var okArgs = [messages].concat(args); 2755 var callback = new AjxCallback(this, callbackFunc,okArgs); 2756 2757 var okCancelDialog = appCtxt.getOkCancelMsgDialog(); 2758 okCancelDialog.registerCallback(DwtDialog.OK_BUTTON, 2759 this._handleRetentionWarningOK, this, [okCancelDialog, callback]); 2760 okCancelDialog.setMessage(warningMsg, DwtMessageDialog.WARNING_STYLE); 2761 okCancelDialog.setVisible(true); 2762 okCancelDialog.popup(); 2763 } 2764 2765 2766 ZmMailListController.prototype._showRetentionWarningDialog = 2767 function(warningMsg, messages, validMessages, callbackFunc, args) { 2768 var retentionDialog = appCtxt.getRetentionWarningDialog(); 2769 retentionDialog.reset(); 2770 2771 var callback; 2772 // This assumes that the first parameter of the OK function is the messages 2773 // to be processed, followed by other arbitrary parameters 2774 var allArgs = [messages].concat(args); 2775 callback = new AjxCallback(this, callbackFunc, allArgs); 2776 retentionDialog.registerCallback(ZmRetentionWarningDialog.DELETE_ALL_BUTTON, 2777 this._handleRetentionWarningOK, this, [retentionDialog, callback]); 2778 2779 var oldArgs = [validMessages].concat(args); 2780 callback = new AjxCallback(this, callbackFunc, oldArgs); 2781 retentionDialog.registerCallback(ZmRetentionWarningDialog.DELETE_VALID_BUTTON, 2782 this._handleRetentionWarningOK, this, [retentionDialog, callback]); 2783 2784 retentionDialog.setMessage(warningMsg, DwtMessageDialog.WARNING_STYLE); 2785 retentionDialog.setVisible(true); 2786 retentionDialog.popup(); 2787 }; 2788 2789 ZmMailListController.prototype._handleRetentionWarningOK = 2790 function(dialog, callback) { 2791 dialog.popdown(); 2792 callback.run(); 2793 }; 2794 2795 // done here since operations may not be defined at parse time 2796 ZmMailListController.prototype._setStatics = function() { 2797 2798 if (!ZmMailListController.INVITE_REPLY_MAP) { 2799 2800 ZmMailListController.INVITE_REPLY_MAP = {}; 2801 ZmMailListController.INVITE_REPLY_MAP[ZmOperation.INVITE_REPLY_ACCEPT] = ZmOperation.REPLY_ACCEPT; 2802 ZmMailListController.INVITE_REPLY_MAP[ZmOperation.INVITE_REPLY_DECLINE] = ZmOperation.REPLY_DECLINE; 2803 ZmMailListController.INVITE_REPLY_MAP[ZmOperation.INVITE_REPLY_TENTATIVE] = ZmOperation.REPLY_TENTATIVE; 2804 2805 ZmMailListController.REPLY_ACTION_MAP = {}; 2806 ZmMailListController.REPLY_ACTION_MAP[ZmOperation.REPLY_ACCEPT_NOTIFY] = ZmOperation.REPLY_ACCEPT; 2807 ZmMailListController.REPLY_ACTION_MAP[ZmOperation.REPLY_ACCEPT_IGNORE] = ZmOperation.REPLY_ACCEPT; 2808 ZmMailListController.REPLY_ACTION_MAP[ZmOperation.REPLY_DECLINE_NOTIFY] = ZmOperation.REPLY_DECLINE; 2809 ZmMailListController.REPLY_ACTION_MAP[ZmOperation.REPLY_DECLINE_IGNORE] = ZmOperation.REPLY_DECLINE; 2810 ZmMailListController.REPLY_ACTION_MAP[ZmOperation.REPLY_TENTATIVE_NOTIFY] = ZmOperation.REPLY_TENTATIVE; 2811 ZmMailListController.REPLY_ACTION_MAP[ZmOperation.REPLY_TENTATIVE_IGNORE] = ZmOperation.REPLY_TENTATIVE; 2812 2813 // convert key mapping to operation 2814 ZmMailListController.ACTION_CODE_TO_OP = {}; 2815 ZmMailListController.ACTION_CODE_TO_OP[ZmKeyMap.REPLY] = ZmOperation.REPLY; 2816 ZmMailListController.ACTION_CODE_TO_OP[ZmKeyMap.REPLY_ALL] = ZmOperation.REPLY_ALL; 2817 ZmMailListController.ACTION_CODE_TO_OP[ZmKeyMap.FORWARD_INLINE] = ZmOperation.FORWARD_INLINE; 2818 ZmMailListController.ACTION_CODE_TO_OP[ZmKeyMap.FORWARD_ATT] = ZmOperation.FORWARD_ATT; 2819 } 2820 }; 2821 2822 // Mail can be grouped by msg or conv 2823 ZmMailListController.prototype.supportsGrouping = function() { 2824 return true; 2825 }; 2826