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  */
 27 
 28 /**
 29  * Creates a share properties dialog.
 30  * @class
 31  * This class represents a share properties dialog.
 32  * 
 33  * @param	{DwtComposite}	shell		the parent
 34  * @param	{String}	className		the class name
 35  *  
 36  * @extends		DwtDialog
 37  */
 38 ZmSharePropsDialog = function(shell, className) {
 39 	className = className || "ZmSharePropsDialog";
 40 	DwtDialog.call(this, {parent:shell, className:className, title:ZmMsg.shareProperties, id:"ShareDialog"});
 41 	this.setButtonListener(DwtDialog.OK_BUTTON, new AjxListener(this, this._handleOkButton));
 42 
 43 	var aifParams = {
 44 		parent:		this,
 45 		inputId:	"ShareDialog_grantee"
 46 	}
 47 
 48 	this._grantee = new ZmAddressInputField(aifParams);
 49 	this._grantee.setData(Dwt.KEY_OBJECT, this);
 50 	Dwt.associateElementWithObject(this._grantee, this);
 51 
 52 	this._granteeInput = this._grantee.getInputElement();
 53 	this._granteeInputId = this._grantee._htmlElId;
 54 	Dwt.associateElementWithObject(this._granteeInput, this);
 55 
 56 	// create auto-completer
 57 	if (appCtxt.get(ZmSetting.CONTACTS_ENABLED) || appCtxt.get(ZmSetting.GAL_ENABLED)) {
 58 		var params = {
 59 			dataClass:		appCtxt.getAutocompleter(),
 60 			options:		{massDLComplete:true},
 61 			matchValue:		ZmAutocomplete.AC_VALUE_EMAIL,
 62 			keyUpCallback:	this._acKeyUpListener.bind(this),
 63 			contextId:		this.toString()
 64 		};
 65 		this._acAddrSelectList = new ZmAutocompleteListView(params);
 66 		this._acAddrSelectList.handle(this._granteeInput, this._granteeInputId);
 67 		this._grantee.setAutocompleteListView(this._acAddrSelectList);
 68 	}
 69 
 70 	// set view
 71 	this.setView(this._createView());
 72 };
 73 
 74 ZmSharePropsDialog.prototype = new DwtDialog;
 75 ZmSharePropsDialog.prototype.constructor = ZmSharePropsDialog;
 76 
 77 ZmSharePropsDialog.prototype.isZmSharePropsDialog = true;
 78 ZmSharePropsDialog.prototype.toString = function() { return "ZmSharePropsDialog"; };
 79 
 80 // Constants
 81 
 82 
 83 // modes
 84 ZmSharePropsDialog.NEW	= ZmShare.NEW;
 85 ZmSharePropsDialog.EDIT	= ZmShare.EDIT;
 86 
 87 // roles
 88 ZmSharePropsDialog.SHARE_WITH = [ 'user', 'external', 'public' ];
 89 ZmSharePropsDialog.SHARE_WITH_MSG = {
 90 	user:       ZmMsg.shareWithUserOrGroup,
 91 	external:   ZmMsg.shareWithGuest,
 92 	'public':     ZmMsg.shareWithPublicLong
 93 };
 94 ZmSharePropsDialog.SHARE_WITH_TYPE = {
 95 	user:       ZmShare.TYPE_USER,
 96 	external:   ZmShare.TYPE_GUEST,
 97 	'public':     ZmShare.TYPE_PUBLIC
 98 };
 99 
100 
101 // Data
102 
103 ZmSharePropsDialog.prototype._mode = ZmSharePropsDialog.NEW;
104 
105 
106 // Public methods
107 
108 
109 /**
110  * Pops-up the dialog.
111  * 
112  * @param	{constant}	mode		the mode
113  * @param	{ZmOrganizer}	object	the organizer object
114  * @param	{ZmShare}	share		the share
115  */
116 ZmSharePropsDialog.prototype.popup =
117 function(mode, object, share) {
118 
119 	this._shareMode = mode;
120 	this._object = object;
121 	this._share = share;
122 
123 	this._nameEl.innerHTML = AjxStringUtil.htmlEncode(object.name);
124 	this._typeEl.innerHTML = ZmMsg[ZmOrganizer.FOLDER_KEY[this._object.type]] || ZmMsg.folder;
125 	// TODO: False until server handling of the flag is added
126 	//if (object.type == ZmOrganizer.FOLDER) {
127 	if (false) {
128 		this._markReadEl.innerHTML = object.globalMarkRead ? ZmMsg.sharingDialogGlobalMarkRead :
129                                                              ZmMsg.sharingDialogPerUserMarkRead;
130 		this._props.setPropertyVisible(this._markReadId, true)
131 	} else {
132 		this._props.setPropertyVisible(this._markReadId, false)
133 	}
134 
135 	var isNewShare = (this._shareMode == ZmSharePropsDialog.NEW);
136 	var isUserShare = share ? share.isUser() || share.isGroup() : true;
137 	var isGuestShare = share ? share.isGuest() : false;
138 	var isPublicShare = share ? share.isPublic() : false;
139 	var supportsPublic = object.supportsPublicAccess();
140 	var externalEnabled = appCtxt.get(ZmSetting.SHARING_EXTERNAL_ENABLED);
141 	var publicEnabled = appCtxt.get(ZmSetting.SHARING_PUBLIC_ENABLED);
142 
143 	this._userRadioEl.checked = isUserShare;
144 	this._userRadioEl.disabled = !isNewShare;
145 	this._guestRadioEl.checked = isGuestShare;
146 	this._guestRadioEl.disabled = !(externalEnabled && isNewShare  && supportsPublic);
147 	this._publicRadioEl.checked = isPublicShare;
148 	this._publicRadioEl.disabled = !(publicEnabled && isNewShare && supportsPublic && (object.type !== ZmOrganizer.FOLDER));
149 
150 	var type = this._getType(isUserShare, isGuestShare, isPublicShare);
151 	this._handleShareWith(type);
152 
153 	var grantee = "", password  = "";
154 	if (share) {
155 		if (isGuestShare) {
156 			grantee = share.grantee.id;
157 			password = share.link.pw;
158 		} else {
159 			grantee = (share.grantee.name || ZmMsg.userUnknown);
160 			password = share.grantee.id;
161 		}
162 	}
163 	this._grantee.clear();
164 	this._grantee.setValue(grantee, true);
165 	this._grantee.setEnabled(isNewShare);
166 
167 	// Make all the properties visible so that their elements are in the
168 	// document. Otherwise, we won't be able to get a handle on them to perform
169 	// operations.
170 	this._props.setPropertyVisible(this._shareWithOptsId, true);
171 	//this._shareWithOptsProps.setPropertyVisible(this._passwordId, true);
172 	this._props.setPropertyVisible(this._shareWithBreakId, true);
173 
174 	//this._passwordButton.setVisible(!isNewShare);
175 	//this._shareWithOptsProps.setPropertyVisible(this._passwordId, isGuestShare);
176 	//this._passwordInput.setValue(password, true);
177 
178 	if (this._inheritEl) {
179 		this._inheritEl.checked = share ? share.link.inh : isNewShare;
180 	}
181 
182 	var perm = share && share.link.perm;
183 
184 	if (perm != null) {
185 		perm = perm.replace(/-./g, "");
186 		this._privateEl.checked = (perm.indexOf(ZmShare.PERM_PRIVATE) != -1);
187 		perm = perm.replace(/p/g, "");
188 		var role = ZmShare._getRoleFromPerm(perm);
189 		var radioEl = this._radioElByRole[role];
190 		if (radioEl) {
191 			radioEl.checked = true;
192 		}
193 	}
194 
195 	this._privatePermissionEnabled = object.supportsPrivatePermission();
196 	this._privatePermission.setVisible(object.supportsPrivatePermission());
197 
198 	if (perm == null || (perm == this._viewerRadioEl.value)) {
199 		this._viewerRadioEl.checked = true;
200 	} else if (perm == this._noneRadioEl.value) {
201 		this._noneRadioEl.checked = true;
202 	} else if (perm == this._managerRadioEl.value) {
203 		this._managerRadioEl.checked = true;
204 	} else if (perm == this._adminRadioEl.value) {
205 		this._adminRadioEl.checked = true;
206 	}
207 
208 	// Force a reply if new share
209 	this._reply.setReplyType(ZmShareReply.STANDARD);
210 	this._reply.setReplyNote("");
211 
212 	this._populateUrls();
213 
214     DwtDialog.prototype.popup.call(this);
215 
216 	var size = this.getSize();
217 	Dwt.setSize(this._granteeInput, 0.6*size.x);
218 	//Dwt.setSize(this._passwordInput.getInputElement(), 0.6*size.x);
219 
220 	this.setButtonEnabled(DwtDialog.OK_BUTTON, false);
221 	if (isNewShare) {
222 		this._userRadioEl.checked = true;
223 		this._grantee.focus();
224 	}
225 
226 	if (appCtxt.multiAccounts) {
227 		var acct = object.account || appCtxt.accountList.mainAccount;
228 		this._acAddrSelectList.setActiveAccount(acct);
229 	}
230 };
231 
232 ZmSharePropsDialog.prototype._populateUrls =
233 function() {
234 
235     var acct, restUrl;
236     if (appCtxt.multiAccounts) {
237         acct = this._object.getAccount();
238         restUrl = this._object.getRestUrl(acct);
239     } else {
240         restUrl = this._object.getRestUrl();
241     }    
242 	if (appCtxt.isOffline) {
243 		var remoteUri = appCtxt.get(ZmSetting.OFFLINE_REMOTE_SERVER_URI, null, acct);
244 		restUrl = remoteUri + restUrl.substring((restUrl.indexOf("/",7)));
245 	}
246 	var url = AjxStringUtil.htmlEncode(restUrl).replace(/&/g,'%26');
247 	var text = url;
248 	if (text.length > 50) {
249 		var length = text.length - 50;
250 		var index = (text.length - length) / 2;
251 		text = text.substr(0, index) + "..." + text.substr(index + length);
252 	}
253 
254 	var proto = (location.protocol === ZmSetting.PROTO_HTTPS) ? "webcals:" : "webcal:";
255     var webcalURL = proto + url.substring((url.indexOf("//")));
256     var webcalText = webcalURL;
257     if (webcalText.length > 50) {
258 		var length = webcalText.length - 50;
259 		var index = (webcalText.length - length) / 2;
260 		webcalText = webcalText.substr(0, index) + "..." + webcalText.substr(index + length);
261 	}
262 
263 	var isRestFolder = this._object.type != ZmOrganizer.FOLDER;
264 	this._urlGroup.setVisible(isRestFolder);
265 	if (isRestFolder) {
266 		if (this._object.type == ZmOrganizer.CALENDAR) {
267 			this._urlEl.innerHTML = [
268 				"<div>", ZmMsg.ics, ":    ",
269 					'<a target=_new id="SharePropsURL_ICS" href="',url,'.ics">',text,".ics</a>",
270 				"</div>",
271 				"<div>", ZmMsg.view, ":  ",
272 					'<a target=_new id="SharePropsURL_view" href="',url,'.html">',text,".html</a>",
273 				"</div>",
274                 "<div>", ZmMsg.outlookURL, ":  ",
275 					'<a target=_new id="SharePropsURL_Outlook" href="',webcalURL,'">',webcalText,"</a>",
276 				"</div>"
277 			].join("");
278 		} else if (this._object.type == ZmOrganizer.TASKS) {
279 			this._urlEl.innerHTML = [
280 				"<div style='padding-left:2em;'>",
281 					'<a target=_new id="SharePropsURL" href="',url,'.ics">',text,".ics</a>",
282 				"</div>"
283 			].join("");
284 		} else {
285 			this._urlEl.innerHTML = [
286 				"<div style='padding-left:2em;'>",
287 					'<a target=_new id="SharePropsURL" href="',url,'">',text,"</a>",
288 				"</div>"
289 			].join("");
290 		}
291 	}
292 };
293 
294 ZmSharePropsDialog.prototype.popdown =
295 function() {
296 	if (this._acAddrSelectList) {
297 		this._acAddrSelectList.reset();
298 		this._acAddrSelectList.show(false);
299 	}
300 	DwtDialog.prototype.popdown.call(this);
301 };
302 
303 // Protected methods
304 
305 ZmSharePropsDialog.prototype._getType =
306 function(isUserShare, isGuestShare, isPublicShare) {
307 	if (arguments.length == 0) {
308 		isUserShare = this._userRadioEl.checked;
309 		isGuestShare = this._guestRadioEl.checked;
310 		isPublicShare = this._publicRadioEl.checked;
311 	}
312 	return (isUserShare && ZmShare.TYPE_USER) ||
313 		   (isGuestShare && ZmShare.TYPE_GUEST) ||
314 		   (isPublicShare && ZmShare.TYPE_PUBLIC);
315 };
316 
317 ZmSharePropsDialog.prototype._handleChangeButton =
318 function(event) {
319 	//this._passwordButton.setVisible(false);
320 	//this._passwordInput.setVisible(true);
321 	//this._passwordInput.focus();
322 };
323 
324 ZmSharePropsDialog.prototype._handleOkButton =
325 function(event) {
326 	var isUserShare = this._userRadioEl.checked;
327 	var isGuestShare = this._guestRadioEl.checked;
328 	var isPublicShare = this._publicRadioEl.checked;
329 	var shareWithMyself = false;
330 
331 	var parsedEmailsFromText = AjxEmailAddress.parseEmailString(this._granteeInput.value);
332 	var goodEmailsFromText = parsedEmailsFromText.good.getArray();
333 	var goodEmailsFromBubbles =  this._grantee.getAddresses();
334 
335 	var goodEmails = goodEmailsFromBubbles.concat(goodEmailsFromText);
336 	var badEmails = parsedEmailsFromText.bad.getArray();
337 
338 	// validate input
339 	if (!isPublicShare) {
340 		var error;
341 		if (badEmails.length) {
342 			error = AjxMessageFormat.format(AjxMsg.invalidEmailAddrValue, AjxStringUtil.htmlEncode(this._granteeInput.value));
343 		}
344 		else if (!goodEmails.length) {
345 			error = AjxMsg.valueIsRequired;
346 		}
347 
348 		if (error) {
349 			var dialog = appCtxt.getErrorDialog();
350 			dialog.setMessage(error);
351 			dialog.popup(null, true);
352 
353 			if (!goodEmails.length) {
354 				return;
355 			}
356 		}
357 	}
358 
359     var replyType = this._reply.getReplyType();
360     if (replyType != ZmShareReply.NONE) {
361         var notes = (replyType == ZmShareReply.QUICK) ? this._reply.getReplyNote() : "";
362     }
363 
364 	var shares = [];
365 	if (this._shareMode == ZmSharePropsDialog.NEW) {
366 		var type = this._getType(isUserShare, isGuestShare, isPublicShare);
367 		if (!isPublicShare) {
368 			for (var i = 0; i < goodEmails.length; i++) {
369 				// bug fix #26428 - exclude me from list of addresses
370 				var addr = goodEmails[i];
371 				//bug#66610: allow Calendar Sharing with addresses present in zimbraAllowFromAddress
372 				var allowLocal;
373 				var excludeAllowFromAddress = true;
374 				if (appCtxt.isMyAddress(addr, allowLocal, excludeAllowFromAddress)) {
375 					shareWithMyself = true;
376 					continue;
377 				}
378 
379 				var share = this._setUpShare();
380 				share.grantee.name = addr;
381 				share.grantee.type = type;
382 				shares.push(share);
383 			}
384 		} else {
385 			var share = this._setUpShare();
386 			share.grantee.type = type;
387 			shares.push(share);
388 		}
389 	} else {
390 		shares.push(this._setUpShare(this._share)); // editing perms on a share
391 	}
392 	
393 	// Since we may be sharing with multiple users, use a batch command
394 	var accountName = appCtxt.multiAccounts ? this._object.getAccount().name : null;
395 	var batchCmd = new ZmBatchCommand(null, accountName);
396 	var perm = this._getPermsFromRole();
397 	//var pw = isGuestShare && this._passwordInput.getValue();
398 	if (shares && shares.length == 0 && shareWithMyself) {
399 		var msgDlg = appCtxt.getMsgDialog(true);
400 		msgDlg.setMessage(ZmMsg.sharingErrorWithSelf,DwtMessageDialog.INFO_STYLE);
401 		msgDlg.setTitle(ZmMsg.sharing);
402 		msgDlg.popup();
403 		return;
404 	}
405 	for (var i = 0; i < shares.length; i++) {
406 		var share = shares[i];
407 		if (perm != share.link.perm) {
408 			var cmd = new AjxCallback(share, share.grant,
409 			                          [perm, null, notes,
410 			                           replyType, this._shareMode]);
411 			batchCmd.add(cmd);
412 		}
413 	}
414 	if (batchCmd.size() > 0) {
415 		var respCallback = !isPublicShare
416 			? (new AjxCallback(this, this._handleResponseBatchCmd, [shares])) : null;
417 		batchCmd.run(respCallback);
418 	}
419 	
420 	this.popdown();
421 };
422 
423 ZmSharePropsDialog.prototype._handleResponseBatchCmd =
424 function(shares, result) {
425 
426 
427     var response = result.getResponse();
428     var batchResponse = response.BatchResponse;
429 
430     //bug:67698 Do not send notification on failed share
431     if(batchResponse.Fault){
432        appCtxt.setStatusMsg(ZmMsg.shareNotCreated,ZmStatusView.LEVEL_WARNING);
433        return false;
434     }
435     else{
436         if (!shares || (shares && shares.length == 0)) { return; }
437         var ignore = this._getFaultyEmails(result);
438         var replyType = this._reply.getReplyType();
439         if (replyType != ZmShareReply.NONE) {
440             var notes = (replyType == ZmShareReply.QUICK) ? this._reply.getReplyNote() : "";
441             var guestnotes;
442             var batchCmd;
443 
444             if (shares.length > 1) {
445                 var accountName = appCtxt.multiAccounts ? this._object.getAccount().name : null;
446                 batchCmd = new ZmBatchCommand(false, accountName, true);
447             }
448 
449             for (var i = 0; i < shares.length; i++) {
450                 var share = shares[i];
451                 var email = share.grantee.email || share.grantee.id;
452                 if (!email) {
453                     // last resort: check if grantee name is a valid email address
454                     if (AjxEmailAddress.isValid(share.grantee.name))
455                         email = share.grantee.name;
456                 }
457 
458                 if (!email || (email && ignore[email])) { continue; }
459 
460                 var addrs = new AjxVector();
461                 var addr = new AjxEmailAddress(email, AjxEmailAddress.TO);
462                 addrs.add(addr);
463 
464                 var tmpShare = new ZmShare({object:share.object});
465 
466                 tmpShare.grantee.id = share.grantee.id;
467                 tmpShare.grantee.email = email;
468                 tmpShare.grantee.name = share.grantee.name;
469 
470                 // REVISIT: What if you have delegated access???
471                 if (tmpShare.object.isRemote()) {
472                     tmpShare.grantor.id = tmpShare.object.zid;
473                     tmpShare.grantor.email = tmpShare.object.owner;
474                     tmpShare.grantor.name = tmpShare.grantor.email;
475                     tmpShare.link.id = tmpShare.object.rid;
476                     tmpShare.link.name = tmpShare.object.oname || tmpShare.object.name;
477                 } else {
478                     // bug: 50936  get setting for respective account
479                     // to prevent sharing the default account unintentionally
480                     tmpShare.grantor.id = appCtxt.get(ZmSetting.USERID, null, this._object.getAccount());
481                     tmpShare.grantor.email = appCtxt.get(ZmSetting.USERNAME, null, this._object.getAccount());
482                     tmpShare.grantor.name = appCtxt.get(ZmSetting.DISPLAY_NAME, null, this._object.getAccount()) || tmpShare.grantor.email;
483                     tmpShare.link.id = tmpShare.object.id;
484                     tmpShare.link.name = tmpShare.object.name;
485                 }
486                 // If folder is not synced before sharing, link ID might have changed in ZD.
487                 // Always get from response.
488                 if(appCtxt.isOffline) {
489                     var linkId = this.getLinkIdfromResp(result);
490                     if(linkId) {
491                         tmpShare.link.id =  [tmpShare.grantor.id, linkId].join(":");
492                     }
493                 }
494 
495                 tmpShare.link.perm = share.link.perm;
496                 tmpShare.link.view = ZmOrganizer.getViewName(tmpShare.object.type);
497                 tmpShare.link.inh = this._inheritEl ? this._inheritEl.checked : true;
498 
499                 if (this._guestRadioEl.checked) {
500                     if (!this._guestFormatter) {
501                         this._guestFormatter = new AjxMessageFormat(ZmMsg.shareCalWithGuestNotes);
502                     }
503 
504                     var url = share.object.getRestUrl();
505                     url = url.replace(/&/g,'%26');
506                     if (appCtxt.isOffline) {
507                         var remoteUri = appCtxt.get(ZmSetting.OFFLINE_REMOTE_SERVER_URI);
508                         url = remoteUri + url.substring((url.indexOf("/",7)));
509                     }
510 
511                     //bug:34647 added webcal url for subscribing to outlook/ical on a click
512                     var webcalURL = "webcals:" + url.substring((url.indexOf("//")));
513 
514                     //var password = this._passwordInput.getValue();
515                     guestnotes = this._guestFormatter.format([url, webcalURL, email, "", notes]);
516                 }
517                 tmpShare.notes = guestnotes || notes;
518 
519                 /*
520                     tmpShare.sendMessage(this._shareMode, addrs, null, batchCmd);
521                 */
522             }
523             if (batchCmd)
524                 batchCmd.run();
525 
526             var shareMsg = (this._shareMode==ZmSharePropsDialog.NEW)?ZmMsg.shareCreatedSubject:ZmMsg.shareModifiedSubject;
527             appCtxt.setStatusMsg(shareMsg);
528 
529         }
530     }
531 };
532 
533 ZmSharePropsDialog.prototype.getLinkIdfromResp =
534 function(result){
535 
536     if (!result) { return; }
537     var resp = result.getResponse().BatchResponse.FolderActionResponse || [];
538     if (resp.length > 0 && resp[0].action) {
539         return resp[0].action.id;
540     } else {
541         return null;
542     }
543 };
544 
545 // HACK: grep the Faults in BatchResponse and sift out the bad emails
546 ZmSharePropsDialog.prototype._getFaultyEmails =
547 function(result) {
548 
549 	if (!result) { return; }
550 	var noSuchAccount = "no such account: ";
551 	var bad = {};
552 	var fault = result.getResponse().BatchResponse.Fault || [];
553 	for (var i = 0; i < fault.length; i++) {
554 		var reason = fault[i].Reason.Text;
555 		if (reason.indexOf(noSuchAccount) == 0) {
556 			bad[reason.substring(noSuchAccount.length)] = true;
557 		}
558 	}
559 	return bad;
560 };
561 
562 ZmSharePropsDialog.prototype._setUpShare =
563 function(share) {
564 	if (!share) {
565 		share = new ZmShare({object:this._object});
566 	}
567 	share.link.inh = (this._inheritEl && this._inheritEl.checked);
568 	
569 	return share;
570 };
571 
572 ZmSharePropsDialog.prototype._acKeyUpListener =
573 function(event, aclv, result) {
574 	ZmSharePropsDialog._enableFieldsOnEdit(this);
575 };
576 
577 ZmSharePropsDialog._handleKeyUp =
578 function(event){
579 	if (DwtInputField._keyUpHdlr(event)) {
580 		return ZmSharePropsDialog._handleEdit(event);
581 	}
582 	return false;
583 };
584 
585 ZmSharePropsDialog._handleEdit =
586 function(event) {
587 	var target = DwtUiEvent.getTarget(event);
588 	var dialog = Dwt.getObjectFromElement(target);
589 	if (dialog instanceof DwtInputField) {
590 		dialog = dialog.getData(Dwt.KEY_OBJECT);
591 	}
592 	if (dialog != null) {
593 		ZmSharePropsDialog._enableFieldsOnEdit(dialog);
594 	}
595 	return true;
596 };
597 
598 ZmSharePropsDialog._enableFieldsOnEdit =
599 function(dialog) {
600 	var isEdit = dialog._mode == ZmSharePropsDialog.EDIT;
601 
602 	var isUserShare = dialog._userRadioEl.checked;
603 	var isPublicShare = dialog._publicRadioEl.checked;
604 	var isGuestShare = dialog._guestRadioEl.checked;
605 
606 	dialog._privatePermission.setVisible(dialog._privatePermissionEnabled && !dialog._noneRadioEl.checked && !isPublicShare);
607 	if (isPublicShare) {
608 		// Remove private permissions (which may have been set earlier) if the share is a public share
609 		dialog._privateEl.checked = false;
610 	}
611 
612 	var hasEmail = AjxStringUtil.trim(dialog._grantee.getValue()) != "";
613 	//var hasPassword = AjxStringUtil.trim(dialog._passwordInput.getValue()) != "";
614 
615 	var enabled = isEdit ||
616 				  isPublicShare ||
617 				  (isUserShare && hasEmail) ||
618 				  (isGuestShare && hasEmail);
619 	dialog.setButtonEnabled(DwtDialog.OK_BUTTON, enabled);
620 };
621 
622 ZmSharePropsDialog._handleShareWith =
623 function(event) {
624 	var target = DwtUiEvent.getTarget(event);
625 	var dialog = Dwt.getObjectFromElement(target);
626 	dialog._handleShareWith(target.value);
627 
628 	return ZmSharePropsDialog._handleEdit(event);
629 };
630 
631 ZmSharePropsDialog.prototype._handleShareWith = function(type) {
632 	var isUserShare = type == ZmShare.TYPE_USER;
633 	var isGuestShare = type == ZmShare.TYPE_GUEST;
634 	var isPublicShare = type == ZmShare.TYPE_PUBLIC;
635 
636     // TODO - Currently external sharing is enabled for briefcase only.
637     var guestRadioLabelEl = document.getElementById("LblShareWith_external");
638 
639     if (appCtxt.getCurrentApp().getName() === ZmId.APP_BRIEFCASE) {
640         this._rolesGroup.setVisible(isUserShare || isGuestShare);
641         guestRadioLabelEl.innerHTML = ZmMsg.shareWithExternalGuest;
642     }
643     else {
644 	    this._rolesGroup.setVisible(isUserShare);
645         guestRadioLabelEl.innerHTML = ZmMsg.shareWithGuest;
646     }
647 	this._messageGroup.setVisible(!isPublicShare);
648 	this._privatePermission.setVisible(this._privatePermissionEnabled && !isPublicShare);
649 
650     var adminRadioRow = document.getElementById("ShareRole_Row_" + ZmShare.ROLE_ADMIN);
651 
652     if (isGuestShare) {
653         this._reply && this._reply.setReplyOptions(ZmShareReply.EXTERNAL_USER_OPTIONS);
654         adminRadioRow.style.display = 'none';
655     }
656     else {
657         this._reply && this._reply.setReplyOptions(ZmShareReply.DEFAULT_OPTIONS);
658         this._reply.setReplyType(ZmShareReply.STANDARD);
659         adminRadioRow.style.display = '';
660     }
661 	this._props.setPropertyVisible(this._shareWithOptsId, !isPublicShare);
662 	//this._shareWithOptsProps.setPropertyVisible(this._passwordId, isGuestShare);
663 	this._props.setPropertyVisible(this._shareWithBreakId, !isPublicShare);
664     this._setAutoComplete(isGuestShare);
665 
666 	if (!isUserShare) {
667 		this._viewerRadioEl.checked = true;
668 	}
669 };
670 
671 /**
672  * Returns a perms string based on the user's selection of a role and privacy.
673  */
674 ZmSharePropsDialog.prototype._getPermsFromRole =
675 function() {
676 	var role = ZmShare.ROLE_NONE;
677 	if (this._viewerRadioEl.checked) {
678 		role = ZmShare.ROLE_VIEWER;
679 	}
680 	if (this._managerRadioEl.checked) {
681 		role = ZmShare.ROLE_MANAGER;
682 	}
683 	if (this._adminRadioEl.checked) {
684 		role = ZmShare.ROLE_ADMIN;
685 	}
686 	var perm = ZmShare.ROLE_PERMS[role];
687 	if (perm && this._privatePermissionEnabled && this._privateEl.checked) {
688 		perm += ZmShare.PERM_PRIVATE;
689 	}
690 	return perm;
691 };
692 
693 ZmSharePropsDialog.prototype._createView = function() {
694 
695 	var view = new DwtComposite(this);
696 
697 	// ids
698 	var nameId = Dwt.getNextId();
699     var markReadValueId = Dwt.getNextId();
700 	var typeId = Dwt.getNextId();
701 	var granteeId = Dwt.getNextId();
702 	var inheritId = Dwt.getNextId();
703 	var urlId = Dwt.getNextId();
704 	var permissionId = Dwt.getNextId();
705 
706 	var shareWithRadioName = this._htmlElId + "_shareWith";
707 	var shareWith = new DwtPropertySheet(this, null, null, DwtPropertySheet.RIGHT);
708 	var shareWithProperties = [], sw, label, value, swRadioId;
709 	for (var i = 0; i < ZmSharePropsDialog.SHARE_WITH.length; i++) {
710 		sw = ZmSharePropsDialog.SHARE_WITH[i];
711         swRadioId = "ShareWith_" + sw;
712 		label = "<label id='LblShareWith_" + sw + "'for='" + swRadioId + "'>" + ZmSharePropsDialog.SHARE_WITH_MSG[sw] + "</label>";
713         value = "<input type='radio' id='" + swRadioId + "' name='" + shareWithRadioName + "' value='" + ZmSharePropsDialog.SHARE_WITH_TYPE[sw] + "'>";
714 		shareWith.addProperty(label, value);
715 	}
716 
717 	this._shareWithOptsProps = new DwtPropertySheet(this);
718 	this._shareWithOptsProps.addProperty(ZmMsg.emailLabel, this._grantee);
719 
720 	var otherHtml = [
721 		"<table class='ZCheckboxTable'>",
722 			"<tr>",
723 				"<td>",
724 					"<input type='checkbox' id='",inheritId,"' checked>",
725 				"</td>",
726 				"<td>","<label for='", inheritId,  "'>" , ZmMsg.inheritPerms, "</label>", "</td>",
727 			"</tr>",
728 		"</table>"
729 	].join("");
730 
731 	this._props = new DwtPropertySheet(view);
732 	this._props.addProperty(ZmMsg.nameLabel, "<span id='" + nameId + "'></span>");
733     this._props.addProperty(ZmMsg.typeLabel, "<span id='" + typeId + "'></span>");
734     this._markReadId = this._props.addProperty(ZmMsg.sharingDialogMarkReadLabel, "<span id='" + markReadValueId + "'></span>");
735 	var shareWithId = this._props.addProperty(ZmMsg.shareWithLabel, shareWith);
736 	var otherId = this._props.addProperty(ZmMsg.otherLabel, otherHtml);
737 
738 	// Accessibility: set aria-labelledby for each radio button to two IDs, one is the group label, other is label for that button
739 	var shareWithLabelId = this._props.getProperty(shareWithId).labelId,
740 		radioId, radioEl;
741 	for (var i = 0; i < ZmSharePropsDialog.SHARE_WITH.length; i++) {
742 		sw = ZmSharePropsDialog.SHARE_WITH[i];
743 		radioId = 'ShareWith_' + sw;
744 		radioEl = document.getElementById(radioId);
745 		if (radioEl) {
746 			radioEl.setAttribute('aria-labelledby', [ shareWithLabelId, 'LblShareWith_' + sw ].join(' '));
747 		}
748 	}
749 
750 	this._inheritEl = document.getElementById(inheritId);
751 
752 	// XXX: for now, we are hiding this property for simplicity's sake
753 	this._props.setPropertyVisible(otherId, false);
754 	this._shareWithBreakId = this._props.addProperty("", "<HR>");
755 	this._shareWithOptsId = this._props.addProperty("", this._shareWithOptsProps);
756 
757 	// add role group
758 	var idx = 0;
759 	var html = [];
760 	html[idx++] = "<table class='ZRadioButtonTable'>";
761 
762 	this._rolesGroup = new DwtGrouper(view);
763 
764 	var roleRadioName = this._htmlElId + "_role";
765 	var roles = [ZmShare.ROLE_NONE, ZmShare.ROLE_VIEWER, ZmShare.ROLE_MANAGER, ZmShare.ROLE_ADMIN];
766 	for (var i = 0; i < roles.length; i++) {
767 		var role = roles[i],
768 			rowId = 'ShareRole_Row_' + role,
769 			radioId = 'ShareRole_' + role,
770 			labelId = 'LblShareRole_' + role,
771 			legendId = this._rolesGroup._labelEl.id,
772 			labelledBy = [ legendId, labelId ].join(' ');
773 
774 		html[idx++] = "<tr id='" + rowId + "'>";
775         html[idx++] = "<td style='padding-left:10px; vertical-align:top;'>";
776 		html[idx++] = "<input type='radio' name='" + roleRadioName + "' value='" + role + "' id='" + radioId + "' aria-labelledby='" + labelledBy + "'>";
777         html[idx++] = "</td>";
778 		html[idx++] = "<td style='font-weight:bold; padding:0 0.5em 0 .25em;'>";
779 		html[idx++] = "<label id='" + labelId + "' for='"+radioId+"' >";
780 		html[idx++] = ZmShare.getRoleName(role);
781 		html[idx++] = "</label>"
782 		html[idx++] = "</td>";
783 		html[idx++] = "<td style='white-space:nowrap'>";
784 		html[idx++] = ZmShare.getRoleActions(role);
785 		html[idx++] = "</td></tr>";
786 	}
787 
788 	html[idx++] = "</table>";
789 
790 	this._rolesGroup.setLabel(ZmMsg.role);
791 	this._rolesGroup.setContent(html.join(""));
792 
793 	this._privatePermission = new DwtPropertySheet(view);
794 	this._privatePermission._vAlign = "middle";
795 	this._privatePermission.addProperty("<input type='checkbox' id='" + permissionId + "'/>",  "<label for='" + permissionId + "' >" +  ZmMsg.privatePermission +  "</label>");
796 	this._privateEl = document.getElementById(permissionId);
797 	Dwt.setHandler(this._privateEl, DwtEvent.ONCLICK, ZmSharePropsDialog._handleEdit);
798 	Dwt.associateElementWithObject(this._privateEl, this);
799 
800 	// add message group
801 	this._messageGroup = new DwtGrouper(view);
802 	this._messageGroup.setLabel(ZmMsg.message);
803 	this._reply = new ZmShareReply({
804 		parent:     view,
805 		legendId:   this._messageGroup._labelEl.id
806 	});
807 	this._messageGroup.setView(this._reply);
808 
809 	// add url group
810 	var urlHtml = [
811 		"<div>",
812 			"<div style='margin-bottom:.25em'>",ZmMsg.shareUrlInfo,"</div>",
813 			"<div style='cursor:text' id='",urlId,"'></div>",
814 		"</div>"
815 	].join("");
816 
817 	this._urlGroup = new DwtGrouper(view);
818 	this._urlGroup.setLabel(ZmMsg.url);
819 	this._urlGroup.setContent(urlHtml);
820 	this._urlGroup._setAllowSelection();
821 
822 	// save information elements
823 	this._nameEl = document.getElementById(nameId);
824     this._typeEl = document.getElementById(typeId);
825     this._markReadEl = document.getElementById(markReadValueId);
826 	this._urlEl = document.getElementById(urlId);
827 
828 	this._setAutoComplete();
829 
830 	// add change handlers
831 	if (this._inheritEl) {
832 		Dwt.setHandler(this._inheritEl, DwtEvent.ONCLICK, ZmSharePropsDialog._handleEdit);
833 		Dwt.associateElementWithObject(this._inheritEl, this);
834 	}
835 
836 	var radios = ["_userRadioEl", "_guestRadioEl", "_publicRadioEl"];
837 	var radioEls = document.getElementsByName(shareWithRadioName);
838 	for (var i = 0; i < radioEls.length; i++) {
839 		this[radios[i]] = radioEls[i];
840 		Dwt.setHandler(radioEls[i], DwtEvent.ONCLICK, ZmSharePropsDialog._handleShareWith);
841 		Dwt.associateElementWithObject(radioEls[i], this);
842 	}
843 
844 	radios = ["_noneRadioEl", "_viewerRadioEl", "_managerRadioEl", "_adminRadioEl"];
845 	radioEls = document.getElementsByName(roleRadioName);
846 	roles = [ZmShare.ROLE_NONE, ZmShare.ROLE_VIEWER, ZmShare.ROLE_MANAGER, ZmShare.ROLE_ADMIN];
847 	this._radioElByRole = {};
848 	for (var i = 0; i < radioEls.length; i++) {
849 		this[radios[i]] = radioEls[i];
850 		this._radioElByRole[roles[i]] = radioEls[i];
851 		Dwt.setHandler(radioEls[i], DwtEvent.ONCLICK, ZmSharePropsDialog._handleEdit);
852 		Dwt.associateElementWithObject(radioEls[i], this);
853 	}
854 
855 	this._tabGroup.addMember(shareWith.getTabGroupMember());
856 	this._tabGroup.addMember(this._grantee);
857 	this._tabGroup.addMember(this._rolesGroup.getTabGroupMember());
858 	this._tabGroup.addMember(this._messageGroup.getTabGroupMember());
859 	this._tabGroup.addMember(this._urlGroup.getTabGroupMember());
860 	this._tabGroup.addMember(this._reply.getTabGroupMember());
861 
862 	return view;
863 };
864 
865 ZmSharePropsDialog.prototype._setAutoComplete =
866 function(disabled) {
867 	if (!disabled && this._acAddrSelectList) {
868 		this._acAddrSelectList.handle(this._granteeInput);
869 	}
870 	else {
871 		Dwt.setHandler(this._granteeInput, DwtEvent.ONKEYUP, ZmSharePropsDialog._handleKeyUp);
872 	}
873 };
874