1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * @overview 26 * This file contains the mail application class. 27 */ 28 29 /** 30 * Creates and initializes the mail application. 31 * @constructor 32 * @class 33 * The mail app manages and displays mail messages. Messages may be grouped 34 * into conversations. New messages are created through a composer. 35 * 36 * @param {DwtControl} container the container 37 * @param {ZmController} parentController the parent window controller (set by the child window) 38 * 39 * @author Conrad Damon 40 * 41 * @extends ZmApp 42 */ 43 ZmMailApp = function(container, parentController) { 44 ZmApp.call(this, ZmApp.MAIL, container, parentController); 45 46 this._dataSourceCollection = {}; 47 this._identityCollection = {}; 48 this._signatureCollection = {}; 49 50 this.numEntries = 0; // offline, initial sync 51 this.globalMailCount = 0; // offline, new mail count 52 53 this._throttleStats = []; 54 this._addSettingsChangeListeners(); 55 AjxCore.addOnloadListener(this._checkVacationReplyEnabled.bind(this)); 56 }; 57 58 ZmMailApp.prototype = new ZmApp; 59 ZmMailApp.prototype.constructor = ZmMailApp; 60 61 ZmMailApp.prototype.isZmMailApp = true; 62 ZmMailApp.prototype.toString = function() { return "ZmMailApp"; }; 63 64 // Organizer and item-related constants 65 ZmEvent.S_CONV = ZmId.ITEM_CONV; 66 ZmEvent.S_MSG = ZmId.ITEM_MSG; 67 ZmEvent.S_ATT = ZmId.ITEM_ATT; 68 ZmEvent.S_FOLDER = ZmId.ORG_FOLDER; 69 ZmEvent.S_DATA_SOURCE = ZmId.ITEM_DATA_SOURCE; 70 ZmEvent.S_IDENTITY = "IDENTITY"; 71 ZmEvent.S_SIGNATURE = "SIGNATURE"; 72 ZmItem.CONV = ZmEvent.S_CONV; 73 ZmItem.MSG = ZmEvent.S_MSG; 74 ZmItem.ATT = ZmEvent.S_ATT; 75 ZmItem.DATA_SOURCE = ZmEvent.S_DATA_SOURCE; 76 ZmOrganizer.FOLDER = ZmEvent.S_FOLDER; 77 78 // App-related constants 79 /** 80 * Defines the "mail" application. 81 */ 82 ZmApp.MAIL = ZmId.APP_MAIL; 83 ZmApp.CLASS[ZmApp.MAIL] = "ZmMailApp"; 84 ZmApp.SETTING[ZmApp.MAIL] = ZmSetting.MAIL_ENABLED; 85 ZmApp.UPSELL_SETTING[ZmApp.MAIL] = ZmSetting.MAIL_UPSELL_ENABLED; 86 ZmApp.LOAD_SORT[ZmApp.MAIL] = 20; 87 ZmApp.QS_ARG[ZmApp.MAIL] = "mail"; 88 89 ZmMailApp.DEFAULT_AUTO_SAVE_DRAFT_INTERVAL = 15; 90 ZmMailApp.AUTO_SAVE_IDLE_TIME = 3; 91 ZmMailApp.DEFAULT_MAX_MESSAGE_SIZE = 250000; 92 93 ZmMailApp.POP_DOWNLOAD_SINCE_ALL = 0; 94 ZmMailApp.POP_DOWNLOAD_SINCE_NO_CHANGE = 1; 95 ZmMailApp.POP_DOWNLOAD_SINCE_FROM_NOW = 2; 96 97 ZmMailApp.POP_DELETE_OPTION_KEEP = "keep"; 98 ZmMailApp.POP_DELETE_OPTION_READ = "read"; 99 ZmMailApp.POP_DELETE_OPTION_TRASH = "trash"; 100 ZmMailApp.POP_DELETE_OPTION_HARD_DELETE = "delete"; 101 102 ZmMailApp.SEND_RECEIPT_NEVER = "never"; 103 ZmMailApp.SEND_RECEIPT_ALWAYS = "always"; 104 ZmMailApp.SEND_RECEIPT_PROMPT = "prompt"; 105 106 ZmMailApp.INC_MAP = {}; 107 ZmMailApp.INC_MAP[ZmSetting.INC_NONE] = [ZmSetting.INC_NONE, false, false]; 108 ZmMailApp.INC_MAP[ZmSetting.INC_ATTACH] = [ZmSetting.INC_ATTACH, false, false]; 109 ZmMailApp.INC_MAP[ZmSetting.INC_BODY] = [ZmSetting.INC_BODY, false, true]; 110 ZmMailApp.INC_MAP[ZmSetting.INC_BODY_ONLY] = [ZmSetting.INC_BODY, false, false]; 111 ZmMailApp.INC_MAP[ZmSetting.INC_BODY_PRE] = [ZmSetting.INC_BODY, true, false]; 112 ZmMailApp.INC_MAP[ZmSetting.INC_BODY_HDR] = [ZmSetting.INC_BODY, false, true]; 113 ZmMailApp.INC_MAP[ZmSetting.INC_BODY_PRE_HDR] = [ZmSetting.INC_BODY, true, true]; 114 ZmMailApp.INC_MAP[ZmSetting.INC_SMART] = [ZmSetting.INC_SMART, false, false]; 115 ZmMailApp.INC_MAP[ZmSetting.INC_SMART_PRE] = [ZmSetting.INC_SMART, true, false]; 116 ZmMailApp.INC_MAP[ZmSetting.INC_SMART_HDR] = [ZmSetting.INC_SMART, false, true]; 117 ZmMailApp.INC_MAP[ZmSetting.INC_SMART_PRE_HDR] = [ZmSetting.INC_SMART, true, true]; 118 119 ZmMailApp.INC_MAP_REV = {}; 120 121 AjxUtil.foreach(ZmMailApp.INC_MAP, function(v, i) { 122 var key = (i == ZmSetting.INC_NONE || i == ZmSetting.INC_ATTACH) ? 123 v[0] : v.join("|"); 124 ZmMailApp.INC_MAP_REV[key] = i; 125 }); 126 127 ZmMailApp.GROUP_MAIL_BY_ITEM = {}; 128 ZmMailApp.GROUP_MAIL_BY_ITEM[ZmSetting.GROUP_BY_CONV] = ZmItem.CONV; 129 ZmMailApp.GROUP_MAIL_BY_ITEM[ZmSetting.GROUP_BY_MESSAGE] = ZmItem.MSG; 130 131 // Construction 132 133 ZmMailApp.prototype._defineAPI = 134 function() { 135 AjxDispatcher.setPackageLoadFunction("MailCore", new AjxCallback(this, this._postLoadCore)); 136 AjxDispatcher.setPackageLoadFunction("Mail", new AjxCallback(this, this._postLoad, ZmOrganizer.FOLDER)); 137 AjxDispatcher.registerMethod("Compose", ["MailCore", "Mail"], new AjxCallback(this, this.compose)); 138 AjxDispatcher.registerMethod("GetComposeController", ["MailCore", "Mail"], new AjxCallback(this, this.getComposeController)); 139 AjxDispatcher.registerMethod("GetConvController", ["MailCore", "Mail"], new AjxCallback(this, this.getConvController)); 140 AjxDispatcher.registerMethod("GetConvListController", ["MailCore", "Mail"], new AjxCallback(this, this.getConvListController)); 141 AjxDispatcher.registerMethod("GetMsgController", ["MailCore", "Mail"], new AjxCallback(this, this.getMsgController)); 142 AjxDispatcher.registerMethod("GetTradController", ["MailCore", "Mail"], new AjxCallback(this, this.getTradController)); 143 AjxDispatcher.registerMethod("GetMailListController", "MailCore", new AjxCallback(this, this.getMailListController)); 144 AjxDispatcher.registerMethod("GetIdentityCollection", "MailCore", new AjxCallback(this, this.getIdentityCollection)); 145 AjxDispatcher.registerMethod("GetSignatureCollection", "MailCore", new AjxCallback(this, this.getSignatureCollection)); 146 AjxDispatcher.registerMethod("GetDataSourceCollection", "MailCore", new AjxCallback(this, this.getDataSourceCollection)); 147 AjxDispatcher.registerMethod("GetMailConfirmController", ["MailCore","Mail"], new AjxCallback(this, this.getConfirmController)); 148 }; 149 150 ZmMailApp.prototype._registerSettings = 151 function(settings) { 152 var settings = settings || appCtxt.getSettings(); 153 settings.registerSetting("ALLOW_ANY_FROM_ADDRESS", {name:"zimbraAllowAnyFromAddress", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 154 settings.registerSetting("AUTO_READ_RECEIPT_ENABLED", {name:"zimbraPrefMailRequestReadReceipts", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 155 settings.registerSetting("AUTO_SAVE_DRAFT_INTERVAL", {name:"zimbraPrefAutoSaveDraftInterval", type:ZmSetting.T_PREF, dataType:ZmSetting.D_LDAP_TIME, defaultValue:ZmMailApp.DEFAULT_AUTO_SAVE_DRAFT_INTERVAL, isGlobal:true}); 156 settings.registerSetting("COLLAPSE_IMAP_TREES", {type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 157 settings.registerSetting("COLOR_MESSAGES", {name:"zimbraPrefColorMessagesEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue: false, isGlobal:true}); 158 settings.registerSetting("COMPOSE_SAME_FORMAT", {name:"zimbraPrefForwardReplyInOriginalFormat", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 159 settings.registerSetting("CONVERSATIONS_ENABLED", {name:"zimbraFeatureConversationsEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 160 settings.registerSetting("CONVERSATION_ORDER", {name:"zimbraPrefConversationOrder", type:ZmSetting.T_PREF, defaultValue:ZmSearch.DATE_DESC, isImplicit:true}); 161 settings.registerSetting("CONVERSATION_PAGE_SIZE", {type:ZmSetting.T_PREF, dataType:ZmSetting.D_INT, defaultValue:250, isGlobal:true}); 162 settings.registerSetting("CONV_SHOW_CALENDAR", {name:"zimbraPrefConvShowCalendar", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isImplicit:true}); 163 settings.registerSetting("DEDUPE_MSG_TO_SELF", {name:"zimbraPrefDedupeMessagesSentToSelf", type:ZmSetting.T_PREF, defaultValue:ZmSetting.DEDUPE_NONE}); 164 settings.registerSetting("DEDUPE_MSG_ENABLED", {name:"zimbraPrefMessageIdDedupingEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 165 settings.registerSetting("DEFAULT_DISPLAY_NAME", {type:ZmSetting.T_PSEUDO, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 166 settings.registerSetting("DETACH_COMPOSE_ENABLED", {name:"zimbraFeatureComposeInNewWindowEnabled",type:ZmSetting.T_PREF,dataType:ZmSetting.D_BOOLEAN,defaultValue:true}); 167 settings.registerSetting("DETACH_MAILVIEW_ENABLED", {name:"zimbraFeatureOpenMailInNewWindowEnabled",type:ZmSetting.T_PREF,dataType:ZmSetting.D_BOOLEAN,defaultValue:true}); 168 settings.registerSetting("DISPLAY_EXTERNAL_IMAGES", {name:"zimbraPrefDisplayExternalImages", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 169 settings.registerSetting("END_DATE_ENABLED", {type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 170 settings.registerSetting("FILTERS_ENABLED", {name:"zimbraFeatureFiltersEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 171 settings.registerSetting("FILTERS_MAIL_FORWARDING_ENABLED", {name:"zimbraFeatureMailForwardingInFiltersEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 172 settings.registerSetting("FORWARD_INCLUDE_HEADERS", {type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, isGlobal:true}); 173 settings.registerSetting("FORWARD_INCLUDE_ORIG", {name:"zimbraPrefForwardIncludeOriginalText", type:ZmSetting.T_PREF, defaultValue:ZmSetting.INC_BODY, isGlobal:true}); 174 settings.registerSetting("FORWARD_INCLUDE_WHAT", {type:ZmSetting.T_PREF, defaultValue:ZmSetting.INC_BODY, isGlobal:true}); 175 settings.registerSetting("FORWARD_MENU_ENABLED", {type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 176 settings.registerSetting("FORWARD_USE_PREFIX", {type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 177 settings.registerSetting("GROUP_MAIL_BY", {name:"zimbraPrefGroupMailBy", type:ZmSetting.T_PREF, defaultValue:ZmSetting.GROUP_BY_MESSAGE, isImplicit:true, isGlobal:true}); 178 settings.registerSetting("HIGHLIGHT_OBJECTS", {name:"zimbraMailHighlightObjectsMaxSize", type:ZmSetting.T_COS, dataType:ZmSetting.D_INT, defaultValue:70}); 179 settings.registerSetting("HTML_SIGNATURE_ENABLED", {type:ZmSetting.T_PREF,dataType:ZmSetting.D_BOOLEAN,defaultValue:true}); 180 settings.registerSetting("IDENTITIES_ENABLED", {name:"zimbraFeatureIdentitiesEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 181 settings.registerSetting("INITIAL_SEARCH", {name:"zimbraPrefMailInitialSearch", type:ZmSetting.T_PREF, defaultValue:"in:inbox"}); 182 settings.registerSetting("INITIAL_SEARCH_ENABLED", {name:"zimbraFeatureInitialSearchPreferenceEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 183 settings.registerSetting("MAIL_ATTACH_VIEW_ENABLED", {type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 184 settings.registerSetting("MAIL_BLACKLIST", {type: ZmSetting.T_PREF, dataType: ZmSetting.D_LIST}); 185 settings.registerSetting("TRUSTED_ADDR_LIST", {name:"zimbraPrefMailTrustedSenderList", type: ZmSetting.T_COS, dataType: ZmSetting.D_LIST}); 186 settings.registerSetting("TRUSTED_ADDR_LIST_MAX_NUM_ENTRIES", {name:"zimbraMailTrustedSenderListMaxNumEntries", type: ZmSetting.T_COS, dataType: ZmSetting.D_INT, defaultValue:100}); 187 settings.registerSetting("MAIL_ACTIVITYSTREAM_FOLDER", {name:"zimbraMailActivityStreamFolder", type:ZmSetting.T_METADATA, dataType:ZmSetting.D_INT, isImplicit:true, section:ZmSetting.M_IMPLICIT}); 188 settings.registerSetting("MAIL_BLACKLIST_MAX_NUM_ENTRIES", {name:"zimbraMailBlacklistMaxNumEntries", type: ZmSetting.T_COS, dataType: ZmSetting.D_INT, defaultValue:100}); 189 settings.registerSetting("MAIL_FOLDER_COLORS_ENABLED", {name:"zimbraPrefFolderColorEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 190 settings.registerSetting("MAIL_FORWARDING_ADDRESS", {name:"zimbraPrefMailForwardingAddress", type:ZmSetting.T_PREF}); 191 settings.registerSetting("MAIL_FORWARDING_ENABLED", {name:"zimbraFeatureMailForwardingEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 192 settings.registerSetting("MAIL_MANDATORY_SPELLCHECK", {name:"zimbraPrefMandatorySpellCheckEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 193 settings.registerSetting("MAIL_FROM_ADDRESS", {name:"zimbraPrefFromAddress", type:ZmSetting.T_PREF, dataType:ZmSetting.D_LIST }); 194 settings.registerSetting("MAIL_FROM_ADDRESS_TYPE", {name:"zimbraPrefFromAddressType", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, defaultValue:"sendAs" }); 195 settings.registerSetting("MAIL_LIFETIME_GLOBAL", {name:"zimbraMailMessageLifetime", type:ZmSetting.T_COS, defaultValue:"0"}); // dataType: DURATION 196 settings.registerSetting("MAIL_LIFETIME_INBOX_READ", {name:"zimbraPrefInboxReadLifetime", type:ZmSetting.T_PREF, defaultValue:"0"}); // dataType: DURATION 197 settings.registerSetting("MAIL_LIFETIME_INBOX_UNREAD", {name:"zimbraPrefInboxUnreadLifetime", type:ZmSetting.T_PREF, defaultValue:"0"}); // dataType: DURATION 198 settings.registerSetting("MAIL_LIFETIME_JUNK", {name:"zimbraPrefJunkLifetime", type:ZmSetting.T_PREF, defaultValue:"0"}); // dataType: DURATION 199 settings.registerSetting("MAIL_LIFETIME_JUNK_GLOBAL", {name:"zimbraMailSpamLifetime", type:ZmSetting.T_COS, defaultValue:"0"}); // dataType: DURATION 200 settings.registerSetting("MAIL_LIFETIME_SENT", {name:"zimbraPrefSentLifetime", type:ZmSetting.T_PREF, defaultValue:"0"}); // dataType: DURATION 201 settings.registerSetting("MAIL_LIFETIME_TRASH", {name:"zimbraPrefTrashLifetime", type:ZmSetting.T_PREF, defaultValue:"0"}); // dataType: DURATION 202 settings.registerSetting("MAIL_LIFETIME_TRASH_GLOBAL", {name:"zimbraMailTrashLifetime", type:ZmSetting.T_COS, defaultValue:"0"}); // dataType: DURATION 203 settings.registerSetting("MAIL_LOCAL_DELIVERY_DISABLED", {name:"zimbraPrefMailLocalDeliveryDisabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 204 settings.registerSetting("MAIL_NOTIFY_ALL", {name:"zimbraPrefShowAllNewMailNotifications", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 205 settings.registerSetting("MAIL_NOTIFY_SOUNDS", {name:"zimbraPrefMailSoundsEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 206 settings.registerSetting("MAIL_NOTIFY_APP", {name:"zimbraPrefMailFlashIcon", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 207 settings.registerSetting("MAIL_NOTIFY_BROWSER", {name:"zimbraPrefMailFlashTitle", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 208 settings.registerSetting("MAIL_NOTIFY_TOASTER", {name:"zimbraPrefMailToasterEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 209 settings.registerSetting("MAIL_PRIORITY_ENABLED", {name:"zimbraFeatureMailPriorityEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 210 settings.registerSetting("MAIL_READ_RECEIPT_ENABLED", {name:"zimbraFeatureReadReceiptsEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 211 settings.registerSetting("MAIL_SEND_LATER_ENABLED", {name:"zimbraFeatureMailSendLaterEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 212 settings.registerSetting("MAIL_SEND_READ_RECEIPTS", {name:"zimbraPrefMailSendReadReceipts", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, defaultValue:"never"}); 213 settings.registerSetting("MAIL_WHITELIST", {type: ZmSetting.T_PREF, dataType: ZmSetting.D_LIST}); 214 settings.registerSetting("MAIL_WHITELIST_MAX_NUM_ENTRIES", {name:"zimbraMailWhitelistMaxNumEntries", type: ZmSetting.T_COS, dataType: ZmSetting.D_INT, defaultValue:100}); 215 settings.registerSetting("MARK_MSG_READ", {name:"zimbraPrefMarkMsgRead", type:ZmSetting.T_PREF, dataType:ZmSetting.D_INT, defaultValue:0, isGlobal:true}); 216 settings.registerSetting("MAX_MESSAGE_SIZE", {type:ZmSetting.T_PREF, defaultValue:ZmMailApp.DEFAULT_MAX_MESSAGE_SIZE}); 217 settings.registerSetting("NEW_WINDOW_COMPOSE", {name:"zimbraPrefComposeInNewWindow", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, isGlobal:true}); 218 settings.registerSetting("NOTIF_ADDRESS", {name:"zimbraPrefNewMailNotificationAddress", type:ZmSetting.T_PREF}); 219 settings.registerSetting("NOTIF_ENABLED", {name:"zimbraPrefNewMailNotificationEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 220 settings.registerSetting("NOTIF_FEATURE_ENABLED", {name:"zimbraFeatureNewMailNotificationEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 221 settings.registerSetting("OPEN_MAIL_IN_NEW_WIN", {name:"zimbraPrefOpenMailInNewWindow", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 222 settings.registerSetting("POP_ENABLED", {name:"zimbraPop3Enabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:!appCtxt.isOffline}); 223 settings.registerSetting("POP_DOWNLOAD_SINCE_VALUE", {type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, defaultValue:""}); 224 settings.registerSetting("POP_DOWNLOAD_SINCE", {name:"zimbraPrefPop3DownloadSince", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, defaultValue:""}); 225 settings.registerSetting("POP_DELETE_OPTION", {name:"zimbraPrefPop3DeleteOption", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, defaultValue:ZmMailApp.POP_DELETE_OPTION_HARD_DELETE}); 226 settings.registerSetting("POP_INCLUDE_SPAM", {name:"zimbraPrefPop3IncludeSpam", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 227 settings.registerSetting("READING_PANE_LOCATION", {name:"zimbraPrefReadingPaneLocation", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, defaultValue:ZmSetting.RP_BOTTOM, isImplicit:true, isGlobal:true}); 228 settings.registerSetting("READING_PANE_LOCATION_CV", {name:"zimbraPrefConvReadingPaneLocation", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, defaultValue:ZmSetting.RP_BOTTOM, isImplicit:true}); 229 settings.registerSetting("READING_PANE_SASH_HORIZONTAL", {name:"zimbraPrefReadingPaneSashHorizontal", type:ZmSetting.T_METADATA, dataType:ZmSetting.D_INT, isImplicit:true, section:ZmSetting.M_IMPLICIT}); 230 settings.registerSetting("READING_PANE_SASH_VERTICAL", {name:"zimbraPrefReadingPaneSashVertical", type:ZmSetting.T_METADATA, dataType:ZmSetting.D_INT, isImplicit:true, section:ZmSetting.M_IMPLICIT}); 231 settings.registerSetting("REPLY_INCLUDE_HEADERS", {type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, isGlobal:true}); 232 settings.registerSetting("REPLY_INCLUDE_ORIG", {name:"zimbraPrefReplyIncludeOriginalText", type:ZmSetting.T_PREF, defaultValue:ZmSetting.INC_BODY, isGlobal:true}); 233 settings.registerSetting("REPLY_INCLUDE_WHAT", {type:ZmSetting.T_PREF, defaultValue:ZmSetting.INC_BODY, isGlobal:true}); 234 settings.registerSetting("REPLY_MENU_ENABLED", {type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 235 settings.registerSetting("REPLY_PREFIX", {name:"zimbraPrefForwardReplyPrefixChar", type:ZmSetting.T_PREF, defaultValue:">", isGlobal:true}); 236 settings.registerSetting("REPLY_TO_ADDRESS", {name:"zimbraPrefReplyToAddress", type:ZmSetting.T_PREF, dataType:ZmSetting.D_LIST }); 237 settings.registerSetting("REPLY_TO_ENABLED", {name:"zimbraPrefReplyToEnabled", type:ZmSetting.T_PREF}); // XXX: Is this a list or single? 238 settings.registerSetting("REPLY_USE_PREFIX", {type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 239 settings.registerSetting("SAVE_DRAFT_ENABLED", {type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 240 settings.registerSetting("SAVE_TO_IMAP_SENT", {type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 241 settings.registerSetting("SAVE_TO_SENT", {name:"zimbraPrefSaveToSent", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, isGlobal:true}); 242 settings.registerSetting("SAVE_TO_SENT_DELEGATED_TARGET", {name: "zimbraPrefDelegatedSendSaveTarget", type: ZmSetting.T_PREF, defaultValue: "owner", isGlobal: true}); 243 settings.registerSetting("SELECT_AFTER_DELETE", {name:"zimbraPrefMailSelectAfterDelete", type:ZmSetting.T_PREF, defaultValue:ZmSetting.DELETE_SELECT_NEXT, isGlobal:true}); 244 settings.registerSetting("SENT_FOLDER_NAME", {name:"zimbraPrefSentMailFolder", type:ZmSetting.T_PREF, defaultValue:"sent"}); 245 settings.registerSetting("SHOW_BCC", {type:ZmSetting.T_PREF, defaultValue:false}); 246 settings.registerSetting("SHOW_FRAGMENTS", {name:"zimbraPrefShowFragments", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true}); 247 settings.registerSetting("SHOW_MAIL_CONFIRM", {name:"zimbraFeatureConfirmationPageEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 248 settings.registerSetting("SHOW_CHATS_FOLDER", {name:"zimbraPrefShowChatsFolderInMail", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 249 settings.registerSetting("SIGNATURE", {name:"zimbraPrefMailSignature", type:ZmSetting.T_PREF}); 250 settings.registerSetting("SIGNATURE_ENABLED", {name:"zimbraPrefMailSignatureEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 251 settings.registerSetting("SIGNATURE_STYLE", {name:"zimbraPrefMailSignatureStyle", type:ZmSetting.T_PREF, defaultValue:ZmSetting.SIG_OUTLOOK}); 252 settings.registerSetting("START_DATE_ENABLED", {type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 253 settings.registerSetting("TAB_IN_EDITOR", {name:"zimbraPrefTabInEditorEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 254 settings.registerSetting("USE_SEND_MSG_SHORTCUT", {name:"zimbraPrefUseSendMsgShortcut", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, isGlobal:true}); 255 settings.registerSetting("USER_FOLDERS_ENABLED", {type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 256 settings.registerSetting("VACATION_DURATION_ENABLED", {type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 257 settings.registerSetting("VACATION_DURATION_ALL_DAY", {type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 258 settings.registerSetting("VACATION_FROM", {name:"zimbraPrefOutOfOfficeFromDate", type:ZmSetting.T_PREF, defaultValue:""}); 259 settings.registerSetting("VACATION_FROM_TIME", {type:ZmSetting.T_PREF, defaultValue:""}); 260 settings.registerSetting("VACATION_MSG", {name:"zimbraPrefOutOfOfficeReply", type:ZmSetting.T_PREF, defaultValue:""}); 261 settings.registerSetting("VACATION_EXTERNAL_TYPE", {name:"zimbraPrefExternalSendersType", type:ZmSetting.T_PREF, defaultValue:"ALL"}); 262 settings.registerSetting("VACATION_EXTERNAL_SUPPRESS", {name:"zimbraPrefOutOfOfficeSuppressExternalReply", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 263 settings.registerSetting("VACATION_CALENDAR_TYPE", {name:"zimbraPrefOutOfOfficeFreeBusyStatus", type:ZmSetting.T_PREF, defaultValue:"OUTOFOFFICE"}); 264 settings.registerSetting("VACATION_CALENDAR_APPT_ID", {name:"zimbraPrefOutOfOfficeCalApptID", type:ZmSetting.T_METADATA, defaultValue:"-1", isImplicit:true, section:ZmSetting.M_IMPLICIT}); 265 settings.registerSetting("VACATION_CALENDAR_ENABLED", {type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 266 settings.registerSetting("VACATION_EXTERNAL_MSG", {name:"zimbraPrefOutOfOfficeExternalReply", type:ZmSetting.T_PREF, defaultValue:""}); 267 settings.registerSetting("VACATION_MSG_ENABLED", {name:"zimbraPrefOutOfOfficeReplyEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 268 settings.registerSetting("VACATION_EXTERNAL_MSG_ENABLED", {name:"zimbraPrefOutOfOfficeExternalReplyEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 269 settings.registerSetting("VACATION_MSG_REMIND_ON_LOGIN", {name:"zimbraPrefOutOfOfficeStatusAlertOnLogin", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true}); 270 settings.registerSetting("VACATION_MSG_FEATURE_ENABLED", {name:"zimbraFeatureOutOfOfficeReplyEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false}); 271 settings.registerSetting("VACATION_UNTIL", {name:"zimbraPrefOutOfOfficeUntilDate", type:ZmSetting.T_PREF, defaultValue:""}); 272 settings.registerSetting("VACATION_UNTIL_TIME", {type:ZmSetting.T_PREF, defaultValue:""}); 273 }; 274 275 ZmMailApp.prototype._registerPrefs = 276 function() { 277 var sections = { 278 MAIL: { 279 title: ZmMsg.mail, 280 icon: "MailApp", 281 templateId: "prefs.Pages#Mail", 282 priority: 10, 283 precondition: ZmSetting.MAIL_PREFERENCES_ENABLED, 284 prefs: [ 285 ZmSetting.AUTO_READ_RECEIPT_ENABLED, 286 ZmSetting.DEDUPE_MSG_TO_SELF, 287 ZmSetting.DEDUPE_MSG_ENABLED, 288 ZmSetting.DISPLAY_EXTERNAL_IMAGES, 289 ZmSetting.GET_MAIL_ACTION, 290 ZmSetting.INITIAL_SEARCH, 291 ZmSetting.MAIL_BLACKLIST, 292 ZmSetting.MAIL_FORWARDING_ADDRESS, 293 ZmSetting.MAIL_LIFETIME_INBOX_READ, 294 ZmSetting.MAIL_LIFETIME_INBOX_UNREAD, 295 ZmSetting.MAIL_LIFETIME_JUNK, 296 ZmSetting.MAIL_LIFETIME_SENT, 297 ZmSetting.MAIL_LIFETIME_TRASH, 298 ZmSetting.MAIL_LOCAL_DELIVERY_DISABLED, 299 ZmSetting.MAIL_NOTIFY_SOUNDS, 300 ZmSetting.MAIL_NOTIFY_ALL, 301 ZmSetting.MAIL_NOTIFY_APP, 302 ZmSetting.MAIL_NOTIFY_BROWSER, 303 ZmSetting.MAIL_NOTIFY_TOASTER, 304 ZmSetting.MAIL_WHITELIST, 305 ZmSetting.MAIL_SEND_READ_RECEIPTS, 306 ZmSetting.MARK_MSG_READ, 307 ZmSetting.NOTIF_ADDRESS, 308 ZmSetting.OFFLINE_NOTIFY_NEWMAIL_ON_INBOX, 309 ZmSetting.OPEN_MAIL_IN_NEW_WIN, 310 ZmSetting.PAGE_SIZE, 311 ZmSetting.POP_DOWNLOAD_SINCE_VALUE, 312 ZmSetting.POP_DOWNLOAD_SINCE, 313 ZmSetting.POP_DELETE_OPTION, 314 ZmSetting.POP_INCLUDE_SPAM, 315 ZmSetting.POLLING_INTERVAL, 316 ZmSetting.SELECT_AFTER_DELETE, 317 ZmSetting.SHOW_FRAGMENTS, 318 ZmSetting.COLOR_MESSAGES, 319 ZmSetting.START_DATE_ENABLED, 320 ZmSetting.VACATION_DURATION_ENABLED, 321 ZmSetting.VACATION_DURATION_ALL_DAY, 322 ZmSetting.VACATION_FROM, 323 ZmSetting.VACATION_FROM_TIME, 324 ZmSetting.VACATION_CALENDAR_ENABLED, 325 ZmSetting.VACATION_MSG_ENABLED, 326 ZmSetting.VACATION_MSG, 327 ZmSetting.VACATION_EXTERNAL_MSG_ENABLED, 328 ZmSetting.VACATION_EXTERNAL_MSG, 329 ZmSetting.VACATION_EXTERNAL_TYPE, 330 ZmSetting.VACATION_EXTERNAL_SUPPRESS, 331 ZmSetting.VACATION_CALENDAR_TYPE, 332 ZmSetting.VACATION_UNTIL, 333 ZmSetting.VACATION_UNTIL_TIME, 334 ZmSetting.VIEW_AS_HTML, 335 ZmSetting.COMPOSE_AS_FORMAT, 336 ZmSetting.COMPOSE_INIT_FONT_COLOR, 337 ZmSetting.COMPOSE_INIT_FONT_FAMILY, 338 ZmSetting.COMPOSE_INIT_FONT_SIZE, 339 ZmSetting.FORWARD_INCLUDE_WHAT, 340 ZmSetting.FORWARD_USE_PREFIX, 341 ZmSetting.FORWARD_INCLUDE_HEADERS, 342 ZmSetting.NEW_WINDOW_COMPOSE, 343 ZmSetting.AUTO_SAVE_DRAFT_INTERVAL, 344 ZmSetting.REPLY_INCLUDE_WHAT, 345 ZmSetting.REPLY_USE_PREFIX, 346 ZmSetting.REPLY_INCLUDE_HEADERS, 347 ZmSetting.REPLY_PREFIX, 348 ZmSetting.SAVE_TO_SENT, 349 ZmSetting.TAB_IN_EDITOR, 350 ZmSetting.USE_SEND_MSG_SHORTCUT, 351 ZmSetting.COMPOSE_SAME_FORMAT, 352 ZmSetting.MAIL_MANDATORY_SPELLCHECK 353 ], 354 manageDirty: true, 355 createView: function(parent, section, controller) { 356 AjxDispatcher.require("Alert"); 357 return new ZmMailPrefsPage(parent, section, controller); 358 } 359 } 360 }; 361 362 for (var id in sections) { 363 ZmPref.registerPrefSection(id, sections[id]); 364 } 365 366 ZmPref.registerPref("ACCOUNTS", { 367 displayContainer: ZmPref.TYPE_CUSTOM 368 }); 369 370 ZmPref.registerPref("AUTO_SAVE_DRAFT_INTERVAL", { 371 displayName: ZmMsg.autoSaveDrafts, 372 displayContainer: ZmPref.TYPE_CHECKBOX, 373 options: [0, ZmMailApp.DEFAULT_AUTO_SAVE_DRAFT_INTERVAL] 374 }); 375 376 ZmPref.registerPref("AUTO_READ_RECEIPT_ENABLED", { 377 displayName: ZmMsg.autoReadReceiptRequest, 378 displayContainer: ZmPref.TYPE_CHECKBOX 379 }); 380 381 ZmPref.registerPref("USE_SEND_MSG_SHORTCUT", { 382 displayFunc: this.formatKeySeq.bind(this, AjxMessageFormat.format(ZmMsg.useSendMsgShortcut,[ZmKeys["compose.Send.display"]])), 383 displayContainer: ZmPref.TYPE_CHECKBOX 384 }); 385 386 ZmPref.registerPref("DEDUPE_MSG_TO_SELF", { 387 displayName: ZmMsg.removeDupesToSelf, 388 displayContainer: ZmPref.TYPE_RADIO_GROUP, 389 displayOptions: [ZmMsg.dedupeNone, ZmMsg.dedupeSecondCopy, ZmMsg.dedupeAll], 390 options: [ZmSetting.DEDUPE_NONE, ZmSetting.DEDUPE_SECOND, ZmSetting.DEDUPE_ALL] 391 }); 392 393 ZmPref.registerPref("DEDUPE_MSG_ENABLED", { 394 displayName: ZmMsg.autoDeleteDedupeMsg, 395 displayContainer: ZmPref.TYPE_CHECKBOX 396 }); 397 398 ZmPref.registerPref("DISPLAY_EXTERNAL_IMAGES", { 399 displayName: ZmMsg.showExternalImages, 400 displayContainer: ZmPref.TYPE_CHECKBOX 401 }); 402 403 ZmPref.registerPref("END_DATE_ENABLED", { 404 displayName: ZmMsg.endOn, 405 displayContainer: ZmPref.TYPE_CHECKBOX, 406 precondition: ZmSetting.VACATION_MSG_FEATURE_ENABLED 407 }); 408 409 ZmPref.registerPref("INITIAL_SEARCH", { 410 displayName: ZmMsg.initialMailSearch, 411 displayContainer: ZmPref.TYPE_INPUT, 412 maxLength: ZmPref.MAX_LENGTH[ZmSetting.INITIAL_SEARCH], 413 errorMessage: AjxMessageFormat.format(ZmMsg.invalidInitialSearch, ZmPref.MAX_LENGTH[ZmSetting.INITIAL_SEARCH]), 414 precondition: ZmSetting.INITIAL_SEARCH_ENABLED 415 }); 416 417 ZmPref.registerPref("MAIL_BLACKLIST", { 418 displayContainer: ZmPref.TYPE_CUSTOM 419 }); 420 421 ZmPref.registerPref("TRUSTED_ADDR_LIST", { 422 displayContainer: ZmPref.TYPE_CUSTOM 423 }); 424 425 ZmPref.registerPref("MAIL_FORWARDING_ADDRESS", { 426 displayName: ZmMsg.mailForwardingAddress, 427 displayContainer: ZmPref.TYPE_INPUT, 428 validationFunction: ZmMailApp.validateForwardEmail, 429 errorMessage: ZmMsg.invalidEmail, 430 precondition: ZmSetting.MAIL_FORWARDING_ENABLED, 431 hint: ZmMsg.enterEmailAddress 432 }); 433 434 ZmPref.registerPref("MAIL_LIFETIME_INBOX_READ", { 435 displayContainer: ZmPref.TYPE_RADIO_GROUP, 436 orientation: ZmPref.ORIENT_HORIZONTAL, 437 displayOptions: [ ZmMsg.lifetimeDurationDays, ZmMsg.lifetimeDurationDays, 438 ZmMsg.lifetimeDurationDays, ZmMsg.lifetimeDurationDays, 439 ZmMsg.lifetimeDurationDays, ZmMsg.lifetimeDurationNever ], 440 options: [ 30, 45, 60, 90, 120, 0 ], 441 approximateFunction: ZmPref.approximateLifetimeInboxRead, 442 displayFunction: ZmPref.durationDay2Int, 443 valueFunction: ZmPref.int2DurationDay, 444 validationFunction: ZmPref.validateLifetime 445 }); 446 447 ZmPref.registerPref("MAIL_LIFETIME_INBOX_UNREAD", { 448 displayContainer: ZmPref.TYPE_RADIO_GROUP, 449 orientation: ZmPref.ORIENT_HORIZONTAL, 450 displayOptions: [ ZmMsg.lifetimeDurationDays, ZmMsg.lifetimeDurationDays, 451 ZmMsg.lifetimeDurationDays, ZmMsg.lifetimeDurationDays, 452 ZmMsg.lifetimeDurationDays, ZmMsg.lifetimeDurationNever ], 453 options: [ 30, 45, 60, 90, 120, 0 ], 454 approximateFunction: ZmPref.approximateLifetimeInboxUnread, 455 displayFunction: ZmPref.durationDay2Int, 456 valueFunction: ZmPref.int2DurationDay, 457 validationFunction: ZmPref.validateLifetime 458 }); 459 460 ZmPref.registerPref("MAIL_LIFETIME_JUNK", { 461 displayContainer: ZmPref.TYPE_RADIO_GROUP, 462 orientation: ZmPref.ORIENT_HORIZONTAL, 463 displayOptions: ZmMsg.lifetimeDurationDays, 464 options: [ 1, 3, 7, 30 ], 465 approximateFunction: ZmPref.approximateLifetimeJunk, 466 displayFunction: ZmPref.durationDay2Int, 467 valueFunction: ZmPref.int2DurationDay, 468 validationFunction: ZmPref.validateLifetimeJunk 469 }); 470 471 ZmPref.registerPref("MAIL_LIFETIME_SENT", { 472 displayContainer: ZmPref.TYPE_RADIO_GROUP, 473 orientation: ZmPref.ORIENT_HORIZONTAL, 474 displayOptions: [ ZmMsg.lifetimeDurationDays, ZmMsg.lifetimeDurationDays, 475 ZmMsg.lifetimeDurationDays, ZmMsg.lifetimeDurationDays, 476 ZmMsg.lifetimeDurationDays, ZmMsg.lifetimeDurationNever ], 477 options: [ 30, 45, 60, 90, 120, 0 ], 478 approximateFunction: ZmPref.approximateLifetimeSent, 479 displayFunction: ZmPref.durationDay2Int, 480 valueFunction: ZmPref.int2DurationDay, 481 validationFunction: ZmPref.validateLifetime 482 }); 483 484 ZmPref.registerPref("MAIL_LIFETIME_TRASH", { 485 displayContainer: ZmPref.TYPE_RADIO_GROUP, 486 orientation: ZmPref.ORIENT_HORIZONTAL, 487 displayOptions: ZmMsg.lifetimeDurationDays, 488 options: [ 1, 3, 7, 30 ], 489 approximateFunction: ZmPref.approximateLifetimeTrash, 490 displayFunction: ZmPref.durationDay2Int, 491 valueFunction: ZmPref.int2DurationDay, 492 validationFunction: ZmPref.validateLifetimeTrash 493 }); 494 495 ZmPref.registerPref("MAIL_LOCAL_DELIVERY_DISABLED", { 496 displayName: ZmMsg.mailDeliveryDisabled, 497 displayContainer: ZmPref.TYPE_CHECKBOX, 498 precondition: ZmSetting.MAIL_FORWARDING_ENABLED, 499 validationFunction: ZmMailApp.validateMailLocalDeliveryDisabled, 500 errorMessage: ZmMsg.errorMissingFwdAddr 501 }); 502 503 ZmPref.registerPref("MAIL_NOTIFY_ALL", { 504 displayName: ZmMsg.messageNotificationFoldersLabel, 505 displayContainer: ZmPref.TYPE_RADIO_GROUP, 506 orientation: ZmPref.ORIENT_VERTICAL, 507 displayOptions: [ ZmMsg.messageNotificationFoldersInbox, ZmMsg.messageNotificationFoldersAll ], 508 options: [ false, true ] 509 }); 510 511 ZmPref.registerPref("MAIL_NOTIFY_SOUNDS", { 512 displayName: ZmMsg.playSound, 513 displayContainer: ZmPref.TYPE_CHECKBOX 514 }); 515 516 ZmPref.registerPref("MAIL_NOTIFY_APP", { 517 displayName: ZmMsg.flashMailAppTab, 518 displayContainer: ZmPref.TYPE_CHECKBOX 519 }); 520 521 ZmPref.registerPref("MAIL_NOTIFY_BROWSER", { 522 displayName: ZmMsg.flashBrowser, 523 displayContainer: ZmPref.TYPE_CHECKBOX 524 }); 525 526 ZmPref.registerPref("MAIL_SEND_READ_RECEIPTS", { 527 displayContainer: ZmPref.TYPE_RADIO_GROUP, 528 displayOptions: [ ZmMsg.readReceiptNever, 529 ZmMsg.readReceiptAlways, 530 ZmMsg.readReceiptAsk 531 ], 532 options: [ ZmMailApp.SEND_RECEIPT_NEVER, 533 ZmMailApp.SEND_RECEIPT_ALWAYS, 534 ZmMailApp.SEND_RECEIPT_PROMPT 535 ], 536 precondition: ZmSetting.MAIL_READ_RECEIPT_ENABLED 537 }); 538 539 ZmPref.registerPref("MAIL_WHITELIST", { 540 displayContainer: ZmPref.TYPE_CUSTOM 541 }); 542 543 ZmPref.registerPref("NOTIF_ADDRESS", { 544 displayName: ZmMsg.mailNotifAddress, 545 displayContainer: ZmPref.TYPE_INPUT, 546 validationFunction: ZmPref.validateEmail, 547 errorMessage: ZmMsg.invalidEmail, 548 precondition: ZmSetting.NOTIF_FEATURE_ENABLED, 549 hint: ZmMsg.enterEmailAddress, 550 setFunction: ZmPref.setMailNotificationAddressValue 551 }); 552 553 ZmPref.registerPref("OPEN_MAIL_IN_NEW_WIN", { 554 displayName: ZmMsg.openMailNewWin, 555 displayContainer: ZmPref.TYPE_CHECKBOX, 556 precondition: ZmSetting.DETACH_MAILVIEW_ENABLED 557 }); 558 559 ZmPref.registerPref("POP_DOWNLOAD_SINCE_VALUE", { 560 displayContainer: ZmPref.TYPE_STATIC, 561 precondition: ZmSetting.POP_ENABLED 562 }); 563 ZmPref.registerPref("POP_DOWNLOAD_SINCE", { 564 displayContainer: ZmPref.TYPE_RADIO_GROUP, 565 displayOptions: [ ZmMsg.externalAccessPopDownloadAll, 566 "*** NOT SHOWN ***", 567 ZmMsg.externalAccessPopDownloadFromNow 568 ], 569 options: [ ZmMailApp.POP_DOWNLOAD_SINCE_ALL, 570 ZmMailApp.POP_DOWNLOAD_SINCE_NO_CHANGE, 571 ZmMailApp.POP_DOWNLOAD_SINCE_FROM_NOW 572 ], 573 displayFunction: ZmPref.downloadSinceDisplay, 574 valueFunction: ZmPref.downloadSinceValue, 575 precondition: ZmSetting.POP_ENABLED 576 }); 577 ZmPref.registerPref("POP_DELETE_OPTION", { 578 displayContainer: ZmPref.TYPE_RADIO_GROUP, 579 displayOptions: [ ZmMsg.popDeleteHardDelete, 580 ZmMsg.popDeleteTrash, 581 ZmMsg.popDeleteRead, 582 ZmMsg.popDeleteKeep 583 ], 584 options: [ ZmMailApp.POP_DELETE_OPTION_HARD_DELETE, 585 ZmMailApp.POP_DELETE_OPTION_TRASH, 586 ZmMailApp.POP_DELETE_OPTION_READ, 587 ZmMailApp.POP_DELETE_OPTION_KEEP 588 ], 589 precondition: ZmSetting.POP_ENABLED 590 }); 591 ZmPref.registerPref("POP_INCLUDE_SPAM", { 592 displayName: ZmMsg.popIncludeSpam, 593 displayContainer: ZmPref.TYPE_CHECKBOX 594 }); 595 ZmPref.registerPref("REPLY_TO_ADDRESS", { 596 displayName: ZmMsg.replyToAddress, 597 displayContainer: ZmPref.TYPE_INPUT, 598 validationFunction: ZmPref.validateEmail, 599 errorMessage: ZmMsg.invalidEmail 600 }); 601 602 ZmPref.registerPref("SELECT_AFTER_DELETE", { 603 displayContainer: ZmPref.TYPE_RADIO_GROUP, 604 orientation: ZmPref.ORIENT_VERTICAL, 605 displayOptions: [ZmMsg.selectNext, ZmMsg.selectPrevious, ZmMsg.selectAdapt], 606 options: [ZmSetting.DELETE_SELECT_NEXT, ZmSetting.DELETE_SELECT_PREV, ZmSetting.DELETE_SELECT_ADAPT] 607 }); 608 609 ZmPref.registerPref("SIGNATURE", { 610 displayName: ZmMsg.signature, 611 displayContainer: ZmPref.TYPE_TEXTAREA, 612 maxLength: ZmPref.MAX_LENGTH[ZmSetting.SIGNATURE], 613 errorMessage: AjxMessageFormat.format(ZmMsg.invalidSignature, ZmPref.MAX_LENGTH[ZmSetting.SIGNATURE]) 614 }); 615 616 ZmPref.registerPref("SIGNATURE_ENABLED", { 617 displayName: ZmMsg.signatureEnabled, 618 displayContainer: ZmPref.TYPE_CHECKBOX 619 }); 620 621 ZmPref.registerPref("SIGNATURE_STYLE", { 622 displayName: ZmMsg.signatureStyle, 623 displayContainer: ZmPref.TYPE_RADIO_GROUP, 624 orientation: ZmPref.ORIENT_HORIZONTAL, 625 displayOptions: [ZmMsg.aboveQuotedText, ZmMsg.atBottomOfMessage], 626 options: [ZmSetting.SIG_OUTLOOK, ZmSetting.SIG_INTERNET] 627 }); 628 629 ZmPref.registerPref("SIGNATURES", { 630 displayContainer: ZmPref.TYPE_CUSTOM, 631 initFunction: ZmPref.regenerateSignatureEditor 632 }); 633 634 ZmPref.registerPref("START_DATE_ENABLED", { 635 displayContainer: ZmPref.TYPE_CHECKBOX, 636 displayName: ZmMsg.startOn, 637 precondition: ZmSetting.VACATION_MSG_FEATURE_ENABLED 638 }); 639 640 ZmPref.registerPref("TAB_IN_EDITOR", { 641 displayName: ZmMsg.tabInEditor, 642 displayContainer: ZmPref.TYPE_CHECKBOX 643 }); 644 645 ZmPref.registerPref("VACATION_DURATION_ENABLED", { 646 displayContainer: ZmPref.TYPE_CHECKBOX, 647 displayName: ZmMsg.oooDurationLabel, 648 precondition: ZmSetting.VACATION_MSG_FEATURE_ENABLED 649 }); 650 651 ZmPref.registerPref("VACATION_DURATION_ALL_DAY", { 652 displayContainer: ZmPref.TYPE_CHECKBOX, 653 displayName: ZmMsg.oooAllDayDurationLabel, 654 precondition: ZmSetting.VACATION_MSG_FEATURE_ENABLED 655 }); 656 657 ZmPref.registerPref("VACATION_CALENDAR_ENABLED", { 658 displayContainer: ZmPref.TYPE_CHECKBOX, 659 displayName: ZmMsg.vacationCalLabel, 660 precondition: ZmSetting.VACATION_MSG_FEATURE_ENABLED 661 }); 662 663 ZmPref.registerPref("VACATION_FROM", { 664 displayName: ZmMsg.startDate, 665 displayContainer: ZmPref.TYPE_INPUT, 666 precondition: ZmSetting.VACATION_MSG_FEATURE_ENABLED, 667 displayFunction: AjxDateUtil.dateGMT2Local, 668 valueFunction: AjxDateUtil.dateLocal2GMT 669 }); 670 671 ZmPref.registerPref("VACATION_UNTIL", { 672 displayName: ZmMsg.endDate, 673 displayContainer: ZmPref.TYPE_INPUT, 674 precondition: ZmSetting.VACATION_MSG_FEATURE_ENABLED, 675 displayFunction: AjxDateUtil.dateGMT2Local, 676 valueFunction: AjxDateUtil.dateLocal2GMT 677 }); 678 679 ZmPref.registerPref("VACATION_MSG", { 680 displayName: ZmMsg.awayMessage, 681 displayContainer: ZmPref.TYPE_TEXTAREA, 682 maxLength: ZmPref.MAX_LENGTH[ZmSetting.AWAY_MESSAGE], 683 errorMessage: AjxMessageFormat.format(ZmMsg.invalidAwayMessage, ZmPref.MAX_LENGTH[ZmSetting.AWAY_MESSAGE]), 684 precondition: ZmSetting.VACATION_MSG_FEATURE_ENABLED, 685 validationFunction: ZmMailApp.validateVacationMsg 686 }); 687 688 ZmPref.registerPref("VACATION_MSG_ENABLED", { 689 displayName: ZmMsg.outOfOffice, 690 displayContainer: ZmPref.TYPE_RADIO_GROUP, 691 orientation: ZmPref.ORIENT_VERTICAL, 692 errorMessage: ZmMsg.missingAwayMessage, 693 displayOptions: [ZmMsg.noAutoReplyMessage, ZmMsg.autoReplyMessage], 694 options: [false, true], 695 inputId: ["VACATION_MSG_DISABLED", "VACATION_MSG_ENABLED"] 696 }); 697 698 ZmPref.registerPref("VACATION_EXTERNAL_TYPE", { // The inside content been left empty, as we just need to register this pref with settings. 699 }); // Depending upon the option the user has chosen in OOO vacation external select dropdown, on saving we add the relevant pref to the list that constructs the request, refer ZmPref.addOOOVacationExternalPrefToList 700 701 ZmPref.registerPref("VACATION_EXTERNAL_SUPPRESS", { 702 displayContainer: ZmPref.TYPE_SELECT, 703 displayOptions: [ZmMsg.vacationExternalAllStandard, ZmMsg.vacationExternalAllCustom, ZmMsg.vacationExternalAllExceptABCustom, ZmMsg.vacationExternalReplySuppress], 704 options: [false, false, false, true], 705 initFunction: ZmPref.initOOOVacationExternalSuppress, 706 setFunction: ZmPref.addOOOVacationExternalPrefOnSave, 707 changeFunction: ZmPref.handleOOOVacationExternalOptionChange, 708 validationFunction: ZmMailApp.validateExternalVacationMsg, 709 errorMessage: ZmMsg.missingAwayMessage 710 }); 711 712 ZmPref.registerPref("VACATION_CALENDAR_TYPE", { 713 displayName: ZmMsg.vacationExternalType, 714 displayContainer: ZmPref.TYPE_SELECT, 715 displayOptions: [ZmMsg.outOfOffice,ZmMsg.busy], 716 options: ["OUTOFOFFICE","BUSY"] 717 }); 718 719 ZmPref.registerPref("VACATION_EXTERNAL_MSG", { 720 displayName: ZmMsg.externalAwayMessage, 721 displayContainer: ZmPref.TYPE_TEXTAREA, 722 maxLength: ZmPref.MAX_LENGTH[ZmSetting.AWAY_MESSAGE], 723 precondition: ZmSetting.VACATION_MSG_FEATURE_ENABLED 724 }); 725 726 ZmPref.registerPref("VACATION_EXTERNAL_MSG_ENABLED", { // The content been left empty, as we just need to register this pref with settings. 727 }); // Depending upon the option the user has chosen in OOO external select dropdown, on saving we add the relevant pref to the list that constructs the request, refer ZmPref.addOOOVacationExternalPrefToList 728 729 AjxDispatcher.require("Alert"); 730 var notifyText = ZmDesktopAlert.getInstance().getDisplayText(); 731 ZmPref.registerPref("MAIL_NOTIFY_TOASTER", { 732 displayFunc: function() { return notifyText; }, 733 precondition: !!notifyText, 734 displayContainer: ZmPref.TYPE_CHECKBOX 735 }); 736 737 if (appCtxt.isOffline) { 738 ZmPref.registerPref("OFFLINE_NOTIFY_NEWMAIL_ON_INBOX", { 739 displayContainer: ZmPref.TYPE_RADIO_GROUP, 740 displayOptions: [ZmMsg.notifyNewMailOnInbox, ZmMsg.notifyNewMailOnAny], 741 options: [true, false] 742 }); 743 } 744 }; 745 746 ZmMailApp.prototype.formatKeySeq = function(keySeq) { 747 // Make sure the modifierKey list is created. This will create the modifierKeys and cache them, but not display them 748 new ZmShortcutList({cols:[]}); 749 return ZmShortcutList._formatDisplay(keySeq); 750 } 751 752 /** 753 * @private 754 */ 755 ZmMailApp.validateForwardEmail = 756 function(emailStr) { 757 if (!emailStr || emailStr == "") { 758 var section = ZmPref.getPrefSectionWithPref(ZmSetting.MAIL_FORWARDING_ADDRESS); 759 if (!section) { return false; } 760 var view = appCtxt.getApp(ZmApp.PREFERENCES).getPrefController().getPrefsView(); 761 var checkbox = view.getView(section.id).getFormObject(ZmSetting.MAIL_LOCAL_DELIVERY_DISABLED); 762 if (checkbox && checkbox.isSelected()) { 763 checkbox.setSelected(false); 764 } 765 } 766 return ZmPref.validateEmail(emailStr); 767 }; 768 769 /** 770 * @private 771 */ 772 ZmMailApp.validateMailLocalDeliveryDisabled = 773 function(checked) { 774 if (!checked) { return true; } 775 var section = ZmPref.getPrefSectionWithPref(ZmSetting.MAIL_FORWARDING_ADDRESS); 776 if (!section) { return false; } 777 var view = appCtxt.getApp(ZmApp.PREFERENCES).getPrefController().getPrefsView(); 778 var input = view.getView(section.id).getFormObject(ZmSetting.MAIL_FORWARDING_ADDRESS); 779 return (input != null && input.isValid()); 780 }; 781 782 /** 783 * Make sure the server won't be sending out a blank away msg for the user. Check for a 784 * combination of an empty away msg and a checked box for "send away message". Since a 785 * pref is validated only if it changes, we have to have validation functions for both 786 * prefs. 787 * 788 * @private 789 */ 790 ZmMailApp.validateVacationMsg = 791 function(awayMsg) { 792 if (awayMsg && (awayMsg.length > 0)) { return true; } 793 var section = ZmPref.getPrefSectionWithPref(ZmSetting.VACATION_MSG_ENABLED); 794 if (!section) { return false; } 795 var view = appCtxt.getApp(ZmApp.PREFERENCES).getPrefController().getPrefsView(); 796 var input = view.getView(section.id).getFormObject(ZmSetting.VACATION_MSG_ENABLED); 797 var isValid = (input && !(input.getSelectedValue() == "true")); 798 if (!isValid) 799 ZmPref.SETUP["VACATION_MSG"].errorMessage = ZmMsg.missingAwayMessage; 800 801 return isValid 802 }; 803 804 /** 805 * @private 806 */ 807 ZmMailApp.validateVacationMsgEnabled = 808 function(checked) { 809 if (!checked) { return true; } 810 var section = ZmPref.getPrefSectionWithPref(ZmSetting.VACATION_MSG); 811 if (!section) { return false; } 812 var view = appCtxt.getApp(ZmApp.PREFERENCES).getPrefController().getPrefsView(); 813 var input = view.getView(section.id).getFormObject(ZmSetting.VACATION_MSG); 814 if (!input) { return false; } 815 var awayMsg = input.getValue(); 816 return (awayMsg && (awayMsg.length > 0)); 817 }; 818 819 /** 820 * Make sure the server won't be sending out a blank away msg for the external user. 821 * We are ignoring this validation in two cases : 822 * a) when 'Do not send auto replies' radio button is selected 823 * b) in OOO vacation external sender type, first and last option is selected, as in this case the 824 * OOO external message container is not visible 825 * @private 826 */ 827 ZmMailApp.validateExternalVacationMsg = 828 function() { 829 var section = ZmPref.getPrefSectionWithPref(ZmSetting.VACATION_MSG_ENABLED); 830 if (!section) { return false; } 831 var view = appCtxt.getApp(ZmApp.PREFERENCES).getPrefController().getPrefsView().getView(section.id); 832 var cbox = view.getFormObject(ZmSetting.VACATION_MSG_ENABLED); 833 if (cbox && cbox.getSelectedValue()==="false"){ // 'Do not send auto replies' radio button is selected .. 834 return true; 835 } 836 var externalSelect = view.getFormObject(ZmSetting.VACATION_EXTERNAL_SUPPRESS); 837 var selectOptionValue = externalSelect.getText(); 838 if (selectOptionValue.indexOf(ZmMsg.vacationExternalAllStandard) >=0 || selectOptionValue.indexOf(ZmMsg.vacationExternalReplySuppress) >=0) { 839 return true; 840 } 841 var externalTxtArea = view.getFormObject(ZmSetting.VACATION_EXTERNAL_MSG); 842 var awayMsg = externalTxtArea.getValue(); 843 return (awayMsg && (awayMsg.length > 0)); 844 }; 845 846 /** 847 * @private 848 */ 849 ZmMailApp.validateExternalVacationMsgEnabled = 850 function(checked) { 851 if (!checked) { return true; } 852 var section = ZmPref.getPrefSectionWithPref(ZmSetting.VACATION_EXTERNAL_MSG); 853 if (!section) { return false; } 854 var view = appCtxt.getApp(ZmApp.PREFERENCES).getPrefController().getPrefsView(); 855 var input = view.getView(section.id).getFormObject(ZmSetting.VACATION_EXTERNAL_MSG); 856 if (!input) { return false; } 857 var awayMsg = input.getValue(); 858 return (awayMsg && (awayMsg.length > 0)); 859 }; 860 861 /** 862 * @private 863 */ 864 ZmMailApp.prototype._registerOperations = 865 function() { 866 ZmOperation.registerOp(ZmId.OP_ADD_FILTER_RULE, {textKey:"createFilter", image:"Plus"}, ZmSetting.FILTERS_ENABLED); 867 ZmOperation.registerOp(ZmId.OP_ADD_TO_FILTER_RULE, {textKey: "addToFilter", image: "MailRule"}, ZmSetting.FILTERS_ENABLED); 868 ZmOperation.registerOp(ZmId.OP_ADD_SIGNATURE, {textKey:"signature", image:"AddSignature", tooltipKey:"chooseSignature"}, ZmSetting.SIGNATURES_ENABLED); 869 ZmOperation.registerOp(ZmId.OP_CHECK_MAIL, {textKey:"checkMail", tooltipKey:"checkMailPrefDefault", image:"Refresh", textPrecedence:90, showImageInToolbar: true}); 870 ZmOperation.registerOp(ZmId.OP_CREATE_APPT, {textKey:"createAppt", image:"NewAppointment"}, ZmSetting.CALENDAR_ENABLED); 871 ZmOperation.registerOp(ZmId.OP_CREATE_TASK, {textKey:"createTask", image:"NewTask"}, ZmSetting.TASKS_ENABLED); 872 ZmOperation.registerOp(ZmId.OP_DELETE_CONV, {textKey:"delConv", image:"DeleteConversation"}, ZmSetting.CONVERSATIONS_ENABLED); 873 ZmOperation.registerOp(ZmId.OP_DELETE_MSG, {textKey:"delMsg", image:"DeleteMessage"}); 874 ZmOperation.registerOp(ZmId.OP_DELETE_MENU, {textKey:"del", image:"Delete", tooltipKey:"deleteTooltip"}); 875 ZmOperation.registerOp(ZmId.OP_DETACH_COMPOSE, {tooltipKey:"detachComposeTooltip", image:"OpenInNewWindow"}); 876 ZmOperation.registerOp(ZmId.OP_DRAFT, null, ZmSetting.SAVE_DRAFT_ENABLED); 877 ZmOperation.registerOp(ZmId.OP_EDIT_FILTER_RULE, {textKey:"filterEdit", image:"Edit"}, ZmSetting.FILTERS_ENABLED); 878 ZmOperation.registerOp(ZmId.OP_FORWARD, {textKey:"forward", tooltipKey:"forwardTooltip", image:"Forward", shortcut:ZmKeyMap.FORWARD, textPrecedence:46}); 879 ZmOperation.registerOp(ZmId.OP_FORWARD_ATT, {textKey:"forwardAtt", tooltipKey:"forwardAtt", image:"Forward"}); 880 ZmOperation.registerOp(ZmId.OP_FORWARD_CONV, {textKey:"forwardConv", tooltipKey:"forwardConv", image:"Forward"}); 881 ZmOperation.registerOp(ZmId.OP_FORWARD_INLINE, {textKey:"forwardInline", tooltipKey:"forwardTooltip", image:"Forward"}); 882 ZmOperation.registerOp(ZmId.OP_INC_ATTACHMENT, {textKey:"includeMenuAttachment"}); 883 ZmOperation.registerOp(ZmId.OP_INC_BODY, {textKey:"includeMenuBody"}); 884 ZmOperation.registerOp(ZmId.OP_INC_NONE, {textKey:"includeMenuNone"}); 885 ZmOperation.registerOp(ZmId.OP_INC_SMART, {textKey:"includeMenuSmart"}); 886 ZmOperation.registerOp(ZmId.OP_INCLUDE_HEADERS, {textKey:"includeHeaders"}); 887 ZmOperation.registerOp(ZmId.OP_KEEP_READING, {textKey:"keepReading", tooltipKey:"keepReadingTooltip", shortcut:ZmKeyMap.KEEP_READING}); 888 ZmOperation.registerOp(ZmId.OP_MARK_READ, {textKey:"markAsRead", image:"ReadMessage", shortcut:ZmKeyMap.MARK_READ}); 889 ZmOperation.registerOp(ZmId.OP_MARK_UNREAD, {textKey:"markAsUnread", image:"UnreadMessage", shortcut:ZmKeyMap.MARK_UNREAD}); 890 ZmOperation.registerOp(ZmId.OP_FLAG, {textKey:"flag", image:"FlagRed", shortcut:ZmKeyMap.FLAG}, ZmSetting.FLAGGING_ENABLED); 891 ZmOperation.registerOp(ZmId.OP_UNFLAG, {textKey:"unflag", image:"FlagDis", shortcut:ZmKeyMap.FLAG}, ZmSetting.FLAGGING_ENABLED); 892 ZmOperation.registerOp(ZmId.OP_MOVE_DOWN_FILTER_RULE, {textKey:"filterMoveDown", image:"DownArrow"}, ZmSetting.FILTERS_ENABLED); 893 ZmOperation.registerOp(ZmId.OP_MOVE_TO_BCC, {textKey:"moveToBcc"}); 894 ZmOperation.registerOp(ZmId.OP_MOVE_TO_CC, {textKey:"moveToCc"}); 895 ZmOperation.registerOp(ZmId.OP_MOVE_TO_TO, {textKey:"moveToTo"}); 896 ZmOperation.registerOp(ZmId.OP_MOVE_UP_FILTER_RULE, {textKey:"filterMoveUp", image:"UpArrow"}, ZmSetting.FILTERS_ENABLED); 897 ZmOperation.registerOp(ZmId.OP_NEW_MESSAGE, {textKey:"newEmail", tooltipKey:"newMessageTooltip", image:"NewMessage", shortcut:ZmKeyMap.NEW_MESSAGE}); 898 ZmOperation.registerOp(ZmId.OP_NEW_MESSAGE_WIN, {textKey:"newEmail", tooltipKey:"newMessageTooltip", image:"NewMessage", shortcut:ZmKeyMap.NEW_MESSAGE_WIN}); 899 ZmOperation.registerOp(ZmId.OP_PRIORITY_HIGH, {textKey:"priorityHigh", image:"PriorityHigh_list"}); 900 ZmOperation.registerOp(ZmId.OP_PRIORITY_LOW, {textKey:"priorityLow", image:"PriorityLow_list"}); 901 ZmOperation.registerOp(ZmId.OP_PRIORITY_NORMAL, {textKey:"priorityNormal", image:"PriorityNormal_list"}); 902 ZmOperation.registerOp(ZmId.OP_REMOVE_FILTER_RULE, {textKey:"filterRemove", image:"Delete"}, ZmSetting.FILTERS_ENABLED); 903 ZmOperation.registerOp(ZmId.OP_REDIRECT, {textKey:"mailRedirect", tooltipKey:"mailRedirectTooltip", image:"Redirect"}); 904 ZmOperation.registerOp(ZmId.OP_REPLY, {textKey:"reply", tooltipKey:"replyTooltip", image:"Reply", shortcut:ZmKeyMap.REPLY, textPrecedence:50}); 905 ZmOperation.registerOp(ZmId.OP_REPLY_ALL, {textKey:"replyAll", tooltipKey:"replyAllTooltip", image:"ReplyAll", shortcut:ZmKeyMap.REPLY_ALL, textPrecedence:48}); 906 ZmOperation.registerOp(ZmId.OP_REQUEST_READ_RECEIPT, {textKey:"requestReadReceipt", image:"ReadMessage"}); 907 ZmOperation.registerOp(ZmId.OP_RESET, {textKey:"reset", image:"Refresh", tooltipKey: "refreshFilters"}); 908 ZmOperation.registerOp(ZmId.OP_RUN_FILTER_RULE, {textKey:"filterRun", image:"SwitchFormat"}, [ ZmSetting.MAIL_ENABLED, ZmSetting.FILTERS_ENABLED ]); 909 ZmOperation.registerOp(ZmId.OP_SAVE_DRAFT, {textKey:"saveDraft", tooltipKey:"saveDraftTooltip", image:"DraftFolder", shortcut:ZmKeyMap.SAVE}, ZmSetting.SAVE_DRAFT_ENABLED); 910 ZmOperation.registerOp(ZmId.OP_SEND_MENU, {textKey:"send", tooltipKey:"sendTooltip", image:"Send"}, ZmSetting.SAVE_DRAFT_ENABLED); 911 ZmOperation.registerOp(ZmId.OP_SEND_LATER, {textKey:"sendLater", tooltipKey:"sendLaterTooltip", image:"SendLater"}, ZmSetting.SAVE_DRAFT_ENABLED); 912 ZmOperation.registerOp(ZmId.OP_SHOW_BCC, {textKey:"showBcc"}); 913 ZmOperation.registerOp(ZmId.OP_SHOW_CONV, {textKey:"showConv", image:"Conversation"}); 914 ZmOperation.registerOp(ZmId.OP_SHOW_ORIG, {textKey:"showOrig", image:"Message"}); 915 ZmOperation.registerOp(ZmId.OP_SPAM, {textKey:"junkLabel", tooltipKey:"junkTooltip", image:"JunkMail", shortcut:ZmKeyMap.SPAM, textPrecedence:70}, ZmSetting.SPAM_ENABLED); 916 ZmOperation.registerOp(ZmId.OP_USE_PREFIX, {textKey:"usePrefix"}); 917 }; 918 919 /** 920 * @private 921 */ 922 ZmMailApp.prototype._registerItems = 923 function() { 924 ZmItem.registerItem(ZmItem.CONV, 925 {app: ZmApp.MAIL, 926 nameKey: "conversation", 927 icon: "Conversation", 928 soapCmd: "ConvAction", 929 itemClass: "ZmConv", 930 node: "c", 931 organizer: ZmOrganizer.FOLDER, 932 dropTargets: [ZmOrganizer.FOLDER, ZmOrganizer.TAG, ZmOrganizer.ZIMLET], 933 searchType: "conversation", 934 resultsList: 935 AjxCallback.simpleClosure(function(search) { 936 AjxDispatcher.require("MailCore"); 937 return new ZmMailList(ZmItem.CONV, search); 938 }, this) 939 }); 940 941 ZmItem.registerItem(ZmItem.MSG, 942 {app: ZmApp.MAIL, 943 nameKey: "message", 944 icon: "Message", 945 soapCmd: "MsgAction", 946 itemClass: "ZmMailMsg", 947 node: "m", 948 organizer: ZmOrganizer.FOLDER, 949 dropTargets: [ZmOrganizer.FOLDER, ZmOrganizer.TAG, ZmOrganizer.ZIMLET], 950 searchType: "message", 951 resultsList: 952 AjxCallback.simpleClosure(function(search) { 953 AjxDispatcher.require("MailCore"); 954 return new ZmMailList(ZmItem.MSG, search); 955 }, this) 956 }); 957 958 ZmItem.registerItem(ZmItem.ATT, 959 {app: ZmApp.MAIL, 960 nameKey: "attachment", 961 icon: "Attachment", 962 itemClass: "ZmMimePart", 963 node: "mp", 964 resultsList: 965 AjxCallback.simpleClosure(function(search) { 966 return new ZmMailList(ZmItem.ATT, search); 967 }, this) 968 }); 969 }; 970 971 /** 972 * @private 973 */ 974 ZmMailApp.prototype._setupSearchToolbar = 975 function() { 976 if (appCtxt.get(ZmSetting.MAIL_ENABLED)) { 977 ZmSearchToolBar.addMenuItem(ZmId.SEARCH_MAIL, 978 {msgKey: "mail", 979 tooltipKey: "searchMail", 980 icon: "Message", 981 shareIcon: "SharedMailFolder", 982 id: ZmId.getMenuItemId(ZmId.SEARCH, ZmId.SEARCH_MAIL) 983 }); 984 } 985 }; 986 987 ZmMailApp.prototype._registerApp = 988 function() { 989 var newItemOps = {}; 990 newItemOps[ZmOperation.NEW_MESSAGE] = "message"; 991 992 var actionCodes = {}; 993 actionCodes[ZmKeyMap.NEW_MESSAGE] = ZmOperation.NEW_MESSAGE; 994 actionCodes[ZmKeyMap.NEW_MESSAGE_WIN] = ZmOperation.NEW_MESSAGE_WIN; 995 996 ZmApp.registerApp(ZmApp.MAIL, { 997 mainPkg: "MailCore", 998 nameKey: "mail", 999 icon: "MailApp", 1000 textPrecedence: 70, 1001 chooserTooltipKey: "goToMail", 1002 viewTooltipKey: "displayMailToolTip", 1003 defaultSearch: appCtxt.isChildWindow ? null : ZmId.SEARCH_MAIL, 1004 organizer: ZmOrganizer.FOLDER, 1005 overviewTrees: [ZmOrganizer.FOLDER, ZmOrganizer.SEARCH, ZmOrganizer.TAG], 1006 searchTypes: [ZmItem.MSG, ZmItem.CONV], 1007 newItemOps: newItemOps, 1008 actionCodes: actionCodes, 1009 gotoActionCode: ZmKeyMap.GOTO_MAIL, 1010 newActionCode: ZmKeyMap.NEW_MESSAGE, 1011 qsViews: ["compose", "msg"], 1012 chooserSort: 10, 1013 defaultSort: 10, 1014 upsellUrl: ZmSetting.MAIL_UPSELL_URL, 1015 //quickCommandType: ZmQuickCommand[ZmId.ITEM_MSG], 1016 searchResultsTab: true 1017 }); 1018 }; 1019 1020 // App API 1021 1022 ZmMailApp.prototype.startup = 1023 function(result) { 1024 }; 1025 1026 /** 1027 * Normalize the notifications that occur when a virtual conv gets promoted to a real conv. 1028 * For example, a virtual conv with ID -676 and one msg (ID 676) receives a second msg (ID 677) 1029 * and becomes a real conv with an ID of 678. The following notifications will arrive: 1030 * 1031 * deleted: -676 1032 * created: c {id:678, n:2} 1033 * m {id:677, cid:678} 1034 * modified: m {id:676, cid:678} 1035 * 1036 * Essentially, we want to handle this as: 1037 * 1038 * created: m {id:677, cid:678} 1039 * modified: c {id:-676, _newId: 678} 1040 * m {id:676, cid:678} 1041 * 1042 * @private 1043 */ 1044 ZmMailApp.prototype.preNotify = 1045 function(notify) { 1046 1047 if (!(notify.deleted && notify.created && notify.modified)) { return notify; } 1048 1049 // first, see if we are deleting any virtual convs (which have negative IDs) 1050 var virtConvDeleted = false; 1051 var deletedIds = notify.deleted.id && notify.deleted.id.split(","); 1052 var virtConv = {}; 1053 var newDeletedIds = []; 1054 if (deletedIds && deletedIds.length) { 1055 for (var i = 0; i < deletedIds.length; i++) { 1056 var id = deletedIds[i]; 1057 var nId = ZmOrganizer.normalizeId(id); 1058 if (nId < 0) { 1059 virtConv[nId] = true; 1060 virtConvDeleted = true; 1061 } else { 1062 newDeletedIds.push(id); 1063 } 1064 } 1065 } 1066 if (!virtConvDeleted) { 1067 return notify; 1068 } 1069 1070 // look for creates of convs that mean a virtual conv got promoted 1071 var gotNewConv = false; 1072 var createdMsgs = {}; 1073 var createdConvs = {}; 1074 for (var name in notify.created) { 1075 var list = notify.created[name]; 1076 if (list && list.length) { 1077 for (var i = 0; i < list.length; i++) { 1078 var create = list[i]; 1079 var id = create.id; 1080 var extra = (name == "m") ? "|cid=" + create.cid + "|l=" + create.l : "|n=" + create.n; 1081 AjxDebug.println(AjxDebug.NOTIFY, name + ": id=" + id + "|su='" + create.su + "'|f=" + create.f + "|d=" + create.d + extra); 1082 if (name == "m") { 1083 createdMsgs[id] = create; 1084 } else if (name == "c" && (create.n > 1)) { 1085 // this is *probably* a create for a real conv from a virtual conv 1086 createdConvs[id] = create; 1087 gotNewConv = true; 1088 } 1089 } 1090 } 1091 } 1092 if (!gotNewConv) { 1093 return notify; 1094 } 1095 1096 // last thing to confirm virt conv promotion is msg changing cid 1097 var msgMoved = false; 1098 var newToOldCid = {}; 1099 var movedMsgs = {}; 1100 var list = notify.modified.m; 1101 if (list && list.length) { 1102 for (var i = 0; i < list.length; i++) { 1103 var mod = list[i]; 1104 var id = mod.id; 1105 var nId = ZmOrganizer.normalizeId(id); 1106 var virtCid = nId * -1; 1107 if (virtConv[virtCid] && createdConvs[mod.cid]) { 1108 msgMoved = true; 1109 movedMsgs[id] = mod; 1110 newToOldCid[mod.cid] = appCtxt.multiAccounts ? ZmOrganizer.getSystemId(virtCid) : virtCid; 1111 createdConvs[mod.cid]._wasVirtConv = true; 1112 // go ahead and update the msg cid, since it's used in 1113 // notification processing for creates 1114 var msg = appCtxt.getById(id), 1115 folderId; 1116 if (msg) { 1117 msg.cid = mod.cid; 1118 folderId = msg.folderId; 1119 } 1120 createdConvs[mod.cid].m = [{ 1121 id: id, 1122 l: folderId 1123 }]; 1124 } 1125 } 1126 } 1127 if (!msgMoved) { 1128 return notify; 1129 } 1130 1131 // We're promoting a virtual conv. Normalize the notifications object, and 1132 // process a preliminary notif that will update the virtual conv's ID to its 1133 // new value. 1134 1135 // First, remove the virt conv from the list of deleted IDs 1136 if (newDeletedIds.length) { 1137 notify.deleted.id = newDeletedIds.join(","); 1138 } else { 1139 delete notify.deleted; 1140 } 1141 1142 // get rid of creates for virtual convs, since they aren't really creates 1143 var tmp = []; 1144 var list = notify.created.c; 1145 if (list && list.length) { 1146 for (var i = 0; i < list.length; i++) { 1147 var create = list[i]; 1148 var c = createdConvs[create.id]; 1149 if (!(c && c._wasVirtConv)) { 1150 tmp.push(create); 1151 } 1152 } 1153 } 1154 if (tmp && tmp.length) { 1155 notify.created.c = tmp; 1156 } else { 1157 delete notify.created.c; 1158 } 1159 1160 // if the second msg matched the current search, we'll want to use the conv 1161 // create node to create the conv later, so save it 1162 for (var id in createdMsgs) { 1163 var msgCreate = createdMsgs[id]; 1164 var convCreate = createdConvs[msgCreate.cid]; 1165 if (convCreate && convCreate._wasVirtConv) { 1166 msgCreate._convCreateNode = convCreate; 1167 } 1168 } 1169 1170 // create modified notifs for the virtual convs that have been promoted, using 1171 // the create notif for the conv as a base 1172 var newMods = []; 1173 for (var cid in newToOldCid) { 1174 var node = createdConvs[cid]; 1175 node.id = newToOldCid[cid]; 1176 node._newId = cid; 1177 newMods.push(node); 1178 } 1179 1180 // Go ahead and process these changes, which will change the ID of each promoted conv 1181 // from its virtual (negative) ID to its real (positive) one. That will replace the DOM 1182 // IDs of that conv's elements with ones that reflect the new conv ID. 1183 if (newMods.length) { 1184 var mods = {}; 1185 mods["c"] = newMods; 1186 appCtxt.getRequestMgr()._handleModifies(mods); 1187 } 1188 }; 1189 1190 /** 1191 * For mail creates, there is no authoritative list (mail lists are always the result 1192 * of a search), so we notify each ZmMailList that we know about. To make life easier, 1193 * we figure out which folder(s) a conv spans before we hand it off. 1194 * <p> 1195 * Since the offline client may receive hundreds of create notifications at a time, we 1196 * make sure a create notification is relevant before creating a mail item.</p> 1197 * 1198 * @param creates [hash] hash of create notifications 1199 * 1200 * @private 1201 */ 1202 ZmMailApp.prototype.createNotify = 1203 function(creates, force) { 1204 if (!creates["m"] && !creates["c"] && !creates["link"]) { return; } 1205 if (!force && !this._noDefer && this._deferNotifications("create", creates)) { 1206 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailApp: skipping/deferring notifications"); 1207 return; 1208 } 1209 1210 if (creates["link"]) { 1211 var list = creates["link"]; 1212 for (var i = 0; i < list.length; i++) { 1213 var create = list[i]; 1214 if (appCtxt.cacheGet(create.id)) { continue; } 1215 this._handleCreateLink(create, ZmOrganizer.FOLDER); 1216 } 1217 } 1218 1219 var controllers = this.getAllControllers(); 1220 1221 // Move currentController to the end of the list if it's not there already 1222 var currentController = this._getCurrentViewController(); 1223 if (currentController && controllers[controllers.length - 1] !== currentController) { 1224 AjxUtil.arrayRemove(controllers, currentController); 1225 controllers.push(currentController); 1226 } 1227 1228 // give each controller a chance to handle the creates 1229 for (var i = 0; i < controllers.length; i++) { 1230 var controller = controllers[i]; 1231 if (controller && controller.isZmDoublePaneController) { 1232 this._checkList(creates, controller.getList(), controller, i == controllers.length - 1); 1233 } 1234 } 1235 1236 this._handleAlerts(creates); 1237 }; 1238 1239 ZmMailApp.prototype._handleAlerts = 1240 function(creates) { 1241 var mailCreates = creates["m"] || []; 1242 if (mailCreates.length == 0) { return; } 1243 1244 AjxDispatcher.require("Alert"); 1245 1246 var activeAcct = appCtxt.getActiveAccount(); 1247 var didAppAlert, didSoundAlert, didBrowserAlert = false; 1248 1249 var toasterCount = 0; 1250 1251 for (var i = 0; i < mailCreates.length; i++) { 1252 var mc = mailCreates[i]; 1253 var parsed = (mc && mc.f && (mc.f.indexOf(ZmItem.FLAG_UNREAD) != -1)) 1254 ? ZmOrganizer.parseId(mc.l) : null; 1255 1256 // don't process alerts while account is undergoing initial sync 1257 var acct = parsed && parsed.account; 1258 if (!acct || (acct && acct.isOfflineInitialSync())) { continue; } 1259 1260 // offline: check whether to show new-mail notification icon 1261 // Skip spam/trash folders and the local account 1262 if (appCtxt.isOffline && parsed && !acct.isMain) { 1263 var doIt = (appCtxt.get(ZmSetting.OFFLINE_NOTIFY_NEWMAIL_ON_INBOX)) 1264 ? (parsed.id == ZmOrganizer.ID_INBOX) 1265 : (parsed.id != ZmOrganizer.ID_SPAM && parsed.id != ZmOrganizer.ID_TRASH); 1266 1267 if (doIt) { 1268 this.globalMailCount++; 1269 acct.inNewMailMode = true; 1270 var allContainers = appCtxt.getOverviewController()._overviewContainer; 1271 for (var j in allContainers) { 1272 allContainers[j].updateAccountInfo(acct, true, true); 1273 } 1274 } 1275 } 1276 1277 if (appCtxt.get(ZmSetting.MAIL_NOTIFY_ALL) || (parsed && parsed.id == ZmOrganizer.ID_INBOX)) { 1278 // for multi-account, highlite the non-active accordion item 1279 if (appCtxt.accountList.size() > 1) { 1280 ZmAccountAlert.get(acct).start(this); 1281 } 1282 1283 // alert mail app tab for the active account and set flag so we only do it *once* 1284 if (!didAppAlert && acct == activeAcct && 1285 appCtxt.get(ZmSetting.MAIL_NOTIFY_APP, null, acct)) 1286 { 1287 this.startAlert(); 1288 didAppAlert = true; 1289 } 1290 1291 // do audible alert for this account and set flag so we only do it *once* 1292 if (!didSoundAlert && appCtxt.get(ZmSetting.MAIL_NOTIFY_SOUNDS, null, acct)) { 1293 ZmSoundAlert.getInstance().start(); 1294 didSoundAlert = true; 1295 } 1296 1297 // do browser alert for this account and set flag so we only do it *once* 1298 if (!didBrowserAlert && appCtxt.get(ZmSetting.MAIL_NOTIFY_BROWSER, null, acct)) { 1299 ZmBrowserAlert.getInstance().start(ZmMsg.newMessage); 1300 didBrowserAlert = true; 1301 } 1302 1303 // generate toaster message if applicable 1304 if (appCtxt.get(ZmSetting.MAIL_NOTIFY_TOASTER, null, acct) && 1305 toasterCount < 5) 1306 { 1307 var msg = appCtxt.getById(mc.id) || ZmMailMsg.createFromDom(mc, {}); 1308 var text = (msg.subject) 1309 ? ([msg.subject, " - ", (msg.fragment || "")].join("")) 1310 : (msg.fragment || ""); 1311 1312 var from = msg.getAddress(AjxEmailAddress.FROM); 1313 var email = (from && from instanceof AjxEmailAddress) ? from.getName() || from.getAddress() : 1314 (from && typeof from == "string") ? from : ZmMsg.unknown; 1315 var title = (appCtxt.accountList.size() > 1) 1316 ? AjxMessageFormat.format(ZmMsg.newMailWithAccount, [email, acct.getDisplayName()]) 1317 : AjxMessageFormat.format(ZmMsg.newMail, email); 1318 ZmDesktopAlert.getInstance().start(title, text); 1319 toasterCount++; 1320 } 1321 } 1322 } 1323 }; 1324 1325 /** 1326 * We can only handle new mail notifications if: 1327 * - we are currently in a mail view 1328 * - the view is the result of a matchable search 1329 * 1330 * @param {Hash} creates the JSON create objects 1331 * @param {ZmMailList} list the mail list to notify 1332 * @param {ZmMailListController} controller the controller that owns list 1333 * @param {boolean} last if true, okay to mark creates as handled 1334 * 1335 * @private 1336 */ 1337 ZmMailApp.prototype._checkList = 1338 function(creates, list, controller, last) { 1339 1340 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailApp: handling mail creates for view " + controller.getCurrentViewId()); 1341 1342 if (!(list && list instanceof ZmMailList)) { 1343 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailApp: list is not a ZmMailList: " + list); 1344 return; 1345 } 1346 1347 var convs = {}; 1348 var msgs = {}; 1349 1350 var sortBy = list.search.sortBy; 1351 1352 var convResults = this._checkType(creates, ZmItem.CONV, convs, list, sortBy, null, last); 1353 var msgResults = this._checkType(creates, ZmItem.MSG, msgs, list, sortBy, convs, last); 1354 1355 if (convResults.gotMail || msgResults.gotMail) { 1356 list.notifyCreate(convs, msgs); 1357 } 1358 1359 // bug: 30546 1360 if (convResults.hasMore || msgResults.hasMore) { 1361 var controller = this._getCurrentViewController(); 1362 1363 if (controller) { 1364 controller.setHasMore(true); 1365 } 1366 } 1367 }; 1368 1369 ZmMailApp.prototype._getCurrentViewController = 1370 function() { 1371 var controller; 1372 var viewType = appCtxt.getCurrentViewType(); 1373 if (viewType == ZmId.VIEW_CONVLIST) { 1374 controller = this.getConvListController(); 1375 } else if (viewType == ZmId.VIEW_TRAD) { 1376 controller = this.getTradController(); 1377 } 1378 return controller; 1379 }; 1380 1381 /** 1382 * Handles the creates for the given type of mail item. 1383 * 1384 * @param {Array} creates a list of JSON create nodes 1385 * @param {constant} type the mail item type 1386 * @param {Hash} items a hash of created mail items 1387 * @param {ZmMailList} currList the list currently being displayed to user 1388 * @param {constant} sortBy the sort order 1389 * @param {Hash} convs the convs, so we can update folders from msgs 1390 * @param {boolean} last if true, okay to mark creates as handled 1391 * 1392 * @return {Hash} a hash with booleans gotItem and gotAlertMessage 1393 * 1394 * @private 1395 */ 1396 ZmMailApp.prototype._checkType = 1397 function(creates, type, items, currList, sortBy, convs, last) { 1398 1399 var result = { gotMail:false, hasMore:false }; 1400 var nodeName = ZmList.NODE[type]; 1401 var list = creates[nodeName]; 1402 if (!(list && list.length)) { return result; } 1403 1404 var throttle; 1405 if (appCtxt.isOffline) { 1406 throttle = (appCtxt.get(ZmSetting.OFFLINE_SHOW_ALL_MAILBOXES)) 1407 ? appCtxt.accountList.isInitialSyncing() 1408 : appCtxt.getActiveAccount().isOfflineInitialSync(); 1409 } 1410 if (throttle) { 1411 if (!this._maxEntries) { 1412 var mlv = this.getMailListController().getCurrentView().getMailListView(); 1413 this._maxEntries = mlv && mlv.calculateMaxEntries(); 1414 } 1415 if (this.numEntries > this._maxEntries) { 1416 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailApp: too many creates: num=" + this.numEntries + ", max=" + this._maxEntries); 1417 return result; 1418 } 1419 } 1420 1421 var INTERVAL_LENGTH = 10 * 1000; //10 seconds 1422 var INTERVAL_THRESHOLD = 20; //throttle more than 20 messages. 1423 for (var i = 0; i < list.length; i++) { 1424 var create = list[i]; 1425 1426 // generic throttling mechanism. Do it per folder. reset every 10 seconds. If more than 40 creates arrive in this interval, stop handling them. 1427 // This is used to throttle external accounts syncs but also good in general to prevent the client from hanging in case of a huge burst of updates. 1428 var folder = create.l || "conv"; //I bundle all conv creates together (since they don't provide folder) to make it simple. There are NOT a lot of conv creates at this stage. we mostly create them in ZmMailList.prototype.notifyCreate from messages. 1429 var now = new Date(); 1430 var data = this._throttleStats[folder]; 1431 if (!data || now.getTime() - data.intervalStart.getTime() > INTERVAL_LENGTH) { 1432 data = this._throttleStats[folder] = { 1433 intervalStart: now, 1434 count: 0 1435 } 1436 } 1437 data.count++; 1438 if (data.count > INTERVAL_THRESHOLD) { 1439 if (data.count == INTERVAL_THRESHOLD + 1) { 1440 DBG.println(AjxDebug.DBG1, "folder " + folder + " starting to throttle at " + now); 1441 } 1442 result.hasMore = true; 1443 continue; 1444 } 1445 1446 1447 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailApp: process create notification:"); 1448 var extra = (type == ZmItem.MSG) ? "|cid=" + create.cid + "|l=" + create.l : "|n=" + create.n; 1449 AjxDebug.println(AjxDebug.NOTIFY, type + ": id=" + create.id + "|su='" + create.su + "'|f=" + create.f + "|d=" + create.d + extra); 1450 if (create._handled) { 1451 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailApp: create already handled " + create.id); 1452 continue; 1453 } 1454 if (last) { 1455 create._handled = true; 1456 } 1457 1458 // new conv does not affect a list of msgs 1459 if (currList.type == ZmItem.MSG && type == ZmItem.CONV) { 1460 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailApp: msg list ignoring conv create"); 1461 continue; 1462 } 1463 1464 // perform stricter checking if we're in offline mode 1465 if (appCtxt.isOffline) { 1466 if ((ZmList.ITEM_TYPE[nodeName] != currList.type) && (currList.type != ZmItem.CONV)) { 1467 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailApp: type mismatch: " + ZmList.ITEM_TYPE[nodeName] + " / " + currList.type); 1468 continue; 1469 } 1470 } 1471 1472 // throttle influx of CREATE notifications during offline initial sync 1473 if (throttle && this.numEntries > this._maxEntries) { 1474 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailApp: throttling"); 1475 result.hasMore = true; 1476 break; 1477 } 1478 1479 DBG.println(AjxDebug.DBG1, "ZmMailApp: handling CREATE for node: " + nodeName); 1480 1481 var item = appCtxt.getById(create.id); 1482 if (!item) { 1483 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailApp: create " + type + " object " + create.id); 1484 var itemClass = eval(ZmList.ITEM_CLASS[type]); 1485 item = itemClass.createFromDom(create, {list: currList}); 1486 } 1487 else if (item.type == ZmItem.MSG) { 1488 // bug 47589: make sure conv knows its folders 1489 var conv = appCtxt.getById(item.cid); 1490 if (conv) { 1491 conv.folders[item.folderId] = true; 1492 } 1493 } 1494 items[item.id] = item; 1495 result.gotMail = true; 1496 } 1497 return result; 1498 }; 1499 1500 ZmMailApp.prototype.modifyNotify = 1501 function(modifies, force) { 1502 if (!modifies["m"] && !modifies["c"]) { return; } 1503 if (!force && !this._noDefer && this._deferNotifications("modify", modifies)) { return; } 1504 1505 this._batchNotify(modifies["m"]); 1506 this._batchNotify(modifies["c"]); 1507 }; 1508 1509 ZmMailApp.prototype.postNotify = 1510 function(notify) { 1511 var lv = this._checkReplenishListView; 1512 if (lv && !lv._isPageless) { 1513 lv._checkReplenish(); 1514 this._checkReplenishListView = null; 1515 } 1516 }; 1517 1518 ZmMailApp.prototype.refresh = 1519 function(refresh) { 1520 1521 var inbox = appCtxt.getById(ZmFolder.ID_INBOX); 1522 if (inbox) { 1523 this.setNewMailNotice(inbox); 1524 } 1525 1526 if (!appCtxt.inStartup) { 1527 this.resetOverview(this.getOverviewId()); 1528 1529 // mark all existing mail list views as stale 1530 var viewIds = [ZmId.VIEW_TRAD, ZmId.VIEW_CONVLIST, ZmId.VIEW_CONV]; 1531 var avm = appCtxt.getAppViewMgr(); 1532 for (var i = 0; i < viewIds.length; i++) { 1533 var views = avm.getViewsByType(viewIds[i]); 1534 for (var j = 0; j < views.length; j++) { 1535 var dpv = avm.getViewComponent(ZmAppViewMgr.C_APP_CONTENT, views[j].id); 1536 if (dpv && dpv.isZmDoublePaneView) { 1537 dpv.isStale = true; 1538 } 1539 } 1540 } 1541 // view is normally updated when user returns to it (from whatever view 1542 // results from the current request); if the request doesn't result in a 1543 // view change, use a timer to check if it still needs to be updated 1544 var curViewId = appCtxt.getCurrentViewId(); 1545 AjxTimedAction.scheduleAction(new AjxTimedAction(this, this._checkRefresh, [curViewId]), 1000); 1546 } 1547 }; 1548 1549 ZmMailApp.prototype._checkRefresh = 1550 function(lastViewId) { 1551 1552 // if the request that prompted the refresh didn't result in a view change 1553 // (eg NoOpRequest), rerun its underlying search 1554 if (appCtxt.getCurrentViewId() == lastViewId) { 1555 var curView = appCtxt.getCurrentView(); 1556 if (curView && curView.isStale && curView._staleHandler) { 1557 curView._staleHandler(); 1558 } 1559 } 1560 }; 1561 1562 ZmMailApp.prototype.handleOp = 1563 function(op, params) { 1564 var inNewWindow = false; 1565 var showLoadingPage = true; 1566 if ((op == ZmOperation.NEW_MESSAGE_WIN) || (op == ZmOperation.NEW_MESSAGE)) { 1567 if (!appCtxt.isWebClientOffline()) { 1568 inNewWindow = (op == ZmOperation.NEW_MESSAGE_WIN) ? true : this._inNewWindow(params && params.ev); 1569 showLoadingPage = false; // don't show "Loading ..." page since main window view doesn't change 1570 } 1571 var loadCallback = new AjxCallback(this, this.compose, {action: ZmOperation.NEW_MESSAGE, inNewWindow:inNewWindow}); 1572 AjxDispatcher.require(["ContactsCore", "Contacts"], false, loadCallback, null, showLoadingPage); 1573 } 1574 }; 1575 1576 // Public methods 1577 1578 ZmMailApp.prototype.getOverviewPanelContent = 1579 function() { 1580 var firstTime = !this._overviewPanelContent; 1581 1582 var overview = ZmApp.prototype.getOverviewPanelContent.apply(this, arguments); 1583 1584 // bug: 42455 - highlight folder now that overview exists 1585 if (firstTime) { 1586 appCtxt.getSearchController().updateOverview(); 1587 } 1588 1589 return overview; 1590 }; 1591 1592 ZmMailApp.prototype.getOverviewContainer = 1593 function() { 1594 var firstTime = !this._overviewContainer; 1595 1596 var container = ZmApp.prototype.getOverviewContainer.apply(this, arguments); 1597 1598 // bug: 42455 - highlight folder now that overview exists 1599 if (firstTime && !appCtxt.get(ZmSetting.OFFLINE_SHOW_ALL_MAILBOXES)) { 1600 appCtxt.getSearchController().updateOverview(); 1601 } 1602 1603 return container; 1604 }; 1605 1606 ZmMailApp.prototype.getNewButtonProps = 1607 function() { 1608 return { 1609 text: ZmMsg.newMessage, 1610 tooltip: ZmMsg.compose, 1611 icon: "NewMessage", 1612 iconDis: "NewMessageDis", 1613 defaultId: ZmOperation.NEW_MESSAGE, 1614 disabled: !this.containsWritableFolder() 1615 }; 1616 }; 1617 1618 ZmMailApp.prototype.launch = 1619 function(params, callback) { 1620 this._setLaunchTime(this.toString(), new Date()); 1621 1622 if (appCtxt.isExternalAccount()) { 1623 var loadCallback = this._handleLoadLaunch.bind(this, params, callback); 1624 AjxDispatcher.require(["MailCore", "Mail", "Startup2"], true, loadCallback, null, true); 1625 } 1626 else { 1627 this._handleLoadLaunch(params, callback); 1628 } 1629 }; 1630 1631 ZmMailApp.prototype._handleLoadLaunch = 1632 function(params, callback) { 1633 // set type for initial search 1634 this._groupBy = appCtxt.get(ZmSetting.GROUP_MAIL_BY); 1635 1636 var query; 1637 params = params || {}; 1638 1639 if (params.qsParams) { 1640 var view = params.qsParams.view, id = params.qsParams.id; 1641 if (view == "compose") { 1642 this._showComposeView(callback); 1643 return; 1644 } else if (id) { 1645 view = view || "msg"; 1646 if (view == "list") { 1647 query = ["item:", id].join(""); 1648 params.searchResponse = null; 1649 this._forceMsgView = true; 1650 } else if (view == "msg") { 1651 1652 var list = new ZmMailList(ZmItem.MSG); 1653 var msg = new ZmMailMsg(id, list, true); 1654 list.add(msg); 1655 1656 var msgParams = {getHtml: appCtxt.get(ZmSetting.VIEW_AS_HTML), 1657 markRead: (appCtxt.get(ZmSetting.MARK_MSG_READ) == ZmSetting.MARK_READ_NOW), 1658 callback: new AjxCallback(this, this._handleResponseMsgLoad, [msg, callback]), 1659 errorCallback: new AjxCallback(this, this._handleErrorMsgLoad, callback)}; 1660 msg.load(msgParams); 1661 return; 1662 } 1663 } 1664 } 1665 1666 this.mailSearch(query, callback, params.searchResponse); 1667 }; 1668 1669 ZmMailApp.prototype._handleErrorLaunch = 1670 function(params, ex) { 1671 if (ex.code == ZmCsfeException.MAIL_NO_SUCH_FOLDER || 1672 ex.code == ZmCsfeException.MAIL_NO_SUCH_TAG || 1673 ex.code == ZmCsfeException.MAIL_QUERY_PARSE_ERROR) 1674 { 1675 // reset the params so we default to searching the inbox which *will* work 1676 var newParams = {query:"in:inbox", callback:params.callback, errorCallback:null, types:params.types}; 1677 appCtxt.getSearchController().search(newParams); 1678 } 1679 }; 1680 1681 /** 1682 * If we can't show the given msg, just do regular mail launch and show initial search. Make sure to 1683 * run the callback so that the rest of the UI is drawn. 1684 * 1685 * @param callback 1686 * @param ex 1687 * 1688 * @private 1689 */ 1690 ZmMailApp.prototype._handleErrorMsgLoad = 1691 function(callback, ex) { 1692 this.mailSearch(); 1693 if (callback) { 1694 callback.run(); 1695 } 1696 this._notifyRendered(); 1697 return false; 1698 }; 1699 1700 ZmMailApp.prototype._handleResponseMsgLoad = 1701 function(msg, callback) { 1702 AjxDispatcher.require("Startup2"); 1703 var msgCtlr = AjxDispatcher.run("GetMsgController"); 1704 if (msgCtlr) { 1705 msgCtlr.show(msg, null, null, null, true); // Show the message without pagination buttons 1706 if (callback) { 1707 callback.run(); 1708 } 1709 this._notifyRendered(); 1710 1711 appCtxt.notifyZimlets('onMsgView', [msg, null, appCtxt.getCurrentView()], {waitUntilLoaded:true}); 1712 } 1713 }; 1714 1715 /** 1716 * Performs a mail search. 1717 * 1718 * @param {String} query the query 1719 * @param {AjxCallback} callback the callback 1720 * @param {Object} response the response 1721 * @param {constant} type the type 1722 */ 1723 ZmMailApp.prototype.mailSearch = 1724 function(query, callback, response, type) { 1725 var account = appCtxt.isOffline && appCtxt.inStartup && appCtxt.accountList.defaultAccount; 1726 if (account) { 1727 appCtxt.accountList.setActiveAccount(account); 1728 } 1729 1730 var sc = appCtxt.getSearchController(); 1731 var queryHint, noUpdateOverview; 1732 if (appCtxt.get(ZmSetting.OFFLINE_SHOW_ALL_MAILBOXES) && 1733 appCtxt.accountList.size() > 2) 1734 { 1735 query = null; 1736 queryHint = appCtxt.accountList.generateQuery(ZmOrganizer.ID_INBOX); 1737 noUpdateOverview = true; 1738 sc.searchAllAccounts = true; 1739 } 1740 else if(appCtxt.isExternalAccount()) { 1741 query = "inid:" + this.getDefaultFolderId(); 1742 } else if (appCtxt.isWebClientOffline()) { 1743 query = query || "in:inbox"; 1744 } else { 1745 query = query || appCtxt.get(ZmSetting.INITIAL_SEARCH, null, account); 1746 } 1747 1748 var types = new AjxVector(); 1749 types.add(type || this.getGroupMailBy()); 1750 var sortBy = AjxUtil.get(response, "Body", "SearchResponse", "sortBy") || ZmSearch.DATE_DESC; 1751 1752 var params = { 1753 searchFor: ZmId.SEARCH_MAIL, 1754 query: query, 1755 queryHint: queryHint, 1756 types: types, 1757 limit: this.getLimit(), 1758 getHtml: appCtxt.get(ZmSetting.VIEW_AS_HTML, null, account), 1759 noUpdateOverview: noUpdateOverview, 1760 offlineCache: true, 1761 accountName: (account && account.name), 1762 callback: callback, 1763 response: response, 1764 sortBy: sortBy 1765 }; 1766 params.errorCallback = new AjxCallback(this, this._handleErrorLaunch, params); 1767 sc.search(params); 1768 }; 1769 1770 /** 1771 * Shows the search results. 1772 * 1773 * @param {Object} results the results 1774 * @param {AjxCallback} callback the callback 1775 * @param {ZmSearchResultsController} searchResultsController owning controller 1776 */ 1777 ZmMailApp.prototype.showSearchResults = 1778 function(results, callback, searchResultsController) { 1779 var loadCallback = this._handleLoadShowSearchResults.bind(this, results, callback, searchResultsController); 1780 AjxDispatcher.require("MailCore", false, loadCallback, null, true); 1781 }; 1782 1783 ZmMailApp.prototype._handleLoadShowSearchResults = 1784 function(results, callback, searchResultsController) { 1785 1786 var sessionId = searchResultsController ? searchResultsController.getCurrentViewId() : ZmApp.MAIN_SESSION; 1787 var controller = ((results.type == ZmItem.MSG) || !appCtxt.get(ZmSetting.CONVERSATIONS_ENABLED)) ? this.getTradController(sessionId, searchResultsController) : 1788 this.getConvListController(sessionId, searchResultsController); 1789 controller.show(results); 1790 this._setLoadedTime(this.toString(), new Date()); 1791 1792 if (this._forceMsgView) { 1793 controller.selectFirstItem(); 1794 this._forceMsgView = false; 1795 } 1796 1797 if (callback) { 1798 callback.run(controller); 1799 } 1800 this._notifyRendered(); 1801 1802 // update the title to reflect the new search results 1803 appCtxt.getAppViewMgr().updateTitle(); 1804 }; 1805 1806 ZmMailApp.prototype._parseComposeUrl = 1807 function(urlQueryStr){ 1808 1809 urlQueryStr = urlQueryStr || ''; 1810 urlQueryStr.replace(/^mailto:/i, ""); 1811 1812 //Decode the whole query string. Components will be decoded as well, but that's okay since it should do no harm and the query string may have been double-encoded as well (once by user to trick crawlers, then again by the browser in constructing the mailto URL). 1813 urlQueryStr = AjxStringUtil.urlComponentDecode(urlQueryStr); 1814 1815 var match = urlQueryStr.match(/\bto=([^&]+)/i); 1816 var to = match ? AjxStringUtil.urlComponentDecode(match[1].replace(/\+/g, " ")) : null; 1817 to = to && AjxEmailAddress.isValid(to) ? AjxStringUtil.urlComponentDecode(to) : AjxStringUtil.htmlEncode(to); 1818 1819 match = urlQueryStr.match(/\bsubject=([^&]+)/i); 1820 var subject = match ? (AjxStringUtil.urlComponentDecode(match[1]).replace(/\+/g, " ")) : null; 1821 1822 match = urlQueryStr.match(/\bcc=([^&]+)/i); 1823 var cc = match ? AjxStringUtil.urlComponentDecode(match[1].replace(/\+/g, " ")) : null; 1824 cc = cc && AjxEmailAddress.isValid(cc) ? cc : AjxStringUtil.htmlEncode(cc); 1825 1826 match = urlQueryStr.match(/\bbcc=([^&]+)/i); 1827 var bcc = match ? AjxStringUtil.urlComponentDecode(match[1].replace(/\+/g, " ")) : null; 1828 bcc = bcc && AjxEmailAddress.isValid(bcc) ? bcc : AjxStringUtil.htmlEncode(bcc); 1829 1830 match = urlQueryStr.match(/\bbody=([^&]+)/i); 1831 var body = match ? (AjxStringUtil.urlComponentDecode(match[1]).replace(/\+/g, " ")) : null; 1832 1833 return { 1834 to: to, 1835 subject: AjxStringUtil.htmlEncode(subject), 1836 cc: cc, 1837 bcc: bcc, 1838 body: AjxStringUtil.htmlEncode(body) 1839 }; 1840 }; 1841 1842 ZmMailApp.prototype._showComposeView = 1843 function(callback, queryStr) { 1844 var qs = queryStr || location.search; 1845 1846 AjxDispatcher.require("Startup2"); 1847 var composeController = AjxDispatcher.run("GetComposeController"); 1848 1849 // RFC 2368 = mailto:user@zimbra.com?(headers=values)* 1850 var composeParams = this._parseComposeUrl(qs); 1851 var to = composeParams.to; 1852 if (to && to.indexOf('mailto') == 0) { 1853 to = to.replace(/mailto:/,''); 1854 var mailtoQuery = to.split('?'); 1855 composeParams.to = mailtoQuery[0]; 1856 if (mailtoQuery.length > 1) { 1857 //mailto:xyz@abc.com?.... 1858 mailtoQuery = mailtoQuery[1]; 1859 var mailtoParams = this._parseComposeUrl(mailtoQuery); 1860 // mailto:user@abc.com?to=xyz@abc.com&... or mailto:?to=xyz@abc.com 1861 composeParams.to = composeParams.to 1862 ? (mailtoParams.to ? [composeParams.to, ','+mailtoParams.to].join('') : composeParams.to ) 1863 : mailtoParams.to; 1864 composeParams.subject = mailtoParams.subject || composeParams.subject; 1865 composeParams.cc = mailtoParams.cc || composeParams.cc; 1866 composeParams.bcc = mailtoParams.bcc || composeParams.bcc; 1867 composeParams.body = mailtoParams.body || composeParams.body; 1868 } 1869 } 1870 1871 var params = { 1872 action: ZmOperation.NEW_MESSAGE, 1873 toOverride: composeParams.to, 1874 ccOverride: composeParams.cc, 1875 bccOverride: composeParams.bcc, 1876 subjOverride: composeParams.subject, 1877 extraBodyText: composeParams.body, 1878 extraBodyTextIsExternal: Boolean(composeParams.body), 1879 callback: callback 1880 }; 1881 1882 // this can happen in offlie where user clicks on mailto link and we're 1883 // already in compose view 1884 if (appCtxt.isOffline && 1885 appCtxt.get(ZmSetting.OFFLINE_SUPPORTS_MAILTO) && 1886 appCtxt.getCurrentViewId() == ZmId.VIEW_COMPOSE) 1887 { 1888 composeController.resetComposeForMailto(params); 1889 } 1890 else { 1891 composeController.doAction(params); 1892 } 1893 1894 this._notifyRendered(); 1895 return composeController; 1896 }; 1897 1898 /** 1899 * Returns a conversation list controller. 1900 * 1901 * @return {ZmConvListController} conversation list controller 1902 */ 1903 ZmMailApp.prototype.getConvListController = 1904 function(sessionId, searchResultsController) { 1905 return this.getSessionController({controllerClass: "ZmConvListController", 1906 sessionId: sessionId || ZmApp.MAIN_SESSION, 1907 searchResultsController: searchResultsController}); 1908 }; 1909 1910 /** 1911 * Returns a conversation controller. 1912 * 1913 * @return {ZmConvController} conversation controller 1914 */ 1915 ZmMailApp.prototype.getConvController = 1916 function(sessionId) { 1917 return this.getSessionController({controllerClass: "ZmConvController", 1918 sessionId: sessionId}); 1919 }; 1920 1921 /** 1922 * Gets the traditional (msg list) controller. 1923 * 1924 * @return {ZmTradController} traditional controller 1925 */ 1926 ZmMailApp.prototype.getTradController = 1927 function(sessionId, searchResultsController) { 1928 return this.getSessionController({controllerClass: "ZmTradController", 1929 sessionId: sessionId || ZmApp.MAIN_SESSION, 1930 searchResultsController: searchResultsController}); 1931 }; 1932 1933 /** 1934 * Gets the message controller. 1935 * 1936 * @return {ZmMsgController} message controller 1937 */ 1938 ZmMailApp.prototype.getMsgController = 1939 function(sessionId) { 1940 1941 // if message is already open get that session controller 1942 var controllers = this._sessionController[ZmId.VIEW_MSG]; 1943 var controller; 1944 for (var id in controllers) { 1945 if (!controllers[id].isHidden && controllers[id].getMsg() && controllers[id].getMsg().nId == sessionId) { 1946 controller = controllers[id]; 1947 break; 1948 } 1949 } 1950 1951 if (controller) { 1952 sessionId = controller.getSessionId(); 1953 this._curSessionId[ZmId.VIEW_MSG] = sessionId; 1954 controller.inactive = false; 1955 return controller; 1956 } 1957 1958 return this.getSessionController({controllerClass: "ZmMsgController", 1959 sessionId: sessionId}); 1960 }; 1961 1962 /** 1963 * Returns a compose controller. 1964 * 1965 * @return {ZmComposeController} compose controller 1966 */ 1967 ZmMailApp.prototype.getComposeController = 1968 function(sessionId) { 1969 return this.getSessionController({controllerClass: "ZmComposeController", 1970 sessionId: sessionId}); 1971 }; 1972 1973 ZmMailApp.prototype.getConfirmController = 1974 function(sessionId) { 1975 return this.getSessionController({controllerClass: "ZmMailConfirmController", 1976 sessionId: sessionId}); 1977 }; 1978 1979 /** 1980 * Gets the current mail list controller, which may be conversation list or msg list (traditional). 1981 * 1982 * @return {ZmTradController|ZmConvListController} mail list controller 1983 */ 1984 ZmMailApp.prototype.getMailListController = 1985 function() { 1986 var groupMailBy = appCtxt.get(ZmSetting.GROUP_MAIL_BY) ; 1987 return (groupMailBy == ZmSetting.GROUP_BY_CONV) ? AjxDispatcher.run("GetConvListController") : 1988 AjxDispatcher.run("GetTradController"); 1989 }; 1990 1991 ZmMailApp.prototype.runRefresh = 1992 function() { 1993 this.getMailListController().runRefresh(); 1994 }; 1995 1996 /** 1997 * Begins a compose session by presenting a form to the user. 1998 * 1999 * @param {Hash} params a hash of parameters 2000 * @param {constant} params.action the new message, reply, forward, or an invite action 2001 * @param {Boolean} params.inNewWindow if <code>true</code>, we are in detached window 2002 * @param {ZmMailMsg} params.msg the original message (reply/forward), or address (new message) 2003 * @param {String} params.toOverride the initial value for To: field 2004 * @param {String} params.subjOverride the initial value for Subject: field 2005 * @param {String} params.extraBodyText the canned text to prepend to body (invites) 2006 * @param {AjxCallback} params.callback the callback to run after view has been set 2007 * @param {String} params.accountName the on-behalf-of From address 2008 */ 2009 ZmMailApp.prototype.compose = 2010 function(params) { 2011 Dwt.setLoadingTime("ZmMailApp-compose"); 2012 params = params || {}; 2013 if (!params.sessionId) { 2014 // see if we already have a compose session for this message 2015 var controllers = this._sessionController[ZmId.VIEW_COMPOSE]; 2016 var controller; 2017 var msgId = params.msg && params.msg.nId; 2018 for (var id in controllers) { 2019 if (controllers[id].getMsg() && controllers[id].getMsg().nId == msgId){ 2020 controller = controllers[id]; 2021 break; 2022 } 2023 } 2024 } 2025 2026 if (!controller) { 2027 controller = AjxDispatcher.run("GetComposeController", params.sessionId); 2028 } 2029 2030 appCtxt.composeCtlrSessionId = controller.getSessionId(); // help new window dispose components 2031 controller.doAction(params); 2032 Dwt.setLoadedTime("ZmMailApp-compose"); 2033 }; 2034 2035 /** 2036 * Sets the new mail notice. 2037 * 2038 * @param {ZmOrganizer} organizer the organizer 2039 */ 2040 ZmMailApp.prototype.setNewMailNotice = 2041 function(organizer) { 2042 var appChooser = appCtxt.getAppChooser(); 2043 if (appChooser) { 2044 var mb = appChooser.getButton(ZmApp.MAIL); 2045 var icon = (organizer.numUnread > 0) ? "EnvelopeOpen" : "MailApp"; 2046 mb.setImage(icon); 2047 } 2048 if(organizer.id == ZmOrganizer.ID_INBOX) { 2049 this._setFavIcon(organizer.numUnread); 2050 } 2051 this._setNewMailBadge(); 2052 }; 2053 2054 ZmMailApp.prototype._setNewMailBadge = 2055 function() { 2056 if (appCtxt.isOffline && appCtxt.get(ZmSetting.OFFLINE_SUPPORTS_DOCK_UPDATE)) { 2057 if (AjxEnv.isMac && window.platform) { 2058 window.platform.icon().badgeText = (this.globalMailCount > 0) 2059 ? this.globalMailCount : null; 2060 } 2061 else if (AjxEnv.isWindows) { 2062 window.platform.icon().imageSpec = (this.globalMailCount > 0) 2063 ? "resource://webapp/icons/default/newmail.png" 2064 : "resource://webapp/icons/default/launcher.ico"; 2065 window.platform.icon().title = (this.globalMailCount > 0) 2066 ? AjxMessageFormat.format(ZmMsg.unreadCount, this.globalMailCount) : null; 2067 } 2068 } 2069 }; 2070 2071 ZmMailApp.prototype.clearNewMailBadge = 2072 function() { 2073 this.globalMailCount = 0; 2074 this._setNewMailBadge(); 2075 }; 2076 2077 ZmMailApp.prototype._setFavIcon = 2078 function(unread) { 2079 var url; 2080 if (unread == 0) { 2081 url = [appContextPath, "/img/logo/favicon.ico"].join(""); 2082 } else if (unread > 9) { 2083 url = [appContextPath,"/img/logo/favicon_plus.ico"].join(""); 2084 } else { 2085 url = [appContextPath, "/img/logo/favicon_", unread, ".ico"].join(""); 2086 } 2087 Dwt.setFavIcon(url); 2088 }; 2089 2090 /** 2091 * Gets the "group mail by" setting. This is a convenience method to 2092 * convert "group mail by" between server (string) and client (int constant) versions. 2093 * 2094 * @return {String} the group by mail setting 2095 */ 2096 ZmMailApp.prototype.getGroupMailBy = 2097 function() { 2098 var setting = this._groupBy || appCtxt.get(ZmSetting.GROUP_MAIL_BY); 2099 return setting ? ZmMailApp.GROUP_MAIL_BY_ITEM[setting] : ZmItem.MSG; 2100 }; 2101 2102 ZmMailApp.prototype.setGroupMailBy = 2103 function(groupBy, skipNotify) { 2104 this._groupBy = groupBy; 2105 appCtxt.set(ZmSetting.GROUP_MAIL_BY, groupBy, null, false, skipNotify); 2106 }; 2107 2108 // return enough for us to get a scroll bar since we are pageless 2109 ZmMailApp.prototype.getLimit = 2110 function(offset) { 2111 var limit = appCtxt.get(ZmSetting.PAGE_SIZE); 2112 return offset ? limit : 2 * limit; 2113 }; 2114 2115 /** 2116 * Adds a "Reply" submenu for replying to sender or all. 2117 * 2118 * @param {ZmToolBar|ZmActionMenu} parent the parent widget (a toolbar or action menu) 2119 * @return {ZmActionMenu} the menu 2120 */ 2121 ZmMailApp.addReplyMenu = 2122 function(parent) { 2123 var list = [ZmOperation.REPLY, ZmOperation.REPLY_ALL]; 2124 var menu = new ZmActionMenu({parent:parent, menuItems:list}); 2125 parent.setMenu(menu); 2126 return menu; 2127 }; 2128 2129 /** 2130 * Adds a "Forward" submenu for forwarding inline or as attachment. 2131 * 2132 * @param {ZmToolBar|ZmActionMenu} parent the parent widget (a toolbar or action menu) 2133 * @return {ZmActionMenu} the menu 2134 */ 2135 ZmMailApp.addForwardMenu = 2136 function(parent) { 2137 var list = [ZmOperation.FORWARD_INLINE, ZmOperation.FORWARD_ATT]; 2138 var menu = new ZmActionMenu({parent:parent, menuItems:list}); 2139 parent.setMenu(menu); 2140 return menu; 2141 }; 2142 2143 /** 2144 * Adds a data source collection. 2145 * 2146 * @param {ZmAccount} account the account 2147 * @return {ZmDataSourceCollection} the data source collection 2148 */ 2149 ZmMailApp.prototype.getDataSourceCollection = 2150 function(account) { 2151 var appCtxt = window.parentAppCtxt || window.appCtxt; 2152 var activeAcct = account ? account.name : appCtxt.getActiveAccount().name; 2153 2154 if (!this._dataSourceCollection[activeAcct]) { 2155 this._dataSourceCollection[activeAcct] = new ZmDataSourceCollection(); 2156 if (appCtxt.getActiveAccount().isMain) { 2157 this._dataSourceCollection[activeAcct].initialize(appCtxt.getSettings().getInfoResponse.dataSources); 2158 } 2159 } 2160 return this._dataSourceCollection[activeAcct]; 2161 }; 2162 2163 /** 2164 * Gets the identity collection. 2165 * 2166 * @param {ZmAccount} account the account 2167 * @return {ZmIdentityCollection} the identity collection 2168 */ 2169 ZmMailApp.prototype.getIdentityCollection = 2170 function(account) { 2171 // child window always gets its own identitiy collection 2172 if (appCtxt.isChildWindow) { 2173 if (!this._identityCollection) { 2174 this._identityCollection = new ZmIdentityCollection(); 2175 } 2176 return this._identityCollection; 2177 } 2178 2179 var activeAcct = account ? account.name : appCtxt.getActiveAccount().name; 2180 2181 if (!this._identityCollection[activeAcct]) { 2182 var ic = this._identityCollection[activeAcct] = new ZmIdentityCollection(); 2183 var settings = appCtxt.getSettings(account); 2184 if (settings) 2185 ic.initialize(settings.getInfoResponse.identities); 2186 } 2187 return this._identityCollection[activeAcct]; 2188 }; 2189 2190 /** 2191 * Gets the signature collection. 2192 * 2193 * @param {ZmAccount} account the account 2194 * @return {ZmSignatureCollection} the signature collection 2195 */ 2196 ZmMailApp.prototype.getSignatureCollection = 2197 function(account) { 2198 var appCtxt = window.parentAppCtxt || window.appCtxt; 2199 account = account || appCtxt.getActiveAccount(); 2200 var activeAcct = account.name; 2201 var settings = appCtxt.getSettings(account); 2202 if (!this._signatureCollection[activeAcct] && settings) { 2203 var sc = this._signatureCollection[activeAcct] = new ZmSignatureCollection(); 2204 sc.initialize(settings.getInfoResponse.signatures); 2205 } 2206 return this._signatureCollection[activeAcct]; 2207 }; 2208 2209 ZmMailApp.prototype._addSettingsChangeListeners = 2210 function() { 2211 ZmApp.prototype._addSettingsChangeListeners.call(this); 2212 2213 if (!this._settingsListener) { 2214 this._settingsListener = new AjxListener(this, this._settingsChangeListener); 2215 } 2216 2217 var settings = appCtxt.getSettings(); 2218 settings.getSetting(ZmSetting.VIEW_AS_HTML).addChangeListener(this._settingListener); 2219 settings.getSetting(ZmSetting.TRUSTED_ADDR_LIST).addChangeListener(this._settingListener); 2220 settings.addChangeListener(this._settingsListener); 2221 }; 2222 2223 /** 2224 * Individual setting listener. 2225 */ 2226 ZmMailApp.prototype._settingChangeListener = 2227 function(ev) { 2228 ZmApp.prototype._settingChangeListener.call(this, ev); 2229 2230 if (ev.type != ZmEvent.S_SETTING) { return; } 2231 2232 var setting = ev.source; 2233 var mlc = this.getMailListController(); 2234 2235 if (mlc && (setting.id == ZmSetting.VIEW_AS_HTML || setting.id == ZmSetting.TRUSTED_ADDR_LIST)) { 2236 this.resetTrustedSendersList(); 2237 var dpv = mlc._doublePaneView; 2238 var msg = dpv ? dpv.getMsg() : null; 2239 if (msg) { 2240 dpv.reset(); 2241 dpv.setItem(msg); 2242 } 2243 } 2244 }; 2245 2246 /** 2247 * Settings listener. Process changed settings as a group, so that we 2248 * don't redo the search more than once if more than one relevant mail 2249 * setting has changed. 2250 * 2251 * @private 2252 */ 2253 ZmMailApp.prototype._settingsChangeListener = 2254 function(ev) { 2255 if (ev.type != ZmEvent.S_SETTINGS) { return; } 2256 2257 var list = ev.getDetail("settings"); 2258 if (!(list && list.length)) { return; } 2259 2260 var mlc = this.getMailListController(); 2261 if (!mlc) { return; } 2262 2263 var curView = mlc.getCurrentViewType(); 2264 var newView, groupByView; 2265 2266 for (var i = 0; i < list.length; i++) { 2267 var setting = list[i]; 2268 if (setting.id == ZmSetting.SHOW_FRAGMENTS) { 2269 if (curView != ZmId.VIEW_MSG) { 2270 newView = groupByView || curView; 2271 } 2272 } 2273 } 2274 newView = groupByView || newView; 2275 2276 if (newView) { 2277 mlc.switchView(newView, true); 2278 } 2279 }; 2280 2281 ZmMailApp.prototype.getTrustedSendersList = 2282 function() { 2283 if(!this._trustedList) { 2284 var trustedList = appCtxt.get(ZmSetting.TRUSTED_ADDR_LIST); 2285 if(trustedList) { 2286 this._trustedList = AjxVector.fromArray(trustedList); 2287 } 2288 else { 2289 this._trustedList = new AjxVector(); 2290 } 2291 } 2292 return this._trustedList; 2293 }; 2294 2295 ZmMailApp.prototype.resetTrustedSendersList = 2296 function() { 2297 this._trustedList = null; 2298 }; 2299 2300 ZmMailApp._handleOOORemindResponse = function(dialog,isTurnOff){ 2301 ZmMailApp._hideOOORemindDialog(dialog); 2302 var dontRemind = document.getElementById(dialog._htmlElId + "_dontRemind"); 2303 2304 if(isTurnOff || dontRemind.checked){ 2305 ZmMailApp._saveRemindStatus(isTurnOff,dontRemind.checked); 2306 } 2307 2308 }; 2309 2310 ZmMailApp._hideOOORemindDialog=function(dialog){ 2311 if(dialog){ 2312 dialog.popdown(); 2313 } 2314 }; 2315 2316 ZmMailApp._saveRemindStatus = function(turnOff,dontRemind) { 2317 var soapDoc = AjxSoapDoc.create("ModifyPrefsRequest", "urn:zimbraAccount"); 2318 2319 if(turnOff){ 2320 var node = soapDoc.set("pref", "FALSE"); 2321 node.setAttribute("name", "zimbraPrefOutOfOfficeReplyEnabled"); 2322 } 2323 else if(dontRemind){ 2324 var node = soapDoc.set("pref", "FALSE"); 2325 node.setAttribute("name", "zimbraPrefOutOfOfficeStatusAlertOnLogin"); 2326 } 2327 2328 var paramsObj = {soapDoc:soapDoc, asyncMode:true}; 2329 if(turnOff){paramsObj.callback=ZmMailApp._oooReplyCallback;} 2330 2331 appCtxt.getAppController().sendRequest(paramsObj); 2332 }; 2333 2334 ZmMailApp._oooReplyCallback = function(){ 2335 appCtxt.set(ZmSetting.VACATION_MSG_ENABLED,false); 2336 } 2337 2338 ZmMailApp.prototype._isOnVacation = function() { 2339 if (!appCtxt.get(ZmSetting.VACATION_MSG_ENABLED)) { 2340 return false; //no vacation 2341 } 2342 var from = appCtxt.get(ZmSetting.VACATION_FROM); 2343 var to = appCtxt.get(ZmSetting.VACATION_UNTIL); 2344 2345 if (!from) { 2346 return true; //unlimited vacation (if from is empty, so is to) 2347 } 2348 2349 var today = new Date(); 2350 var formatter = new AjxDateFormat("yyyyMMddHHmmss'Z'"); 2351 var fromDate = formatter.parse(AjxDateUtil.dateGMT2Local(from)); 2352 var toDate = formatter.parse(AjxDateUtil.dateGMT2Local(to)); 2353 return fromDate < today && today < toDate; 2354 }; 2355 2356 2357 ZmMailApp.prototype._checkVacationReplyEnabled = function(){ 2358 if (!appCtxt.get(ZmSetting.VACATION_MSG_REMIND_ON_LOGIN)) { 2359 return; //reminder not enabled 2360 } 2361 2362 if (!this._isOnVacation()) { 2363 return; 2364 } 2365 2366 var ynDialog = new DwtMessageDialog({parent:appCtxt.getShell(), buttons:[DwtDialog.YES_BUTTON, DwtDialog.NO_BUTTON], id: "VacationDialog"}); 2367 var content = AjxTemplate.expand("mail.Message#VacationRemindDialog", {id:ynDialog._htmlElId}); 2368 ynDialog.setTitle(ZmMsg.OOORemindDialogTitle); 2369 ynDialog.setContent(content); 2370 var dontRemind = document.getElementById(ynDialog._htmlElId + "_dontRemind"); 2371 dontRemind.checked = false; 2372 ynDialog.registerCallback(DwtDialog.YES_BUTTON, ZmMailApp._handleOOORemindResponse, null, [ynDialog, true]); 2373 ynDialog.registerCallback(DwtDialog.NO_BUTTON, ZmMailApp._handleOOORemindResponse, null, [ynDialog, false]); 2374 ynDialog.popup(); 2375 }; 2376 2377 ZmMailApp.prototype._createVirtualFolders = 2378 function() { 2379 ZmOffline.addOutboxFolder(); 2380 }; 2381 2382 ZmMailApp.prototype.resetWebClientOfflineOperations = 2383 function() { 2384 ZmApp.prototype.resetWebClientOfflineOperations.apply(this); 2385 //Refreshing the mail list both for online and offline mode 2386 var isWebClientOnline = !appCtxt.isWebClientOffline(); 2387 this.refresh(); 2388 var folders = appCtxt.getFolderTree().getByType(ZmOrganizer.FOLDER); 2389 var overview = this.getOverview(); 2390 if (folders && overview) { 2391 for (var i = 0; i < folders.length; i++) { 2392 var folder = folders[i]; 2393 var treeItem = folder && overview.getTreeItemById(folder.id); 2394 if (!treeItem) { 2395 continue; 2396 } 2397 if (isWebClientOnline) { 2398 treeItem.setVisible(true); 2399 } 2400 else { 2401 //Don't hide ROOT folder and OUTBOX folder 2402 if (folder.id != ZmFolder.ID_ROOT && folder.rid != ZmFolder.ID_ROOT && folder.id != ZmFolder.ID_OUTBOX && folder.webOfflineSyncDays === 0) { 2403 treeItem.setVisible(false); 2404 } 2405 } 2406 } 2407 } 2408 }; 2409 2410 /* 2411 * Enables Mail preferences in case of Admin viewing user account keeping Mail App is disabled. 2412 */ 2413 ZmMailApp.prototype.enableMailPrefs = 2414 function() { 2415 // ZmPref is unavailable, hence we load it, register settings, operations & preferences. 2416 AjxDispatcher.require("PreferencesCore"); 2417 this._registerSettings(); 2418 this._registerOperations(); 2419 this._registerPrefs(); 2420 }; 2421 2422 // Folders to ignore when displaying a conv's messages 2423 ZmMailApp.FOLDERS_TO_OMIT = [ZmFolder.ID_TRASH, ZmFolder.ID_SPAM]; 2424 2425 // returns lookup hash of folders (starting with Trash/Junk) whose messages aren't included when 2426 // viewing or replying a conv; if we're in one of those, we still show its messages 2427 ZmMailApp.getFoldersToOmit = function(search) { 2428 2429 search = search || appCtxt.getCurrentSearch(); 2430 2431 var folders = ZmMailApp.FOLDERS_TO_OMIT, 2432 omit = [], 2433 curFolderId = search && search.folderId; 2434 2435 var isUserInitiatedSearch = search && search.userInitiated; 2436 2437 for (var i = 0; i < folders.length; i++) { 2438 if (!isUserInitiatedSearch && folders[i] != curFolderId) { 2439 omit.push(folders[i]); 2440 } 2441 } 2442 return AjxUtil.arrayAsHash(omit); 2443 }; 2444 2445 /* 2446 returns the folders to omit in case of reply/reply-all/forward - this includes DRAFTS always in addition to the others as returned by ZmMailApp.getFoldersToOmit 2447 (the others depend on current folder, but DRAFTS should always be ignored when replying/forwarding, even under Drafts folder) 2448 */ 2449 ZmMailApp.getReplyFoldersToOmit = function(search) { 2450 var omit = ZmMailApp.getFoldersToOmit(search); 2451 omit[ZmFolder.ID_DRAFTS] = true; 2452 return omit; 2453 }; 2454