1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 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) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * @overview
 26  * This file defines the settings class.
 27  */
 28 
 29 /**
 30  * Creates a collection of settings with default values. If no app context is given,
 31  * then this is a skeletal, non-live version of settings which can provide default
 32  * settings and parse SOAP settings.
 33  * @class
 34  * This class is a collection of various sorts of settings: config values, preferences,
 35  * and COS features. Each setting has an ID which can be used to retrieve it.
 36  *
 37  * @author Conrad Damon
 38  *
 39  * @param {Boolean}	noInit	if <code>true</code>, skip initialization
 40  *
 41  * @extends		ZmModel
 42  */
 43 ZmSettings = function(noInit) {
 44 
 45 	ZmModel.call(this, ZmEvent.S_SETTINGS);
 46 
 47 	this._settings = {};	// settings by ID
 48 	this._nameToId = {};	// map to get from server setting name to setting ID
 49 
 50 	this.getInfoResponse = null; // Cached GetInfoResponse for lazy creation of identities, etc.
 51 	this._handleImplicitChange = new AjxListener(this, this._implicitChangeListener);
 52 
 53 	if (!noInit) {
 54         this.initialize();
 55 	}
 56 };
 57 
 58 ZmSettings.prototype = new ZmModel;
 59 ZmSettings.prototype.constructor = ZmSettings;
 60 
 61 ZmSettings.BASE64_TO_NORMAL_RATIO = 1.34;
 62 
 63 /**
 64  * Creates a new setting and adds it to the settings.
 65  *
 66  * @param {String}		id			the unique ID of the setting
 67  * @param {Hash}	params		a hash of parameters
 68  * @param {String}	params.name			the name of the pref or attr on the server
 69  * @param {constant}	params.type		config, pref, or COS
 70  * @param {constant}	params.dataType	string, int, or boolean (defaults to string)
 71  * @param {Object}	params.defaultValue	the default value
 72  */
 73 ZmSettings.prototype.registerSetting =
 74 function(id, params) {
 75 	ZmSetting[id] = id;
 76 	var setting = this._settings[id] = new ZmSetting(id, params);
 77 	if (params.name) {
 78 		this._nameToId[params.name] = id;
 79 	}
 80 	if (params.isImplicit) {
 81 		setting.addChangeListener(this._handleImplicitChange);
 82 	}
 83 	return setting;
 84 };
 85 
 86 /**
 87  * Returns a string representation of the object.
 88  *
 89  * @return		{String}		a string representation of the object
 90  */
 91 ZmSettings.prototype.toString =
 92 function() {
 93 	return "ZmSettings";
 94 };
 95 
 96 /**
 97  * Initializes the settings.
 98  *
 99  */
100 ZmSettings.prototype.initialize =
101 function() {
102 	this._initialize();
103 	this._setDefaults();
104 	this.userSettingsLoaded = false;
105 
106 	// set listeners for settings
107 	var listener = new AjxListener(this, this._changeListener);
108 	if (!appCtxt.multiAccounts) {
109 		this.getSetting(ZmSetting.QUOTA_USED).addChangeListener(listener);
110 	}
111 	this.getSetting(ZmSetting.POLLING_INTERVAL).addChangeListener(listener);
112 	this.getSetting(ZmSetting.SKIN_NAME).addChangeListener(listener);
113 	this.getSetting(ZmSetting.SHOW_SELECTION_CHECKBOX).addChangeListener(listener);
114 	this.getSetting(ZmSetting.LOCALE_NAME).addChangeListener(listener);
115 	this.getSetting(ZmSetting.FONT_NAME).addChangeListener(listener);
116 	this.getSetting(ZmSetting.FONT_SIZE).addChangeListener(listener);
117 	this.getSetting(ZmSetting.SHORTCUTS).addChangeListener(listener);
118 	this.getSetting(ZmSetting.CHILD_ACCTS_VISIBLE).addChangeListener(listener);
119 	this.getSetting(ZmSetting.ATTACHMENTS_BLOCKED).addChangeListener(listener);
120 	this.getSetting(ZmSetting.CHAT_PLAY_SOUND).addChangeListener(listener);
121 	this.getSetting(ZmSetting.CHAT_ENABLED).addChangeListener(listener);
122 
123 
124 	if (appCtxt.isOffline) {
125 		this.getSetting(ZmSetting.OFFLINE_IS_MAILTO_HANDLER).addChangeListener(listener);
126         this.getSetting(ZmSetting.OFFLINE_BACKUP_ACCOUNT_ID).addChangeListener(listener);
127         this.getSetting(ZmSetting.OFFLINE_BACKUP_INTERVAL).addChangeListener(listener);
128         this.getSetting(ZmSetting.OFFLINE_BACKUP_PATH).addChangeListener(listener);
129         this.getSetting(ZmSetting.OFFLINE_BACKUP_KEEP).addChangeListener(listener);
130         this.getSetting(ZmSetting.OFFLINE_UPDATE_NOTIFY).addChangeListener(listener);
131 	}
132 };
133 
134 /**
135  * Gets the value of the given setting.
136  *
137  * @param {String}	id		the ID of the setting
138  * @param	{String}	key		the key
139  * @return	{Object}	the value or <code>null</code> for none
140  */
141 ZmSettings.prototype.get =
142 function(id, key) {
143 	return (id && this._settings[id]) ? this._settings[id].getValue(key) : null;
144 };
145 
146 /**
147  * Sets the value of the given setting.
148  *
149  * @param {String}	id		the ID of the setting
150  * @param {Object}	value			the new value for the setting
151  * @param {String}	key 			optional key for use by hash table data type
152  * @param {Boolean}	setDefault		if <code>true</code>, also set the default value
153  * @param {Boolean}	skipNotify		if <code>true</code>, do not notify listeners
154  * @param {Boolean}	skipImplicit		if <code>true</code>, do not check for change to implicit pref
155  */
156 ZmSettings.prototype.set = function(id, value, key, setDefault, skipNotify, skipImplicit) {
157 	if (id && this._settings[id]) {
158 		this._settings[id].setValue(value, key, setDefault, skipNotify, skipImplicit);
159 	}
160 	else {
161 		DBG.println(AjxDebug.DBG1, "ZmSettings.set: ID missing (value = " + value);
162 	}
163 };
164 
165 /**
166  * Gets the setting.
167  *
168  * @param {String}	id		the ID of the setting
169  * @return	{ZmSetting}	the setting
170  */
171 ZmSettings.prototype.getSetting =
172 function(id) {
173 	return this._settings[id];
174 };
175 
176 /**
177  * Populates settings values.
178  *
179  * @param {Hash}	list		a hash of preference or attribute values
180  */
181 ZmSettings.prototype.createFromJs =
182 function(list, setDefault, skipNotify, skipImplicit) {
183     // default skipImplicit value is true
184     skipImplicit = skipImplicit == null || skipImplicit; 
185 
186 	for (var i in list) {
187 		var val = list[i];
188 		var setting = this._settings[this._nameToId[i]];
189 		if (setting) {
190 			if (setting.dataType == ZmSetting.D_HASH) {
191 				var pairs = val.split(",");
192 				var value = {};
193 				for (var j = 0; j < pairs.length; j++) {
194 					var fields = pairs[j].split(":");
195 					value[fields[0]] = fields[1];
196 				}
197 				val = value;
198 			}
199 			setting.setValue(val, null, setDefault, skipNotify, skipImplicit);
200 			if (ZmSetting.IS_IMPLICIT[setting.id]) {
201 				setting.origValue = setting.copyValue();
202 			}
203 		} else {
204 			DBG.println(AjxDebug.DBG3, "*** Unrecognized setting: " + i);
205 		}
206 	}
207 };
208 
209 /**
210  * Gets the setting that is associated with the given server-side setting, if any.
211  *
212  * @param {String}	name	the server-side setting name (for example, "zimbraFeatureContactsEnabled")
213  * @return	{String}	the setting id
214  */
215 ZmSettings.prototype.getSettingByName =
216 function(name) {
217 	return this._nameToId[name];
218 };
219 
220 /**
221  * Checks if the given ID was received from the server. Use this method
222  * to determine whether this ID is supported by a ZCS server. Currently used by
223  * ZDesktop since it can "talk" to both v5 and v6 ZCS.
224  *
225  * @param {String}	id	the setting ID
226  * @return	{Boolean}	<code>true</code> if the attribute is supported
227  */
228 ZmSettings.prototype.attrExists =
229 function(id) {
230 	var name = this.getSetting(id).name;
231 	return (this.getInfoResponse.prefs._attrs[name] ||
232 			this.getInfoResponse.attrs._attrs[name]);
233 };
234 
235 /**
236  * Retrieves the preferences, COS settings, and metadata for the current user.
237  * All the data gets stored into the settings collection.
238  *
239  * @param {AjxCallback}	callback 			the callback to run after response is received
240  * @param {AjxCallback}	errorCallback 	the callback to run error is received
241  * @param {String}	accountName		the name of account to load settings for
242  * @param {Object}	response			the pre-determined JSON response object
243  * @param {ZmBatchCommand}	batchCommand		set if part of a batch request
244  */
245 ZmSettings.prototype.loadUserSettings =
246 function(callback, errorCallback, accountName, response, batchCommand) {
247 	var args = [callback, accountName];
248 
249 	var soapDoc = AjxSoapDoc.create("GetInfoRequest", "urn:zimbraAccount");
250 	soapDoc.setMethodAttribute("rights", "createDistList"); //not sure when this is called, but let's add it anyway. (on login it's called from within launchZCS.JSP calling GetInfoJSONTag.java
251 	var respCallback = this._handleResponseLoadUserSettings.bind(this, args);
252 	if (batchCommand) {
253 		batchCommand.addNewRequestParams(soapDoc, respCallback);
254 	}
255 	else {
256 		var params = {
257 			soapDoc: (response ? null : soapDoc),
258 			accountName: accountName,
259 			asyncMode: true,
260 			callback: respCallback,
261 			errorCallback: errorCallback,
262 			response: response
263 		};
264 		appCtxt.getAppController().sendRequest(params);
265 	}
266 };
267 
268 /**
269  * @private
270  */
271 ZmSettings.prototype._handleResponseLoadUserSettings =
272 function(callback, accountName, result) {
273     this.setUserSettings(result.getResponse().GetInfoResponse, accountName);
274     this.userSettingsLoaded = true;
275     if (callback) {
276         callback.run(result);
277     }
278 };
279 
280 /**
281  * Sets the user settings.
282  *
283  * @param {hash}    params
284  * @param {object}  params.info             The GetInfoResponse object.
285  * @param {string}  [params.accountName]    The name of the account.
286  * @param {boolean} [params.setDefault]     Set default value
287  * @param {boolean} [params.skipNotify]     Skip change notification
288  * @param {boolean} [params.skipImplicit]   Skip implicit changes
289  * @param {boolean} [params.preInit]        Only init base settings for startup
290  */
291 ZmSettings.prototype.setUserSettings = function(params) {
292 
293     params = Dwt.getParams(arguments, ["info", "accountName", "setDefault", "skipNotify", "skipImplicit", "preInit"]);
294 
295     var info = this.getInfoResponse = params.info;
296 
297 	appCtxt.createDistListAllowed = false;
298 	appCtxt.createDistListAllowedDomains = [];
299 	appCtxt.createDistListAllowedDomainsMap = {};
300 	var rightTargets = info.rights && info.rights.targets;
301 	if (rightTargets) {
302 		for (var i = 0; i < rightTargets.length; i++) {
303 			var target = rightTargets[i];
304 			if (target.right == "createDistList") {
305 				if (target.target[0].type == "domain") {
306 					appCtxt.createDistListAllowed = true;
307 					appCtxt.createDistListAllowedDomains.push(target.target[0].name);
308 					appCtxt.createDistListAllowedDomainsMap[target.target[0].name] = true;
309 					break;
310 				}
311 			}
312 
313 		}
314 	}
315 
316 	// For delegated admin who can see prefs but not mail, we still need to register the mail settings so
317 	// they can be created and set below in createFromJs().
318 	if (this.get(ZmSetting.ADMIN_DELEGATED)) {
319 		if (!this.get(ZmSetting.ADMIN_MAIL_ENABLED) && this.get(ZmSetting.ADMIN_PREFERENCES_ENABLED)) {
320 			(new ZmMailApp()).enableMailPrefs();
321 		}
322 	}
323 
324 	// Voice feature
325     this.set(ZmSetting.VOICE_ENABLED, this._hasVoiceFeature(), null, false, true);
326 
327     var accountName = params.accountName;
328     var setDefault = params.preInit ? false : params.setDefault;
329     var skipNotify = params.preInit ? true : params.skipNotify;
330     var skipImplicit = params.preInit ? true : params.skipImplicit;
331 
332     var settings = [
333         ZmSetting.ADMIN_DELEGATED,          info.adminDelegated,
334         ZmSetting.MESSAGE_SIZE_LIMIT,    this._base64toNormalSize(info.attSizeLimit),
335         ZmSetting.CHANGE_PASSWORD_URL,      info.changePasswordURL,
336         ZmSetting.DOCUMENT_SIZE_LIMIT,      this._base64toNormalSize(info.docSizeLimit),
337         ZmSetting.LAST_ACCESS,              info.accessed,
338         ZmSetting.LICENSE_STATUS,           info.license && info.license.status,
339         ZmSetting.PREVIOUS_SESSION,         info.prevSession,
340         ZmSetting.PUBLIC_URL,               info.publicURL,
341 		ZmSetting.ADMIN_URL,                info.adminURL,
342         ZmSetting.QUOTA_USED,               info.used,
343         ZmSetting.RECENT_MESSAGES,          info.recent,
344         ZmSetting.REST_URL,                 info.rest,
345         ZmSetting.USERNAME,                 info.name,
346 		ZmSetting.EMAIL_VALIDATION_REGEX, 	info.zimbraMailAddressValidationRegex,
347 		ZmSetting.DISABLE_SENSITIVE_ZIMLETS_IN_MIXED_MODE, 	(info.domainSettings && info.domainSettings.zimbraZimletDataSensitiveInMixedModeDisabled ? info.domainSettings.zimbraZimletDataSensitiveInMixedModeDisabled : "FALSE")
348     ];
349     for (var i = 0; i < settings.length; i += 2) {
350         var value = settings[i+1];
351         if (value != null) {
352             this.set(settings[i], value, null, setDefault, skipNotify, skipImplicit);
353         }
354     }
355 
356     // features and other settings
357     if (info.attrs && info.attrs._attrs) {
358         this.createFromJs(info.attrs._attrs, setDefault, skipNotify, skipImplicit);
359     }
360 
361 	// By default, everything but mail is enabled for delegated admin. Require an additional setting to allow admin to view mail.
362 	if (this.get(ZmSetting.ADMIN_DELEGATED)) {
363 		this.set(ZmSetting.MAIL_ENABLED, this.get(ZmSetting.ADMIN_MAIL_ENABLED), setDefault, skipNotify, skipImplicit);
364 		var enableMailPrefs = this.get(ZmSetting.MAIL_ENABLED) || (this.get(ZmSetting.ADMIN_DELEGATED) && this.get(ZmSetting.ADMIN_PREFERENCES_ENABLED));
365 		this.set(ZmSetting.MAIL_PREFERENCES_ENABLED, enableMailPrefs, setDefault, skipNotify, skipImplicit);
366 		// Disable other areas where mail could be exposed to a prefs-only admin
367 		if (this.get(ZmSetting.MAIL_PREFERENCES_ENABLED) && !this.get(ZmSetting.MAIL__ENABLED)) {
368 			this.set(ZmSetting.MAIL_FORWARDING_ENABLED, false, setDefault, skipNotify, skipImplicit);
369 			this.set(ZmSetting.FILTERS_MAIL_FORWARDING_ENABLED, false, setDefault, skipNotify, skipImplicit);
370 			this.set(ZmSetting.NOTIF_FEATURE_ENABLED, false, setDefault, skipNotify, skipImplicit);
371 		}
372 	}
373 
374 	// Server may provide us with SSO-enabled URL for Community integration (integration URL with OAuth signature)
375 	if (info.communityURL) {
376 		this.set(ZmSetting.SOCIAL_EXTERNAL_URL, info.communityURL, null, setDefault, skipNotify);
377 	}
378 
379 	if (params.preInit) {
380 	    return;
381     }
382 
383     // preferences
384     if (info.prefs && info.prefs._attrs) {
385         this.createFromJs(info.prefs._attrs, setDefault, skipNotify, skipImplicit);
386     }
387 
388     // accounts
389 	var setting;
390 	if (!accountName) {
391 		// NOTE: only the main account can have children
392 		appCtxt.accountList.createAccounts(this, info);
393 
394 		// for offline, find out whether this client supports prism-specific features
395 		if (appCtxt.isOffline) {
396 			if (AjxEnv.isPrism && window.platform) {
397 				this.set(ZmSetting.OFFLINE_SUPPORTS_MAILTO, true, null, setDefault, skipNotify, skipImplicit);
398 				this.set(ZmSetting.OFFLINE_SUPPORTS_DOCK_UPDATE, true, null, setDefault, skipNotify, skipImplicit);
399 			}
400 
401 			// bug #45804 - sharing always enabled for offline
402 			appCtxt.set(ZmSetting.SHARING_ENABLED, true, null, setDefault, skipNotify);
403 		}
404 	}
405 
406 	// handle settings whose values may depend on other settings
407 	setting = this._settings[ZmSetting.REPLY_TO_ADDRESS];
408 	if (setting) {
409 		setting.defaultValue = this.get(ZmSetting.USERNAME);
410 	}
411 	if (this.get(ZmSetting.FORCE_CAL_OFF)) {
412 		this.set(ZmSetting.CALENDAR_ENABLED, false, null, setDefault, skipNotify, skipImplicit);
413 	}
414 
415 	if (!this.get(ZmSetting.OPTIONS_ENABLED)) {
416 		this.set(ZmSetting.FILTERS_ENABLED, false, null, setDefault, skipNotify, skipImplicit);
417 	}
418 
419 	// load zimlets *only* for the main account
420 	if (!accountName) {
421 		if (info.zimlets && info.zimlets.zimlet) {
422             if (this.get(ZmSetting.ZIMLETS_SYNCHRONOUS)) {
423                 var action = new AjxTimedAction(this, this._beginLoadZimlets, [info.zimlets.zimlet, info.props.prop, true]);
424                 AjxTimedAction.scheduleAction(action, 0);
425             } else {
426                 var listener = new AjxListener(this, this._beginLoadZimlets, [info.zimlets.zimlet, info.props.prop, false]);
427                 appCtxt.getAppController().addListener(ZmAppEvent.POST_STARTUP, listener);
428             }
429 		} else {
430 			appCtxt.allZimletsLoaded();
431 		}
432 	}
433 
434 	var value = appCtxt.get(ZmSetting.REPLY_INCLUDE_ORIG);
435 	if (value) {
436 		var list = ZmMailApp.INC_MAP[value];
437 		appCtxt.set(ZmSetting.REPLY_INCLUDE_WHAT, list[0], null, setDefault, skipNotify);
438 		appCtxt.set(ZmSetting.REPLY_USE_PREFIX, list[1], null, setDefault, skipNotify);
439 		appCtxt.set(ZmSetting.REPLY_INCLUDE_HEADERS, list[2], null, setDefault, skipNotify);
440 	}
441 
442 	var value = appCtxt.get(ZmSetting.FORWARD_INCLUDE_ORIG);
443 	if (value) {
444 		var list = ZmMailApp.INC_MAP[value];
445 		appCtxt.set(ZmSetting.FORWARD_INCLUDE_WHAT, list[0], null, setDefault, skipNotify);
446 		appCtxt.set(ZmSetting.FORWARD_USE_PREFIX, list[1], null, setDefault, skipNotify);
447 		appCtxt.set(ZmSetting.FORWARD_INCLUDE_HEADERS, list[2], null, setDefault, skipNotify);
448 	}
449 
450     // Populate Sort Order Defaults
451     var sortPref =  ZmSettings.DEFAULT_SORT_PREF;
452     sortPref[ZmId.VIEW_CONVLIST]			= ZmSearch.DATE_DESC;
453     sortPref[ZmId.VIEW_CONV]				= ZmSearch.DATE_DESC;
454     sortPref[ZmId.VIEW_TRAD]				= ZmSearch.DATE_DESC;
455     sortPref[ZmId.VIEW_CONTACT_SRC]			= ZmSearch.NAME_ASC;
456     sortPref[ZmId.VIEW_CONTACT_TGT]			= ZmSearch.NAME_ASC;
457     sortPref[ZmId.VIEW_CONTACT_SIMPLE]		= ZmSearch.NAME_ASC;
458     sortPref[ZmId.VIEW_CAL]					= ZmSearch.DATE_ASC;
459     sortPref[ZmId.VIEW_TASKLIST]			= ZmSearch.DUE_DATE_ASC;
460     sortPref[ZmId.VIEW_BRIEFCASE_DETAIL]	= ZmSearch.SUBJ_ASC;
461 
462     var sortOrderSetting = this._settings[ZmSetting.SORTING_PREF];
463     if (sortOrderSetting) {
464         // Populate empty sort pref's with defaultValues
465         for (var pref in sortPref){
466             if (!sortOrderSetting.getValue(pref)){
467                 sortOrderSetting.setValue(sortPref[pref], pref, false, true);
468             }
469         }
470 
471         // Explicitly Set defaultValue
472         sortOrderSetting.defaultValue = AjxUtil.hashCopy(sortPref);
473     }
474 	
475 	DwtControl.useBrowserTooltips = this.get(ZmSetting.BROWSER_TOOLTIPS_ENABLED);
476 
477 	this._updateUserFontPrefsRule();
478 };
479 
480 
481 ZmSettings.prototype._base64toNormalSize =
482 function(base64) {
483 	if (!base64 || base64 === -1) { //-1 is unlimited
484 		return base64;
485 	}
486 	return base64 / ZmSettings.BASE64_TO_NORMAL_RATIO;
487 };
488 
489 
490 /**
491  * @private
492  */
493 ZmSettings.prototype._beginLoadZimlets =
494 function(zimlet, prop, sync) {
495 	var zimletsCallback = new AjxCallback(this, this._loadZimletPackage, [zimlet, prop, sync]);
496 	AjxDispatcher.require(["Startup2"], false, zimletsCallback);
497 };
498 
499 ZmSettings.prototype._loadZimletPackage =
500 function(zimlet, prop, sync) {
501 	var zimletsCallback = new AjxCallback(this, this._loadZimlets, [zimlet, prop, sync]);
502 	AjxDispatcher.require("Zimlet", false, zimletsCallback);
503 };
504 
505 /**
506  * @private
507  */
508 ZmSettings.prototype._loadZimlets =
509 function(allZimlets, props, sync) {
510 
511 	allZimlets = allZimlets || [];
512 	this.registerSetting("ZIMLETS",		{type:ZmSetting.T_CONFIG, defaultValue:allZimlets, isGlobal:true});
513 	this.registerSetting("USER_PROPS",	{type:ZmSetting.T_CONFIG, defaultValue:props});
514 
515 	var zimlets = this._getCheckedZimlets(allZimlets);
516 
517 	DBG.println(AjxDebug.DBG1, "Zimlets - Loading " + zimlets.length + " Zimlets");
518 	var zimletMgr = appCtxt.getZimletMgr();
519 	zimletMgr.loadZimlets(zimlets, props, null, null, sync);
520 
521 	if (zimlets && zimlets.length) {
522 		var activeApp = appCtxt.getCurrentApp();
523 		if (activeApp) {
524 			var overview;
525 			if (appCtxt.multiAccounts) {
526 				var containerId = activeApp.getOverviewContainer().containerId;
527 				var zimletLabel = ZmOrganizer.LABEL[ZmOrganizer.ZIMLET];
528 				var overviewId = [containerId, zimletLabel].join("_");
529 				overview = appCtxt.getOverviewController().getOverview(overviewId);
530 			} else {
531 				overview = activeApp.getOverview();
532 			}
533 		}
534 
535 		// update overview tree
536 		if (overview) {
537 			overview.setTreeView(ZmOrganizer.ZIMLET);
538 
539 			// HACK: for multi-account, hide the zimlet section if no panel zimlets
540 			if (appCtxt.multiAccounts && zimletMgr.getPanelZimlets().length == 0) {
541 				activeApp.getOverviewContainer().removeZimletSection();
542 			}
543 		}
544 
545 		// create global portlets
546 		if (appCtxt.get(ZmSetting.PORTAL_ENABLED)) {
547 			var portletMgr = appCtxt.getApp(ZmApp.PORTAL).getPortletMgr();
548 			var portletIds = portletMgr.createPortlets(true);
549 		}
550 	}
551 };
552 
553 /**
554  * Filters a list of zimlets, returned ones that are checked.
555  *
556  * @param zimlets			[array]		list of zimlet objects
557  *
558  * @private
559  */
560 ZmSettings.prototype._getCheckedZimlets =
561 function(allZimlets) {
562 
563 	var zimlets = [];
564 	for (var i = 0; i < allZimlets.length; i++) {
565 		var zimletObj = allZimlets[i];
566 		if (zimletObj.zimletContext[0].presence != "disabled") {
567 			zimlets.push(zimletObj);
568 		}
569 	}
570 
571 	return zimlets;
572 };
573 
574 /**
575  * Loads the preference data.
576  *
577  * @param	{AjxCallback}	callback		the callback
578  */
579 ZmSettings.prototype.loadPreferenceData =
580 function(callback) {
581 	// force main account (in case multi-account) since locale/skins are global
582 	var command = new ZmBatchCommand(null, appCtxt.accountList.mainAccount.name);
583 
584 	var skinDoc = AjxSoapDoc.create("GetAvailableSkinsRequest", "urn:zimbraAccount");
585 	var skinCallback = new AjxCallback(this, this._handleResponseLoadAvailableSkins);
586 	command.addNewRequestParams(skinDoc, skinCallback);
587 
588 	var localeDoc = AjxSoapDoc.create("GetAvailableLocalesRequest", "urn:zimbraAccount");
589 	var localeCallback = new AjxCallback(this, this._handleResponseGetAllLocales);
590 	command.addNewRequestParams(localeDoc, localeCallback);
591 
592 	var csvFormatsDoc = AjxSoapDoc.create("GetAvailableCsvFormatsRequest", "urn:zimbraAccount");
593 	var csvFormatsCallback = new AjxCallback(this, this._handleResponseGetAvailableCsvFormats);
594 	command.addNewRequestParams(csvFormatsDoc, csvFormatsCallback);
595 
596 	command.run(callback);
597 };
598 
599 /**
600  * @private
601  */
602 ZmSettings.prototype._handleResponseLoadAvailableSkins =
603 function(result) {
604 	var resp = result.getResponse().GetAvailableSkinsResponse;
605 	var skins = resp.skin;
606 	if (skins && skins.length) {
607 		var setting = appCtxt.accountList.mainAccount.settings.getSetting(ZmSetting.AVAILABLE_SKINS);
608 		for (var i = 0; i < skins.length; i++) {
609 			// always save available skins on the main account (in case multi-account)
610 			setting.setValue(skins[i].name);
611 		}
612 	}
613 };
614 
615 /**
616  * @private
617  */
618 ZmSettings.prototype._handleResponseGetAllLocales =
619 function(response) {
620 	var locales = response._data.GetAvailableLocalesResponse.locale;
621 	if (locales && locales.length) {
622 		for (var i = 0, count = locales.length; i < count; i++) {
623 			var locale = locales[i];
624 			// bug: 38038
625 			locale.id = locale.id.replace(/^in/,"id");
626 			ZmLocale.create(locale.id, locale.name, ZmMsg["localeName_" + locale.id] || locale.localName);
627 		}
628         if (locales.length === 1) {
629             //Fix for bug# 80762 - Set the value to always true in case of only one language/locale present
630             this.set(ZmSetting.LOCALE_CHANGE_ENABLED, true);
631         }
632         else {
633             this.set(ZmSetting.LOCALE_CHANGE_ENABLED, ZmLocale.hasChoices());
634         }
635 	}
636 };
637 
638 /**
639  * @private
640  */
641 ZmSettings.prototype._handleResponseGetAvailableCsvFormats =
642 function(result){
643 	var formats = result.getResponse().GetAvailableCsvFormatsResponse.csv;
644 	if (formats && formats.length) {
645 		var setting = appCtxt.accountList.mainAccount.settings.getSetting(ZmSetting.AVAILABLE_CSVFORMATS);
646 		for (var i = 0; i < formats.length; i++) {
647 			setting.setValue(formats[i].name);
648 		}
649 	}
650 };
651 
652 /**
653  * Saves one or more settings.
654  *
655  * @param {Array}		list			a list of {ZmSetting} objects
656  * @param {AjxCallback}	callback		the callback to run after response is received
657  * @param {ZmBatchCommand}	batchCommand	the batch command
658  * @param {ZmZimbraAccount}	account		the account to save under
659  * @param {boolean}			isImplicit	if true, we are saving implicit settings
660  */
661 ZmSettings.prototype.save =
662 function(list, callback, batchCommand, account, isImplicit) {
663 	if (!(list && list.length)) { return; }
664 
665 	var acct = account || appCtxt.getActiveAccount();
666 	var soapDoc = AjxSoapDoc.create("ModifyPrefsRequest", "urn:zimbraAccount");
667 	var gotOne = false;
668 	var metaData = [], done = {}, setting;
669 	for (var i = 0; i < list.length; i++) {
670 		setting = list[i];
671         if (done[setting.id]) { continue; }
672 		if (setting.type == ZmSetting.T_METADATA) {
673 			metaData.push(setting);
674 			// update the local meta data
675 			acct.metaData.update(setting.section, setting.name, setting.getValue());
676 			continue;
677 		} else if (setting.type != ZmSetting.T_PREF) {
678 			DBG.println(AjxDebug.DBG1, "*** Attempt to modify non-pref: " + setting.id + " / " + setting.name);
679 			continue;
680 		}
681 		if (!setting.name) {
682 			DBG.println(AjxDebug.DBG2, "Modify internal pref: " + setting.id);
683 			continue;
684 		}
685 		if (setting.dataType == ZmSetting.D_LIST) {
686 			// LDAP supports multi-valued attrs, so don't serialize list
687 			var value = setting.getValue();
688 			if (value && value.length) {
689 				for (var j = 0; j < value.length; j++) {
690 					var node = soapDoc.set("pref", value[j]);
691 					node.setAttribute("name", setting.name);
692 				}
693 			} else {
694 				var node = soapDoc.set("pref", "");
695 				node.setAttribute("name", setting.name);
696 			}
697 		} else {
698 			var value = setting.getValue(null, true);
699 			var node = soapDoc.set("pref", value);
700 			node.setAttribute("name", setting.name);
701 		}
702 
703         done[setting.id] = true;
704 		gotOne = true;
705 	}
706 
707     // bug: 50668 if the setting is implicit and global, use main Account
708     if(appCtxt.isOffline && ZmSetting.IS_IMPLICIT[setting.id] && ZmSetting.IS_GLOBAL[setting.id]) {
709         acct = appCtxt.accountList.mainAccount;
710     }
711 
712 	if (metaData.length > 0) {
713 		var metaDataCallback = new AjxCallback(this, this._handleResponseSaveMetaData, [metaData]);
714 		var sections = [ZmSetting.M_IMPLICIT, ZmSetting.M_OFFLINE];
715 		acct.metaData.save(sections, metaDataCallback);
716 	}
717 
718 	if (gotOne) {
719 		var respCallback;
720 		if (callback || batchCommand) {
721 			respCallback = new AjxCallback(this, this._handleResponseSave, [list, callback]);
722 		}
723 		if (batchCommand) {
724 			batchCommand.addNewRequestParams(soapDoc, respCallback);
725 		} else {
726 			appCtxt.getAppController().sendRequest({soapDoc:soapDoc, asyncMode:true, callback:respCallback,
727 			 										accountName:acct.name, noBusyOverlay:isImplicit});
728 		}
729 	}
730 };
731 
732 /**
733  * @private
734  */
735 ZmSettings.prototype._handleResponseSaveMetaData =
736 function(list, result) {
737 	for (var i = 0; i < list.length; i++) {
738 		var setting = list[i];
739 		if (!ZmSetting.IS_IMPLICIT[setting.id]) {
740 			setting.origValue = setting.copyValue();
741 			setting._notify(ZmEvent.E_MODIFY);
742 		}
743 	}
744 };
745 
746 /**
747  * @private
748  */
749 ZmSettings.prototype._handleResponseSave =
750 function(list, callback, result) {
751 	var resp = result.getResponse();
752 	if (resp.ModifyPrefsResponse != null) {
753 		// notify each changed setting's listeners
754 		for (var i = 0; i < list.length; i++) {
755 			var setting = list[i];
756 			setting.origValue = setting.copyValue();
757 			if (!ZmSetting.IS_IMPLICIT[setting.id]) {
758 				setting._notify(ZmEvent.E_MODIFY);
759 			}
760 		}
761 		// notify any listeners on the settings as a whole
762 		this._notify(ZmEvent.E_MODIFY, {settings:list});
763 	}
764 
765 	if (callback) {
766 		callback.run(result);
767 	}
768 };
769 
770 ZmSettings.DEFAULT_SORT_PREF = {};
771 
772 /**
773  * Set defaults which are determined dynamically (which can't be set in static code).
774  *
775  * @private
776  */
777 ZmSettings.prototype._setDefaults =
778 function() {
779 
780 	var value = AjxUtil.formatUrl({host:location.hostname, path:"/service/soap/", qsReset:true});
781 	this.set(ZmSetting.CSFE_SERVER_URI, value, null, false, true);
782 
783 	// CSFE_MSG_FETCHER_URI
784 	value = AjxUtil.formatUrl({host:location.hostname, path:"/service/home/~/", qsReset:true, qsArgs:{auth:"co"}});
785 	this.set(ZmSetting.CSFE_MSG_FETCHER_URI, value, null, false, true);
786 
787 	// CSFE_UPLOAD_URI
788 	value = AjxUtil.formatUrl({host:location.hostname, path:"/service/upload", qsReset:true, qsArgs:{lbfums:""}});
789 	this.set(ZmSetting.CSFE_UPLOAD_URI, value, null, false, true);
790 
791 	// CSFE_ATTACHMENT_UPLOAD_URI
792 	value = AjxUtil.formatUrl({host:location.hostname, path:"/service/upload", qsReset:true});
793 	this.set(ZmSetting.CSFE_ATTACHMENT_UPLOAD_URI, value, null, false, true);
794 
795 	// CSFE EXPORT URI
796 	value = AjxUtil.formatUrl({host:location.hostname, path:"/service/home/~/", qsReset:true, qsArgs:{auth:"co", id:"{0}", fmt:"csv"}});
797 	this.set(ZmSetting.CSFE_EXPORT_URI, value, null, false, true);
798 
799 	var h = location.hostname;
800 	var isDev = ((h.indexOf(".zimbra.com") != -1) || (window.appDevMode && (/\.local$/.test(h) || (!appCtxt.isOffline && h == "localhost"))));
801 	this.set(ZmSetting.IS_DEV_SERVER, isDev);
802 	if (isDev || window.isScriptErrorOn) {
803 		this.set(ZmSetting.SHOW_SCRIPT_ERRORS, true, null, false, true);
804 	}
805 
806 	this.setReportScriptErrorsSettings(AjxException, ZmController.handleScriptError);
807 };
808 
809 ZmSettings.prototype.persistImplicitSortPrefs =
810 function(id){
811     return ZmSettings.DEFAULT_SORT_PREF[id];
812 };
813 
814 /**
815  * sets AjxException static attributes. This is extracted so it can be called from ZmNewwindow as well.
816  * this is since the child window gets its own AjxException variable.
817  *
818  * @param AjxExceptionClassVar
819  * @param handler
820  */
821 ZmSettings.prototype.setReportScriptErrorsSettings =
822 function(AjxExceptionClassVar, handler) {
823 	// script error reporting
824 	var rse = AjxExceptionClassVar.reportScriptErrors = this._settings[ZmSetting.SHOW_SCRIPT_ERRORS].getValue();
825 	if (rse) {
826 		AjxExceptionClassVar.setScriptErrorHandler(handler);
827 	}
828 
829 };
830 
831 /**
832  * Loads the standard settings and their default values.
833  *
834  * @private
835  */
836 ZmSettings.prototype._initialize =
837 function() {
838 	// CONFIG SETTINGS
839     this.registerSetting("ADMIN_DELEGATED",                 {type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
840 	this.registerSetting("AC_TIMER_INTERVAL",				{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_INT, defaultValue:300});
841 	this.registerSetting("ASYNC_MODE",						{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
842 	this.registerSetting("BRANCH",							{type:ZmSetting.T_CONFIG, defaultValue:"JUDASPRIEST"});
843 
844 	// next 3 are replaced during deployment
845 	this.registerSetting("CLIENT_DATETIME",					{type:ZmSetting.T_CONFIG, defaultValue:"@buildDateTime@"});
846 	this.registerSetting("CLIENT_RELEASE",					{type:ZmSetting.T_CONFIG, defaultValue:"@buildRelease@"});
847 	this.registerSetting("CLIENT_VERSION",					{type:ZmSetting.T_CONFIG, defaultValue:"@buildVersion@"});
848 	this.registerSetting("CONFIG_PATH",						{type:ZmSetting.T_CONFIG, defaultValue:appContextPath + "/js/zimbraMail/config"});
849 	this.registerSetting("CSFE_EXPORT_URI",					{type:ZmSetting.T_CONFIG});
850 	this.registerSetting("CSFE_MSG_FETCHER_URI",			{type:ZmSetting.T_CONFIG});
851 	this.registerSetting("CSFE_SERVER_URI",					{type:ZmSetting.T_CONFIG});
852 	this.registerSetting("CSFE_UPLOAD_URI",					{type:ZmSetting.T_CONFIG});
853 	this.registerSetting("CSFE_ATTACHMENT_UPLOAD_URI",		{type:ZmSetting.T_CONFIG});
854 	this.registerSetting("DEV",								{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
855 	this.registerSetting("FORCE_CAL_OFF",					{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
856 	this.registerSetting("HELP_URI",						{type:ZmSetting.T_CONFIG, defaultValue:appContextPath + ZmMsg.helpURI});
857 	this.registerSetting("HTTP_PORT",						{type:ZmSetting.T_CONFIG, defaultValue:ZmSetting.HTTP_DEFAULT_PORT});
858 	this.registerSetting("HTTPS_PORT",						{type:ZmSetting.T_CONFIG, defaultValue:ZmSetting.HTTPS_DEFAULT_PORT});
859 	this.registerSetting("INSTANT_NOTIFY_INTERVAL",			{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_INT, defaultValue:500}); // milliseconds
860 	this.registerSetting("INSTANT_NOTIFY_TIMEOUT",			{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_INT, defaultValue:300}); // seconds
861 	this.registerSetting("IS_DEV_SERVER",					{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
862 	this.registerSetting("LOG_REQUEST",						{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
863 	this.registerSetting("LOGO_URI",						{type:ZmSetting.T_CONFIG, defaultValue:null});
864 	this.registerSetting("PROTOCOL_MODE",					{type:ZmSetting.T_CONFIG, defaultValue:ZmSetting.PROTO_HTTP});
865 	this.registerSetting("SERVER_VERSION",					{type:ZmSetting.T_CONFIG});
866 	this.registerSetting("SHOW_SCRIPT_ERRORS",				{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
867 	this.registerSetting("TIMEOUT",							{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_INT, defaultValue:30}); // seconds
868 	this.registerSetting("USE_XML",							{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
869 	this.registerSetting("SMIME_HELP_URI",						{type:ZmSetting.T_CONFIG, defaultValue:appContextPath + ZmMsg.smimeHelpURI});
870 
871 	// DOMAIN SETTINGS
872 	this.registerSetting("CHANGE_PASSWORD_URL",				{type:ZmSetting.T_CONFIG});
873 	this.registerSetting("PUBLIC_URL",						{type:ZmSetting.T_CONFIG});
874 	this.registerSetting("ADMIN_URL",						{type:ZmSetting.T_CONFIG});
875 	this.registerSetting("DISABLE_SENSITIVE_ZIMLETS_IN_MIXED_MODE",		{type:ZmSetting.T_CONFIG});
876 
877 	// COS SETTINGS - APPS
878 	this.registerSetting("BRIEFCASE_ENABLED",				{name:"zimbraFeatureBriefcasesEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
879 	this.registerSetting("ATTACHMENTS_BLOCKED",				{name:"zimbraAttachmentsBlocked", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
880 	this.registerSetting("CALENDAR_ENABLED",				{name:"zimbraFeatureCalendarEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
881 	this.registerSetting("CALENDAR_UPSELL_ENABLED",			{name:"zimbraFeatureCalendarUpsellEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
882 	this.registerSetting("CALENDAR_UPSELL_URL",				{name:"zimbraFeatureCalendarUpsellURL", type:ZmSetting.T_COS});
883 	this.registerSetting("CONTACTS_ENABLED",				{name:"zimbraFeatureContactsEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
884 	this.registerSetting("CONTACTS_UPSELL_ENABLED",			{name:"zimbraFeatureContactsUpsellEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
885 	this.registerSetting("CONTACTS_UPSELL_URL",				{name:"zimbraFeatureContactsUpsellURL", type:ZmSetting.T_COS});
886 	this.registerSetting("IMPORT_ENABLED",					{name:"zimbraFeatureImportFolderEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
887 	this.registerSetting("EXPORT_ENABLED",					{name:"zimbraFeatureExportFolderEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
888     this.registerSetting("MAIL_ENABLED",					{name:"zimbraFeatureMailEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
889     this.registerSetting("EXTERNAL_USER_MAIL_ADDRESS",		{name:"zimbraExternalUserMailAddress", type:ZmSetting.T_COS, dataType:ZmSetting.D_STRING});
890     this.registerSetting("ADMIN_MAIL_ENABLED",				{name:"zimbraFeatureAdminMailEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
891     this.registerSetting("ADMIN_PREFERENCES_ENABLED",		{name:"zimbraFeatureAdminPreferencesEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
892 	this.registerSetting("MAIL_UPSELL_ENABLED",				{name:"zimbraFeatureMailUpsellEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
893 	this.registerSetting("MAIL_UPSELL_URL",					{name:"zimbraFeatureMailUpsellURL", type:ZmSetting.T_COS});
894 	this.registerSetting("OPTIONS_ENABLED",					{name:"zimbraFeatureOptionsEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
895 	this.registerSetting("PORTAL_ENABLED",					{name:"zimbraFeaturePortalEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
896 	this.registerSetting("SOCIAL_ENABLED",					{name:"zimbraFeatureSocialEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
897 	this.registerSetting("SOCIAL_EXTERNAL_ENABLED",			{name:"zimbraFeatureSocialExternalEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
898 	this.registerSetting("SOCIAL_EXTERNAL_URL",				{name:"zimbraFeatureSocialExternalURL", type:ZmSetting.T_COS});
899 	this.registerSetting("SOCIAL_NAME",				        {name:"zimbraFeatureSocialName", type:ZmSetting.T_COS, defaultValue:ZmMsg.communityName});
900 	this.registerSetting("TASKS_ENABLED",					{name:"zimbraFeatureTasksEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
901 	this.registerSetting("VOICE_ENABLED",					{name:"zimbraFeatureVoiceEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
902 	this.registerSetting("VOICE_UPSELL_ENABLED",			{name:"zimbraFeatureVoiceUpsellEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
903 	this.registerSetting("VOICE_UPSELL_URL",				{name:"zimbraFeatureVoiceUpsellURL", type:ZmSetting.T_COS});
904 	this.registerSetting("DLS_FOLDER_ENABLED",				{name:"zimbraFeatureDistributionListFolderEnabled", type: ZmSetting.T_COS, dataType: ZmSetting.D_BOOLEAN, defaultValue: true});
905 
906 	// COS SETTINGS
907     this.registerSetting("ATTACHMENTS_VIEW_IN_HTML_ONLY",	{name:"zimbraAttachmentsViewInHtmlOnly", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
908 	this.registerSetting("AVAILABLE_SKINS",					{type:ZmSetting.T_COS, dataType:ZmSetting.D_LIST, isGlobal:true});
909 	this.registerSetting("AVAILABLE_CSVFORMATS",			{type:ZmSetting.T_COS, dataType:ZmSetting.D_LIST, isGlobal:true});
910 	this.registerSetting("CHANGE_PASSWORD_ENABLED",			{name:"zimbraFeatureChangePasswordEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
911 	this.registerSetting("DISPLAY_NAME",					{name:"displayName", type:ZmSetting.T_COS});
912 	this.registerSetting("DUMPSTER_ENABLED",				{name:"zimbraDumpsterEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
913 	this.registerSetting("ERROR_REPORT_URL",				{name:"zimbraErrorReportUrl", type:ZmSetting.T_COS, dataType:ZmSetting.D_STRING});
914 	this.registerSetting("EXPORT_MAX_DAYS",					{name:"zimbraExportMaxDays", type:ZmSetting.T_COS, dataType:ZmSetting.D_INT, defaultValue:0});
915 	this.registerSetting("FILTER_BATCH_SIZE",               {name:"zimbraFilterBatchSize", type:ZmSetting.T_COS, dataType:ZmSetting.D_INT, defaultValue: 10000});
916     this.registerSetting("FLAGGING_ENABLED",				{name:"zimbraFeatureFlaggingEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
917 	this.registerSetting("FOLDERS_EXPANDED",				{name:"zimbraPrefFoldersExpanded", type:ZmSetting.T_METADATA, dataType: ZmSetting.D_HASH, isImplicit:true, section:ZmSetting.M_IMPLICIT});
918 	this.registerSetting("FOLDER_TREE_OPEN",				{name:"zimbraPrefFolderTreeOpen", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, isImplicit:true});
919 	this.registerSetting("FOLDER_TREE_SASH_WIDTH",          {name:"zimbraPrefFolderTreeSash", type:ZmSetting.T_METADATA, dataType:ZmSetting.D_INT, isImplicit:true, section:ZmSetting.M_IMPLICIT});
920 	this.registerSetting("GAL_AUTOCOMPLETE_ENABLED",		{name:"zimbraFeatureGalAutoCompleteEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN,	defaultValue:false});
921 	this.registerSetting("GAL_ENABLED",						{name:"zimbraFeatureGalEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN,	defaultValue:true});
922 	this.registerSetting("GROUP_CALENDAR_ENABLED",			{name:"zimbraFeatureGroupCalendarEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
923 	this.registerSetting("HTML_COMPOSE_ENABLED",			{name:"zimbraFeatureHtmlComposeEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
924 	this.registerSetting("IDLE_SESSION_TIMEOUT",			{name:"zimbraMailIdleSessionTimeout", type:ZmSetting.T_COS, dataType:ZmSetting.D_LDAP_TIME, defaultValue:0});
925 	this.registerSetting("IMAP_ACCOUNTS_ENABLED",			{name:"zimbraFeatureImapDataSourceEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
926 	this.registerSetting("IMPORT_ON_LOGIN_ENABLED",			{name:"zimbraDataSourceImportOnLogin", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
927 	this.registerSetting("INSTANT_NOTIFY",					{name:"zimbraFeatureInstantNotify", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
928 	this.registerSetting("LOCALE_CHANGE_ENABLED",			{name:"zimbraFeatureLocaleChangeEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
929 	this.registerSetting("LOCALES",							{type:ZmSetting.T_COS, dataType:ZmSetting.D_LIST});
930 	this.registerSetting("LOGIN_URL",						{name:"zimbraWebClientLoginURL", type:ZmSetting.T_COS});
931 	this.registerSetting("LOGOUT_URL",						{name:"zimbraWebClientLogoutURL", type:ZmSetting.T_COS});
932 	this.registerSetting("MIN_POLLING_INTERVAL",			{name:"zimbraMailMinPollingInterval", type:ZmSetting.T_COS, dataType:ZmSetting.D_LDAP_TIME, defaultValue:120});
933 	this.registerSetting("MOBILE_SYNC_ENABLED",				{name:"zimbraFeatureMobileSyncEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
934 	this.registerSetting("MOBILE_POLICY_ENABLED",			{name:"zimbraFeatureMobilePolicyEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
935 	this.registerSetting("POP_ACCOUNTS_ENABLED",			{name:"zimbraFeaturePop3DataSourceEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
936 	this.registerSetting("PORTAL_NAME",						{name:"zimbraPortalName", type:ZmSetting.T_COS, defaultValue:"example"});
937 	this.registerSetting("PRIORITY_INBOX_ENABLED",          {name:"zimbraFeaturePriorityInboxEnabled", type:ZmSetting.T_COS, dataType: ZmSetting.D_BOOLEAN, defaultValue:true});
938 	this.registerSetting("PWD_MAX_LENGTH",					{name:"zimbraPasswordMaxLength", type:ZmSetting.T_COS, dataType:ZmSetting.D_INT, defaultValue:64});
939 	this.registerSetting("PWD_MIN_LENGTH",					{name:"zimbraPasswordMinLength", type:ZmSetting.T_COS, dataType:ZmSetting.D_INT, defaultValue:6});
940 	this.registerSetting("QUOTA",							{name:"zimbraMailQuota", type:ZmSetting.T_COS, dataType:ZmSetting.D_INT, defaultValue:0});
941 	this.registerSetting("SAVED_SEARCHES_ENABLED",			{name:"zimbraFeatureSavedSearchesEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
942 	this.registerSetting("SEARCH_TREE_OPEN",				{name:"zimbraPrefSearchTreeOpen", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, isImplicit:true});
943 	this.registerSetting("SHARING_ENABLED",					{name:"zimbraFeatureSharingEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
944 	this.registerSetting("SHARING_PUBLIC_ENABLED",			{name:"zimbraPublicSharingEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
945 	this.registerSetting("SHARING_EXTERNAL_ENABLED",		{name:"zimbraExternalSharingEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
946 	this.registerSetting("SHORTCUT_ALIASES_ENABLED",		{name:"zimbraFeatureShortcutAliasesEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
947 	this.registerSetting("SHOW_OFFLINE_LINK",				{name:"zimbraWebClientShowOfflineLink", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
948 	this.registerSetting("SIGNATURES_ENABLED",				{name:"zimbraFeatureSignaturesEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
949 	this.registerSetting("SKIN_CHANGE_ENABLED",				{name:"zimbraFeatureSkinChangeEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
950     this.registerSetting("SOCIAL_FILTERS_ENABLED",          {name:"zimbraFeatureSocialFiltersEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_LIST, isImplicit: true, section:ZmSetting.M_IMPLICIT});
951 	this.registerSetting("SPAM_ENABLED",					{name:"zimbraFeatureAntispamEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
952 	this.registerSetting("TAG_TREE_OPEN",					{name:"zimbraPrefTagTreeOpen", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, isImplicit:true});
953 	this.registerSetting("TAGGING_ENABLED",					{name:"zimbraFeatureTaggingEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
954 	this.registerSetting("VIEW_ATTACHMENT_AS_HTML",			{name:"zimbraFeatureViewInHtmlEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
955 	this.registerSetting("EXPAND_DL_ENABLED",				{name:"zimbraFeatureDistributionListExpandMembersEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
956 	this.registerSetting("FORCE_CLEAR_COOKIES",				{name:"zimbraForceClearCookies", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
957 	this.registerSetting("WEBCLIENT_OFFLINE_ENABLED",		{name:"zimbraFeatureWebClientOfflineAccessEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
958 	this.registerSetting("SPELL_DICTIONARY",                                {name:"zimbraPrefSpellDictionary", type:ZmSetting.T_COS, defaultValue:""});
959 	this.registerSetting("TWO_FACTOR_AUTH_AVAILABLE",	    {name:"zimbraFeatureTwoFactorAuthAvailable", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
960 	this.registerSetting("TWO_FACTOR_AUTH_REQUIRED",	    {name:"zimbraFeatureTwoFactorAuthRequired", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
961 	this.registerSetting("TRUSTED_DEVICES_ENABLED",         {name:"zimbraFeatureTrustedDevicesEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
962 	this.registerSetting("APP_PASSWORDS_ENABLED",	        {name:"zimbraFeatureAppSpecificPasswordsEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
963 
964 	// user metadata (included with COS since the user can't change them)
965 	this.registerSetting("LICENSE_STATUS",					{type:ZmSetting.T_COS, defaultValue:ZmSetting.LICENSE_GOOD});
966 	this.registerSetting("QUOTA_USED",						{type:ZmSetting.T_COS, dataType:ZmSetting.D_INT});    
967 	this.registerSetting("USERID",							{name:"zimbraId", type:ZmSetting.T_COS});
968 	this.registerSetting("USERNAME",						{type:ZmSetting.T_COS});
969 	this.registerSetting("CN",								{name:"cn", type:ZmSetting.T_COS});
970 	this.registerSetting("LAST_ACCESS",						{type:ZmSetting.T_COS, dataType:ZmSetting.D_INT});
971 	this.registerSetting("PREVIOUS_SESSION",				{type:ZmSetting.T_COS, dataType:ZmSetting.D_INT});
972 	this.registerSetting("RECENT_MESSAGES",					{type:ZmSetting.T_COS, dataType:ZmSetting.D_INT});
973 	this.registerSetting("REST_URL",						{name:"rest" , type:ZmSetting.T_COS});
974 	this.registerSetting("IS_ADMIN",						{name:"zimbraIsAdminAccount", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue: false});
975 	this.registerSetting("IS_EXTERNAL",						{name:"zimbraIsExternalVirtualAccount", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue: false});
976 	this.registerSetting("IS_DELEGATED_ADMIN",				{name:"zimbraIsDelegatedAdminAccount", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue: false});
977     this.registerSetting("MESSAGE_SIZE_LIMIT",              {type:ZmSetting.T_COS, dataType:ZmSetting.D_INT});
978     this.registerSetting("DOCUMENT_SIZE_LIMIT",             {type:ZmSetting.T_COS, dataType:ZmSetting.D_INT});
979 
980 	// CLIENT SIDE FEATURE SUPPORT
981 	this.registerSetting("ATTACHMENT_ENABLED",				{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
982 	this.registerSetting("ATT_VIEW_ENABLED",				{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
983 	this.registerSetting("BROWSER_TOOLTIPS_ENABLED",		{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
984 	this.registerSetting("EVAL_ENABLED",					{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
985 	this.registerSetting("FEED_ENABLED",					{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
986 	this.registerSetting("HELP_ENABLED",					{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
987 	this.registerSetting("HISTORY_SUPPORT_ENABLED",			{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:("onhashchange" in window)});
988 	this.registerSetting("NOTES_ENABLED",					{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
989 	this.registerSetting("PRINT_ENABLED",					{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
990 	this.registerSetting("SEARCH_ENABLED",					{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
991 	this.registerSetting("SHORTCUT_LIST_ENABLED",			{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
992 	this.registerSetting("OFFLINE_ENABLED",					{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:appCtxt.isOffline});
993 	this.registerSetting("SPELL_CHECK_ENABLED",				{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:!appCtxt.isOffline && (!AjxEnv.isSafari || AjxEnv.isSafari3up || AjxEnv.isChrome)});
994 	this.registerSetting("SPELL_CHECK_ADD_WORD_ENABLED",	{type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:!AjxEnv.isSafari || AjxEnv.isSafari3up || AjxEnv.isChrome});
995 
996 	//SETTINGS SET AT DOMAIN LEVEL
997 	this.registerSetting("EMAIL_VALIDATION_REGEX",			{name:"zimbraMailAddressValidationRegex", type:ZmSetting.T_DOMAIN, dataType:ZmSetting.D_LIST});
998 	this.registerSetting("SUPPORTED_HELPS",					{name:"zimbraWebClientSupportedHelps", type:ZmSetting.T_DOMAIN, dataType:ZmSetting.D_LIST});
999 
1000 	// USER PREFERENCES (mutable)
1001 
1002 	// general preferences
1003 	this.registerSetting("ACCOUNTS",						{type: ZmSetting.T_PREF, dataType: ZmSetting.D_HASH});
1004 	this.registerSetting("TWO_FACTOR_AUTH_ENABLED",	        {name:"zimbraTwoFactorAuthEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
1005 	this.registerSetting("ACCOUNT_TREE_OPEN",				{name:"zimbraPrefAccountTreeOpen", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isImplicit:true});
1006 	this.registerSetting("CHILD_ACCTS_VISIBLE",				{name:"zimbraPrefChildVisibleAccount", type:ZmSetting.T_PREF, dataType:ZmSetting.D_LIST});
1007 	this.registerSetting("CLIENT_TYPE",						{name:"zimbraPrefClientType", type:ZmSetting.T_PREF, defaultValue:ZmSetting.CLIENT_ADVANCED});
1008 	this.registerSetting("COMPOSE_AS_FORMAT",				{name:"zimbraPrefComposeFormat", type:ZmSetting.T_PREF, defaultValue:ZmSetting.COMPOSE_HTML, isGlobal:true});
1009 	this.registerSetting("COMPOSE_INIT_FONT_COLOR",			{name:"zimbraPrefHtmlEditorDefaultFontColor", type:ZmSetting.T_PREF, defaultValue:ZmSetting.COMPOSE_FONT_COLOR, isGlobal:true});
1010 	this.registerSetting("COMPOSE_INIT_FONT_FAMILY",		{name:"zimbraPrefHtmlEditorDefaultFontFamily", type:ZmSetting.T_PREF, defaultValue:ZmSetting.COMPOSE_FONT_FAM, isGlobal:true});
1011 	this.registerSetting("COMPOSE_INIT_FONT_SIZE",			{name:"zimbraPrefHtmlEditorDefaultFontSize", type:ZmSetting.T_PREF, defaultValue:ZmSetting.COMPOSE_FONT_SIZE, isGlobal:true});
1012     this.registerSetting("COMPOSE_INIT_DIRECTION",			{name:"zimbraPrefComposeDirection", type:ZmSetting.T_PREF, defaultValue:ZmSetting.LTR, isGlobal:true});
1013     this.registerSetting("SHOW_COMPOSE_DIRECTION_BUTTONS",	{name:"zimbraPrefShowComposeDirection", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true});
1014 	this.registerSetting("DEFAULT_TIMEZONE",				{name:"zimbraPrefTimeZoneId", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, defaultValue:AjxTimezone.getServerId(AjxTimezone.DEFAULT), isGlobal:true});
1015     this.registerSetting("WEBCLIENT_OFFLINE_BROWSER_KEY",	{name:"zimbraPrefWebClientOfflineBrowserKey", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, isImplicit:true});
1016     this.registerSetting("DEFAULT_PRINTFONTSIZE",	    	{name:"zimbraPrefDefaultPrintFontSize", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, defaultValue:ZmSetting.PRINT_FONT_SIZE, isGlobal:true});
1017 	this.registerSetting("GROUPBY_HASH",                    {type: ZmSetting.T_PREF, dataType:ZmSetting.D_HASH});
1018 	this.registerSetting("GROUPBY_LIST",                    {name:"zimbraPrefGroupByList", type:ZmSetting.T_METADATA, dataType:ZmSetting.D_HASH, isImplicit:true, section:ZmSetting.M_IMPLICIT});
1019     this.registerSetting("FILTERS",							{type: ZmSetting.T_PREF, dataType: ZmSetting.D_HASH});
1020 	this.registerSetting("FONT_NAME",						{name:"zimbraPrefFont", type:ZmSetting.T_PREF, defaultValue: ZmSetting.FONT_SYSTEM, isGlobal:true});
1021 	this.registerSetting("FONT_SIZE",						{name:"zimbraPrefFontSize", type:ZmSetting.T_PREF, defaultValue: ZmSetting.FONT_SIZE_NORMAL, isGlobal:true});
1022 	this.registerSetting("IDENTITIES",						{type: ZmSetting.T_PREF, dataType: ZmSetting.D_HASH});
1023 	this.registerSetting("INITIALLY_SEARCH_GAL",			{name:"zimbraPrefGalSearchEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
1024 	this.registerSetting("LIST_VIEW_COLUMNS",				{name:"zimbraPrefListViewColumns", type:ZmSetting.T_PREF, dataType:ZmSetting.D_HASH, isImplicit:true});
1025 	this.registerSetting("LOCALE_NAME",						{name:"zimbraPrefLocale", type:ZmSetting.T_PREF, defaultValue:appRequestLocaleId, isGlobal:true});
1026 	this.registerSetting("SHOW_SELECTION_CHECKBOX",			{name:"zimbraPrefShowSelectionCheckbox", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, isGlobal:true});
1027 	// PAGE_SIZE: number of items to fetch for virtual paging; also used for number of msgs in one page of a conv
1028 	this.registerSetting("PAGE_SIZE",						{name: "zimbraPrefItemsPerVirtualPage", type:ZmSetting.T_PREF, dataType:ZmSetting.D_INT, defaultValue:50, isGlobal:true});
1029 	this.registerSetting("PASSWORD",						{type:ZmSetting.T_PREF, dataType:ZmSetting.D_NONE});
1030 	this.registerSetting("POLLING_INTERVAL",				{name:"zimbraPrefMailPollingInterval", type:ZmSetting.T_PREF, dataType:ZmSetting.D_LDAP_TIME, defaultValue:300});
1031 	this.registerSetting("POLLING_INTERVAL_ENABLED",		{name:"zimbraFeatureMailPollingIntervalPreferenceEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
1032 	this.registerSetting("SEARCH_INCLUDES_SHARED",			{name:"zimbraPrefIncludeSharedItemsInSearch", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true});
1033 	this.registerSetting("SEARCH_INCLUDES_SPAM",			{name:"zimbraPrefIncludeSpamInSearch", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true});
1034 	this.registerSetting("SEARCH_INCLUDES_TRASH",			{name:"zimbraPrefIncludeTrashInSearch", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true});
1035 	this.registerSetting("SHORT_ADDRESS",					{name:"zimbraPrefShortEmailAddress", type:ZmSetting.T_PREF, dataType: ZmSetting.D_BOOLEAN, defaultValue: true});
1036 	this.registerSetting("SHORTCUTS",						{name:"zimbraPrefShortcuts", type:ZmSetting.T_PREF});
1037 	this.registerSetting("SHOW_SEARCH_STRING",				{name:"zimbraPrefShowSearchString", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true});
1038 	this.registerSetting("SIGNATURES",						{type: ZmSetting.T_PREF, dataType: ZmSetting.D_HASH});
1039 	this.registerSetting("SIGNATURES_MAX",					{name:"zimbraSignatureMaxNumEntries", type:ZmSetting.T_COS, dataType:ZmSetting.D_INT, defaultValue:20});
1040 	this.registerSetting("SIGNATURES_MIN",					{name:"zimbraSignatureMinNumEntries", type:ZmSetting.T_COS, dataType:ZmSetting.D_INT, defaultValue:1});
1041 	this.registerSetting("SKIN_NAME",						{name:"zimbraPrefSkin", type:ZmSetting.T_PREF, defaultValue:"skin", isGlobal:true});
1042 	this.registerSetting("SORTING_PREF",					{name:"zimbraPrefSortOrder", type:ZmSetting.T_PREF, dataType:ZmSetting.D_HASH, isImplicit:true, isGlobal:true, dontSaveDefault: true});
1043 	this.registerSetting("USE_KEYBOARD_SHORTCUTS",			{name:"zimbraPrefUseKeyboardShortcuts", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
1044 	this.registerSetting("VIEW_AS_HTML",					{name:"zimbraPrefMessageViewHtmlPreferred", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true});
1045 	this.registerSetting("VOICE_ACCOUNTS",					{type: ZmSetting.T_PREF, dataType: ZmSetting.D_HASH});
1046 	this.registerSetting("WARN_ON_EXIT",					{name:"zimbraPrefWarnOnExit", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
1047 
1048 	this._registerOfflineSettings();
1049 	this._registerZimletsSettings();
1050 
1051 	// need to do this before loadUserSettings(), and zimlet settings are not tied to an app where it would normally be done
1052 	this.registerSetting("ZIMLET_TREE_OPEN",				{name:"zimbraPrefZimletTreeOpen", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isImplicit:true});
1053 	
1054 	//shared settings
1055 	this.registerSetting("MAIL_ALIASES",					{name:"zimbraMailAlias", type:ZmSetting.T_COS, dataType:ZmSetting.D_LIST});
1056 	this.registerSetting("ALLOW_FROM_ADDRESSES",			{name:"zimbraAllowFromAddress", type:ZmSetting.T_COS, dataType:ZmSetting.D_LIST});
1057 
1058 	// Internal pref to control display of mail-related preferences. The only time it will be false is for a delegated admin with zimbraFeatureAdminMailEnabled and
1059 	// zimbraFeatureAdminPreferencesEnabled set to FALSE.
1060 	this.registerSetting("MAIL_PREFERENCES_ENABLED",	    {type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
1061 
1062 	//chat settings
1063 	this.registerSetting("CHAT_PLAY_SOUND",					{name:"zimbraPrefChatPlaySound", type: ZmSetting.T_PREF, dataType: ZmSetting.D_BOOLEAN, defaultValue:true, isGlobal:true});
1064 	this.registerSetting("CHAT_FEATURE_ENABLED",				{name:"zimbraFeatureChatEnabled", type: ZmSetting.T_COS, dataType: ZmSetting.D_BOOLEAN, defaultValue:true, isGlobal:true});
1065 	this.registerSetting("CHAT_ENABLED",				{name:"zimbraPrefChatEnabled", type: ZmSetting.T_PREF, dataType: ZmSetting.D_BOOLEAN, defaultValue:true, isGlobal:true});
1066 
1067 	ZmApp.runAppFunction("registerSettings", this);
1068 };
1069 
1070 /**
1071  * @private
1072  */
1073 ZmSettings.prototype._registerZimletsSettings =
1074 function() {
1075 	// zimlet-specific
1076 	this.registerSetting("CHECKED_ZIMLETS_ENABLED",			{name:"zimbraFeatureManageZimlets", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, isGlobal:true});
1077 	this.registerSetting("CHECKED_ZIMLETS",					{name:"zimbraPrefZimlets", type:ZmSetting.T_PREF, dataType:ZmSetting.D_LIST, isGlobal:true});
1078     this.registerSetting("MANDATORY_ZIMLETS",		        {name:"zimbraZimletMandatoryZimlets", type:ZmSetting.T_COS, dataType:ZmSetting.D_LIST});
1079     this.registerSetting("ZIMLETS_SYNCHRONOUS",		        {name:"zimbraZimletLoadSynchronously", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
1080 
1081 };
1082 
1083 /**
1084  * @private
1085  */
1086 ZmSettings.prototype._registerOfflineSettings =
1087 function() {
1088 	if (!appCtxt.isOffline) { return; }
1089 
1090 	// offline-specific
1091 	this.registerSetting("OFFLINE_ACCOUNT_FLAVOR",			{name:"offlineAccountFlavor", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING});
1092 	this.registerSetting("OFFLINE_COMPOSE_ENABLED",			{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true});
1093 	this.registerSetting("OFFLINE_DEBUG_TRACE",				{type:ZmSetting.T_CONFIG, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
1094 	this.registerSetting("OFFLINE_IS_MAILTO_HANDLER",		{name:"zimbraPrefMailtoHandlerEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true});
1095 	this.registerSetting("OFFLINE_REMOTE_SERVER_URI",		{name:"offlineRemoteServerUri", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING});
1096 	this.registerSetting("OFFLINE_REPORT_EMAIL",			{type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, defaultValue:"zdesktop-report@zimbra.com", isGlobal:true});
1097 	this.registerSetting("OFFLINE_SHOW_ALL_MAILBOXES",		{name:"offlineShowAllMailboxes", type:ZmSetting.T_METADATA, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, section:ZmSetting.M_OFFLINE, isGlobal:true});
1098 	this.registerSetting("OFFLINE_ALL_MAILBOXES_TREE_OPEN",	{name:"offlineAllMailboxesTreeOpen", type:ZmSetting.T_METADATA, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, section:ZmSetting.M_OFFLINE, isGlobal:true, isImplicit:true});
1099 	this.registerSetting("OFFLINE_NOTIFY_NEWMAIL_ON_INBOX",	{name:"offlineNotifyNewMailOnInbox", type:ZmSetting.T_METADATA, dataType:ZmSetting.D_BOOLEAN, defaultValue:true, section:ZmSetting.M_OFFLINE, isGlobal:true});
1100 	this.registerSetting("OFFLINE_SAVED_SEARCHES_TREE_OPEN",{name:"offlineSavedSearchesTreeOpen", type:ZmSetting.T_METADATA, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, section:ZmSetting.M_OFFLINE, isGlobal:true, isImplicit:true});
1101 	this.registerSetting("OFFLINE_SMTP_ENABLED",			{name:"zimbraDataSourceSmtpEnabled", type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:true});
1102 	this.registerSetting("OFFLINE_SUPPORTS_MAILTO",			{type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true});
1103 	this.registerSetting("OFFLINE_SUPPORTS_DOCK_UPDATE",	{type:ZmSetting.T_PREF, dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true});
1104 	this.registerSetting("OFFLINE_WEBAPP_URI",				{name:"offlineWebappUri", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING});
1105     this.registerSetting("OFFLINE_BACKUP_INTERVAL",	        {name:"zimbraPrefOfflineBackupInterval", type:ZmSetting.T_PREF, dataType:ZmSetting.D_INT, defaultValue:0, isGlobal:true});
1106     this.registerSetting("OFFLINE_BACKUP_PATH",	            {name:"zimbraPrefOfflineBackupPath", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, isGlobal:true});
1107     this.registerSetting("OFFLINE_BACKUP_KEEP",	            {name:"zimbraPrefOfflineBackupKeep", type:ZmSetting.T_PREF, dataType:ZmSetting.D_INT, isGlobal:true});
1108     this.registerSetting("OFFLINE_BACKUP_ACCOUNT_ID",       {name:"zimbraPrefOfflineBackupAccountId", type:ZmSetting.T_PREF, dataType:ZmSetting.D_INT, isGlobal:true});
1109     this.registerSetting("OFFLINE_BACKUP_RESTORE",          {name:"zimbraPrefOfflineBackupRestore", dataType:ZmSetting.D_INT, isGlobal:true});
1110     this.registerSetting("OFFLINE_BACKUP_NOW_BUTTON",       {name:"zimbraPrefOfflineBackupAccount", dataType:ZmSetting.D_BOOLEAN, defaultValue:false, isGlobal:true});
1111     this.registerSetting("OFFLINE_ZIMLET_SYNC_ACCOUNT_ID",  {name:"zimbraPrefOfflineZimletSyncAccountId", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING, isGlobal:true});
1112 	this.registerSetting("OFFLINE_WEBAPP_URI",				{name:"offlineWebappUri", type:ZmSetting.T_PREF, dataType:ZmSetting.D_STRING});
1113 
1114 	// reset the help URI to zimbra.com for offline
1115 	this.registerSetting("HELP_URI",						{type:ZmSetting.T_CONFIG, defaultValue:"https://www.zimbra.com/desktop7/"});
1116 //	// make default false for DUMPSTER_ENABLED. shouldn't be necessary since GetInfoResponse includes zimbraDumpsterEnabled:"FALSE", but can't find why settings is not read correctly
1117 	this.registerSetting("DUMPSTER_ENABLED",				{name:"zimbraDumpsterEnabled", type:ZmSetting.T_COS, dataType:ZmSetting.D_BOOLEAN, defaultValue:false});
1118     this.registerSetting("OFFLINE_UPDATE_NOTIFY",			{name:"zimbraPrefOfflineUpdateChannel", type: ZmSetting.T_PREF, dataType: ZmSetting.D_STRING, isGlobal:true});
1119 };
1120 
1121 /**
1122  * @private
1123  */
1124 ZmSettings.prototype._changeListener =
1125 function(ev) {
1126 
1127 	if (ev.type !== ZmEvent.S_SETTING) {
1128 		return;
1129 	}
1130 
1131 	var id = ev.source.id,
1132 		value = ev.source.getValue();
1133 
1134 	if (id === ZmSetting.QUOTA_USED) {
1135 		appCtxt.getAppController().setUserInfo();
1136 	}
1137 	else if (id === ZmSetting.POLLING_INTERVAL) {
1138 		appCtxt.getAppController().setPollInterval();
1139 	}
1140 	else if (id === ZmSetting.SKIN_NAME) {
1141 		this._showConfirmDialog(ZmMsg.skinChangeRestart, this._refreshBrowserCallback.bind(this));
1142 	}
1143 	else if (id === ZmSetting.SHOW_SELECTION_CHECKBOX) {
1144 		this._showConfirmDialog(value ? ZmMsg.checkboxChangeRestartShow : ZmMsg.checkboxChangeRestartHide, this._refreshBrowserCallback.bind(this));
1145 	}
1146 	else if (id === ZmSetting.FONT_NAME || id === ZmSetting.FONT_SIZE) {
1147 		this._showConfirmDialog(ZmMsg.fontChangeRestart, this._refreshBrowserCallback.bind(this));
1148 	}
1149 	else if (id === ZmSetting.LOCALE_NAME) {
1150 		// bug: 29786
1151 		if (appCtxt.isOffline && AjxEnv.isPrism) {
1152 			try {
1153 				netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
1154 				var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
1155 				if (prefs) {
1156 					var newLocale = appCtxt.get(ZmSetting.LOCALE_NAME).replace("_", "-");
1157 					prefs.setCharPref("general.useragent.locale", newLocale);
1158 					prefs.setCharPref("intl.accept_languages", newLocale);
1159 				}
1160 			} catch (ex) {
1161 				// do nothing for now
1162 			}
1163 		}
1164 		this._showConfirmDialog(ZmMsg.localeChangeRestart, this._refreshBrowserCallback.bind(this));
1165 	}
1166 	else if (id === ZmSetting.CHILD_ACCTS_VISIBLE) {
1167 		this._showConfirmDialog(ZmMsg.accountChangeRestart, this._refreshBrowserCallback.bind(this));
1168 	}
1169 	else if (appCtxt.isOffline && id === ZmSetting.OFFLINE_IS_MAILTO_HANDLER) {
1170 		appCtxt.getAppController().registerMailtoHandler(true, ev.source.getValue());
1171 	}
1172 	else if (appCtxt.isOffline && id === ZmSetting.OFFLINE_UPDATE_NOTIFY) {
1173 		appCtxt.getAppController()._offlineUpdateChannelPref(ev.source.getValue());
1174 	}
1175     else if (id === ZmSetting.CHAT_ENABLED || id === ZmSetting.CHAT_PLAY_SOUND || (id === ZmSetting.CHAT_PLAY_SOUND && id === ZmSetting.CHAT_ENABLED)) {
1176 		this._showConfirmDialog(ZmMsg.chatFeatureChangeRestart, this._refreshBrowserCallback.bind(this));
1177 	}
1178 };
1179 
1180 // Shows a confirm dialog that asks the user if they want to reload ZCS to show the change they just made
1181 ZmSettings.prototype._showConfirmDialog = function(text, callback, style) {
1182 
1183 	var confirmDialog = appCtxt.getYesNoMsgDialog();
1184 	confirmDialog.reset();
1185 	confirmDialog.registerCallback(DwtDialog.YES_BUTTON, callback);
1186 	confirmDialog.setMessage(text, style || DwtMessageDialog.WARNING_STYLE);
1187 	confirmDialog.popup();
1188 };
1189 
1190 ZmSettings.prototype._implicitChangeListener =
1191 function(ev) {
1192 	if (!appCtxt.get(ZmSetting.OPTIONS_ENABLED)) {
1193 		return;
1194 	}
1195 	if (ev.type != ZmEvent.S_SETTING) { return; }
1196 	var id = ev.source.id;
1197 	var setting = this.getSetting(id);
1198 	if (id == ZmSetting.FOLDERS_EXPANDED && window.duringExpandAll) {
1199 		if (!window.afterExpandAllCallback) {
1200 			window.afterExpandAllCallback = this.save.bind(this, [setting], null, null, appCtxt.getActiveAccount(), true);
1201 		}
1202 		return;
1203 	}
1204 	if (ZmSetting.IS_IMPLICIT[id] && setting) {
1205         if (id === ZmSetting.WEBCLIENT_OFFLINE_BROWSER_KEY) {
1206             var callback = this._offlineSettingsSaveCallback.bind(this, setting);
1207         }
1208 		else {
1209 			//Once implicit preference is saved, reload the application cache to get the latest changes
1210 			var callback = appCtxt.reloadAppCache.bind(appCtxt, false);
1211 		}
1212 		this.save([setting], callback, null, appCtxt.getActiveAccount(), true);
1213 	}
1214 };
1215 
1216 /**
1217  * @private
1218  */
1219 ZmSettings.prototype._refreshBrowserCallback =
1220 function(args) {
1221 	appCtxt.getYesNoMsgDialog().popdown();
1222 	window.onbeforeunload = ZmZimbraMail.getConfirmExitMethod();
1223 	var url = AjxUtil.formatUrl({qsArgs : args});
1224 	window.location.replace(url);
1225 };
1226 
1227 // Adds/replaces a CSS rule that comprises user font prefs
1228 ZmSettings.prototype._updateUserFontPrefsRule =
1229 function() {
1230 	if (this._userFontPrefsRuleIndex != null) {
1231 		DwtCssStyle.removeRule(document.styleSheets[0], this._userFontPrefsRuleIndex);
1232 	}
1233 	var selector = "." + ZmSetting.USER_FONT_CLASS;
1234 	var declaration = "font-family:" + appCtxt.get(ZmSetting.COMPOSE_INIT_FONT_FAMILY) + ";" +
1235 					  "font-size:" + appCtxt.get(ZmSetting.COMPOSE_INIT_FONT_SIZE) + ";" +
1236 					  "color:" + appCtxt.get(ZmSetting.COMPOSE_INIT_FONT_COLOR) + ";";
1237 	this._userFontPrefsRuleIndex = DwtCssStyle.addRule(document.styleSheets[0], selector, declaration);
1238 };
1239 
1240 // Check license to see if voice feature is allowed
1241 // License block format:
1242 //
1243 //  <license status="OK">
1244 //    <attr name="SMIME">FALSE</attr>
1245 //    <attr name="VOICE">TRUE</attr>
1246 //  </license>
1247 
1248 ZmSettings.prototype._hasVoiceFeature = function() {
1249 
1250     var info = this.getInfoResponse;
1251     var license = info && info.license;
1252     var status = license && license.status;
1253 
1254     if (!license || !license.attr) {
1255         return false;
1256     }
1257 
1258     // License not installed or not activated or expired
1259     if (ZmSetting.LICENSE_MSG[status]) {
1260         return false;
1261     }
1262 
1263     // check for VOICE license attribute
1264 
1265     for (var i = 0; license && i < license.attr.length; i++) {
1266         var attr = license.attr[i];
1267 
1268         if (attr.name == "VOICE") {
1269             return attr._content == "TRUE";
1270         }
1271     }
1272 
1273     return false;
1274 };
1275 
1276 /**
1277  * @private
1278  */
1279 ZmSettings.prototype._offlineSettingsSaveCallback =
1280 function(setting) {
1281 	var offlineBrowserKey = setting.getValue();
1282 	var localOfflineBrowserKey = localStorage.getItem(ZmSetting.WEBCLIENT_OFFLINE_BROWSER_KEY);
1283 	if (offlineBrowserKey && offlineBrowserKey.indexOf(localOfflineBrowserKey) !== -1) {
1284 		this._showConfirmDialog(ZmMsg.offlineChangeRestart, this._refreshBrowserCallback.bind(this));
1285     }
1286     else {
1287         ZmOffline.deleteOfflineData();
1288         appCtxt.initWebOffline();// To reset the property isWebClientOfflineSupported
1289 	    //delete outbox folder
1290 	    var outboxFolder = appCtxt.getById(ZmFolder.ID_OUTBOX);
1291 	    if (outboxFolder) {
1292 		    outboxFolder.notifyDelete();
1293 	    }
1294     }
1295 	//Always reload appcache whenever offline setting is enabled/disabled. Appcache will be updated/emptied depending upon the setting.
1296 	appCtxt.reloadAppCache(true);
1297 };
1298