1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 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) 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 dialog with various trees so a user can select a folder.
 30  * @class
 31  * This class represents choose folder dialog.
 32  * 
 33  * @param	{DwtControl}	shell		the parent
 34  * @param	{String}	className		the class name
 35  * 
 36  * @extends		ZmDialog
 37  */
 38 ZmChooseFolderDialog = function(parent, className) {
 39 	var newButton = new DwtDialog_ButtonDescriptor(ZmChooseFolderDialog.NEW_BUTTON, ZmMsg._new, DwtDialog.ALIGN_LEFT);
 40 	var params = {parent:parent, className:className, extraButtons:[newButton], id:"ChooseFolderDialog"};
 41 	ZmDialog.call(this, params);
 42 
 43 	this._createControls();
 44 	this._setNameField(this._inputDivId);
 45 	this.registerCallback(ZmChooseFolderDialog.NEW_BUTTON, this._showNewDialog, this);
 46 	this._changeListener = new AjxListener(this, this._folderTreeChangeListener);
 47 
 48 	this._treeView = {};
 49 	this._creatingFolder = false;
 50 	this._treeViewListener = new AjxListener(this, this._treeViewSelectionListener);
 51 
 52 	this._multiAcctOverviews = {};
 53 };
 54 
 55 ZmChooseFolderDialog.prototype = new ZmDialog;
 56 ZmChooseFolderDialog.prototype.constructor = ZmChooseFolderDialog;
 57 
 58 ZmChooseFolderDialog.prototype.isZmChooseFolderDialog = true;
 59 ZmChooseFolderDialog.prototype.toString = function() { return "ZmChooseFolderDialog"; };
 60 
 61 ZmChooseFolderDialog.NEW_BUTTON = ++DwtDialog.LAST_BUTTON;
 62 
 63 
 64 /**
 65  * Since this dialog is intended for use in a variety of situations, we need to be
 66  * able to create different sorts of overviews based on what the calling function
 67  * wants. By default, we show the folder tree view.
 68  * 
 69  * @param {Hash}	params				a hash of parameters
 70  * @param	{Object}	params.data					a array of items, a folder, an item or <code>null</code>
 71  * @param	{Array}	params.treeIds				a list of trees to show
 72  * @param	{String}	params.overviewId			the overview ID
 73  * @param	{Hash}	params.omit					a list IDs to not show
 74  * @param	{String}	params.title					the dialog title
 75  * @param	{String}	params.description			the description of what the user is selecting
 76  * @param	{Boolean}	params.skipReadOnly		if <code>true</code>, read-only folders will not be displayed
 77  * @param	{Boolean}	params.skipRemote			if <code>true</code>, remote folders (mountpoints) will not be displayed
 78  * @param	{Boolean}	params.hideNewButton 		if <code>true</code>, new button will not be shown
 79  * @param	{Boolean}	params.noRootSelect			if <code>true</code>, do not make root tree item(s) selectable
 80  * @params  {Boolean}   params.showDrafts			if <code>true</code>, drafts folder will not be omited
 81  */
 82 ZmChooseFolderDialog.prototype.popup =
 83 function(params) {
 84 
 85 	this._keyPressedInField = false; //see comment in _handleKeyUp
 86 
 87 	// use reasonable defaults
 88 	params = params || {};
 89 
 90 	// create an omit list for each account
 91 	// XXX: does this need to happen more then once???
 92 	var omitPerAcct = {};
 93 	var accounts = appCtxt.accountList.visibleAccounts;
 94 	for (var i = 0; i < accounts.length; i++) {
 95 		var acct = accounts[i];
 96 
 97 		if (params.forceSingle && acct != appCtxt.getActiveAccount()) {
 98 			continue;
 99 		}
100 
101 		var omit = omitPerAcct[acct.id] = params.omit || {};
102 		
103 		omit[ZmFolder.ID_DRAFTS] = !params.showDrafts;
104 		omit[ZmFolder.ID_OUTBOX] = true;
105 		omit[ZmFolder.ID_SYNC_FAILURES] = true;
106 		omit[ZmFolder.ID_DLS] = true;
107 
108 		var folderTree = appCtxt.getFolderTree(acct);
109 
110 		// omit any folders that are read only
111 		if (params.skipReadOnly || params.skipRemote || appCtxt.isOffline) {
112 			var folders = folderTree.asList({includeRemote : true});
113 			for (var i = 0; i < folders.length; i++) {
114 				var folder = folders[i];
115 
116 				// if skipping read-only,
117 				if (params.skipReadOnly && folder.link && folder.isReadOnly()) {
118 					omit[folder.id] = true;
119 					continue;
120 				}
121 
122 				// if skipping remote folders,
123 				if (params.skipRemote && folder.isRemote()) {
124 					omit[folders[i].id] = true;
125 				}
126 			}
127 		}
128 	}
129 
130 	if (this.setTitle) {
131 		this.setTitle(params.title || ZmMsg.chooseFolder);
132 	}
133 	var descCell = document.getElementById(this._folderDescDivId);
134 	if (descCell) {
135 		descCell.innerHTML = params.description || "";
136 	}
137 
138 	var treeIds = this._treeIds = (params.treeIds && params.treeIds.length)
139 		? params.treeIds : [ZmOrganizer.FOLDER];
140 
141 	// New button doesn't make sense if we're only showing saved searches
142 	var searchOnly = (treeIds.length == 1 && treeIds[0] == ZmOrganizer.SEARCH);
143 	var newButton = this._getNewButton();
144 	if (newButton) {
145 		newButton.setVisible(!searchOnly && !params.hideNewButton && !appCtxt.isExternalAccount() && !appCtxt.isWebClientOffline());
146 	}
147 
148 	this._data = params.data;
149 
150 	var omitParam = {};
151 	if (appCtxt.multiAccounts) {
152 		omitParam[ZmOrganizer.ID_ZIMLET] = true;
153 		omitParam[ZmOrganizer.ID_ALL_MAILBOXES] = true;
154 	} else {
155 		omitParam = omitPerAcct[appCtxt.accountList.mainAccount.id];
156 	}
157 	
158 	var popupParams = {
159 		treeIds:		treeIds,
160 		omit:			omitParam,
161 		omitPerAcct:	omitPerAcct,
162 		fieldId:		this._folderTreeDivId,
163 		overviewId:		params.overviewId,
164 		noRootSelect:	params.noRootSelect,
165 		treeStyle:		params.treeStyle || DwtTree.SINGLE_STYLE,	// we don't want checkboxes!
166 		appName:		params.appName,
167 		selectable:		false,
168 		forceSingle:	params.forceSingle
169 	};
170 
171 	// make sure the requisite packages are loaded
172 	var treeIdMap = {};
173 	for (var i = 0; i < treeIds.length; i++) {
174 		treeIdMap[treeIds[i]] = true;
175 	}
176 
177 	this._acceptFolderMatch = params.acceptFolderMatch;
178 
179 	// TODO: Refactor packages so that we don't have to bring in so much
180 	// TODO: code just do make sure this dialog works.
181 	// TODO: I opened bug 34447 for this performance enhancement.
182 	var pkg = [];
183 	if (treeIdMap[ZmOrganizer.BRIEFCASE]) pkg.push("BriefcaseCore","Briefcase");
184 	if (treeIdMap[ZmOrganizer.CALENDAR]) pkg.push("MailCore","CalendarCore","Calendar");
185 	if (treeIdMap[ZmOrganizer.ADDRBOOK]) pkg.push("ContactsCore","Contacts");
186 	if (treeIdMap[ZmOrganizer.FOLDER]) pkg.push("MailCore","Mail");
187 	if (treeIdMap[ZmOrganizer.TASKS]) pkg.push("TasksCore","Tasks");
188 	
189 	AjxDispatcher.require(pkg, true, new AjxCallback(this, this._doPopup, [popupParams]));
190 };
191 
192 ZmChooseFolderDialog.prototype._getNewButton =
193 function () {
194 	return this.getButton(ZmChooseFolderDialog.NEW_BUTTON);
195 };
196 
197 ZmChooseFolderDialog.prototype._doPopup =
198 function(params) {
199 	var ov = this._setOverview(params, params.forceSingle);
200 
201 	if (appCtxt.multiAccounts && !params.forceSingle) {
202 		// ov is an overview container, and overviewId is the containerId
203 		this._multiAcctOverviews[params.overviewId] = ov;
204 		for (var i in this._multiAcctOverviews) {
205 			this._multiAcctOverviews[i].setVisible(i == params.overviewId);
206 		}
207 
208 		var overviews = ov.getOverviews();
209 		for (var i in overviews) {
210 			var overview = overviews[i];
211             // zimlet overview resets folder list
212             // need to stop resetting folder list for each overview
213             if (overview._treeIds[0].toLowerCase() != "zimlet") {
214                 this._resetTree(params.treeIds, overview);
215             }
216 		}
217 
218 		ov.expandAccountOnly(appCtxt.getActiveAccount());
219 
220 	} else {
221 		this._resetTree(params.treeIds, ov);
222 	}
223 
224 	if (this.isZmDialog) {
225 		this._focusElement = this._inputField;
226 		this._inputField.setValue("");
227 		ZmDialog.prototype.popup.call(this);
228 	}
229 };
230 
231 
232 ZmChooseFolderDialog.prototype.getOverviewId =
233 function(part, appName) {
234 	return appCtxt.getOverviewId([this.toString(), part, appName], null);
235 };
236 
237 ZmChooseFolderDialog.prototype.getOverviewId = function(appName) {
238 
239 	return appCtxt.getOverviewId([this.toString(), appName], null);
240 };
241 
242 ZmChooseFolderDialog.prototype._resetTree =
243 function(treeIds, overview) {
244 
245 	var account = overview.account || appCtxt.getActiveAccount() || appCtxt.accountList.mainAccount;
246 	var acctTreeView = this._treeView[account.id] = {};
247 	var folderTree = appCtxt.getFolderTree(account);
248 
249 	for (var i = 0; i < treeIds.length; i++) {
250 		var treeId = treeIds[i];
251 		var treeView = acctTreeView[treeId] = overview.getTreeView(treeId, true);
252 		if (!treeView) { continue; }
253 
254 		// bug #18533 - always make sure header item is visible in "MoveTo" dialog
255 		var headerItem = treeView.getHeaderItem();
256 		if (treeIds.length > 1) {
257 			if (treeId == ZmId.ORG_FOLDER) {
258 				headerItem.setText(ZmMsg.mailFolders);
259 			}
260 			else if (treeId == ZmId.ORG_BRIEFCASE) {
261 				headerItem.setText(ZmMsg.briefcaseFolders);
262 			}
263 		}
264 		headerItem.setVisible(true, true);
265 
266 		// expand root item
267 		var ti = treeView.getTreeItemById(folderTree.root.id);
268 		ti.setExpanded(true);
269 
270 		// bug fix #13159 (regression of #10676)
271 		// - small hack to get selecting Trash folder working again
272 		var trashId = ZmOrganizer.getSystemId(ZmOrganizer.ID_TRASH, account);
273 		var ti = treeView.getTreeItemById(trashId);
274 		if (ti) {
275 			ti.setData(ZmTreeView.KEY_TYPE, treeId);
276 		}
277 
278 		treeView.removeSelectionListener(this._treeViewListener);
279 		treeView.addSelectionListener(this._treeViewListener);
280 	}
281 
282 	folderTree.removeChangeListener(this._changeListener);
283 	// this listener has to be added after folder tree view is set
284 	// (so that it comes after the view's standard change listener)
285 	folderTree.addChangeListener(this._changeListener);
286 
287 	this._loadFolders();
288 	this._resetTreeView(true);
289 };
290 
291 ZmChooseFolderDialog.prototype.reset =
292 function() {
293 	var descCell = document.getElementById(this._folderDescDivId);
294 	descCell.innerHTML = "";
295 	ZmDialog.prototype.reset.call(this);
296 	this._data = this._treeIds = null;
297 	this._creatingFolder = false;
298 };
299 
300 ZmChooseFolderDialog.prototype._contentHtml =
301 function() {
302 	this._inputDivId = this._htmlElId + "_inputDivId";
303 	this._folderDescDivId = this._htmlElId + "_folderDescDivId";
304 	this._folderTreeDivId = this._htmlElId + "_folderTreeDivId";
305 
306 	return AjxTemplate.expand("share.Widgets#ZmChooseFolderDialog", {id:this._htmlElId});
307 };
308 
309 ZmChooseFolderDialog.prototype._createControls =
310 function() {
311 	this._inputField = new DwtInputField({parent: this});
312 	document.getElementById(this._inputDivId).appendChild(this._inputField.getHtmlElement());
313 	this._inputField.addListener(DwtEvent.ONKEYUP, new AjxListener(this, this._handleKeyUp));
314 	//this._inputField.addListener(DwtEvent.ONKEYDOWN, new AjxListener(this, this._handleKeyDown));
315 	// unfortunately there's no onkeydown generally set for input fields so above line does not work
316 	this._inputField.setHandler(DwtEvent.ONKEYDOWN, AjxCallback.simpleClosure(this._handleKeyDown, this));
317 };
318 
319 ZmChooseFolderDialog.prototype._showNewDialog =
320 function() {
321 	var itemType = this._getOverview().getSelected(true);
322 	var newType = itemType || this._treeIds[0];
323 	var ftc = this._opc.getTreeController(newType);
324 	var dialog = ftc._getNewDialog();
325 	dialog.reset();
326 	dialog.registerCallback(DwtDialog.OK_BUTTON, this._newCallback, this, [ftc, dialog]);
327 	dialog.popup();
328 };
329 
330 ZmChooseFolderDialog.prototype._newCallback =
331 function(ftc, dialog, params) {
332 	ftc._doCreate(params);
333 	dialog.popdown();
334 	this._creatingFolder = true;
335 };
336 
337 // After the user creates a folder, select it and optionally move items to it.
338 ZmChooseFolderDialog.prototype._folderTreeChangeListener =
339 function(ev) {
340 	if (ev.event == ZmEvent.E_CREATE && this._creatingFolder) {
341 		var organizers = ev.getDetail("organizers") || (ev.source && [ev.source]);
342 		var org = organizers[0];
343 		if (org) {
344 			var tv = this._treeView[org.getAccount().id][org.type];
345 			tv.setSelected(org, true);
346 			if (this._moveOnFolderCreate && !ev.shiftKey && !ev.ctrlKey) {
347 				tv._itemClicked(tv.getTreeItemById(org.id), ev);
348 			}
349 		}
350 		this._creatingFolder = false;
351 	}
352 	this._loadFolders();
353 };
354 
355 ZmChooseFolderDialog.prototype._okButtonListener =
356 function(ev) {
357     var tgtFolder = this._getOverview().getSelected(false);
358     var tgtType   = this._getOverview().getSelected(true);
359     var folderList = (tgtFolder && (!(tgtFolder instanceof Array)))
360 		? [tgtFolder] : tgtFolder;
361 
362 	var msg = (!folderList || (folderList && folderList.length == 0))
363 		? ZmMsg.noTargetFolder : null;
364 
365 	// check for valid target
366 	if (!msg && this._data) {
367 		for (var i = 0; i < folderList.length; i++) {
368 			var folder = folderList[i];
369 			//Note - although this case is checked in mayContain, I do not change mayContain since mayContain is complicated and returns a boolean and used from DnD and is overridden a bunch of times.
370 			//Only here we need the special message (see bug 82064) so I settle for that for now.
371 			if (this._data.isZmFolder && !folder.isInTrash() && folder.hasChild(this._data.name) && !this._acceptFolderMatch) {
372 				msg = ZmMsg.folderAlreadyExistsInDestination;
373 				break;
374 			}
375 			else if (folder.mayContain && !folder.mayContain(this._data, tgtType, this._acceptFolderMatch)) {
376 				if (this._data.isZmFolder) {
377 					msg = ZmMsg.badTargetFolder; 
378 				} else {
379 					var items = AjxUtil.toArray(this._data);
380 					for (var i = 0; i < items.length; i++) {
381 						var item = items[i];
382 						if (!item) {
383 							continue;
384 						}
385 						if (item.isDraft && (folder.nId != ZmFolder.ID_TRASH && folder.nId != ZmFolder.ID_DRAFTS && folder.rid != ZmFolder.ID_DRAFTS)) {
386 							// can move drafts into Trash or Drafts
387 							msg = ZmMsg.badTargetFolderForDraftItem;
388 							break;
389 						} else if ((folder.nId == ZmFolder.ID_DRAFTS || folder.rid == ZmFolder.ID_DRAFTS) && !item.isDraft)	{
390 							// only drafts can be moved into Drafts
391 							msg = ZmMsg.badItemForDraftsFolder;
392 							break;
393 						}
394 					}	
395 					if(!msg) {
396 						msg = ZmMsg.badTargetFolderItems; 
397 					}
398 				}
399 				break;
400 			}
401 		}
402 	}
403 
404 	if (msg) {
405 		this._showError(msg);
406 	} else {
407 		DwtDialog.prototype._buttonListener.call(this, ev, [tgtFolder]);
408 	}
409 };
410 
411 ZmChooseFolderDialog.prototype._getTabGroupMembers =
412 function() {
413 	return AjxUtil.collapseList([this._inputField, this._overview[this._curOverviewId]]);
414 };
415 
416 ZmChooseFolderDialog.prototype._loadFolders =
417 function() {
418 	this._folders = [];
419 
420 	for (var accountId in this._treeView) {
421 		var treeViews = this._treeView[accountId];
422 
423 		for (var type in treeViews) {
424 			var treeView = treeViews[type];
425 			if (!treeView) { continue; }
426 
427 			var items = treeView.getTreeItemList();
428 			for (var i = 0, len = items.length; i < len; i++) {
429 				var ti = items[i];
430 				if (!ti.getData) {  //not sure if this could happen but it was here before my refactoring.
431 					continue;
432 				}
433 				var folder = items[i].getData(Dwt.KEY_OBJECT);
434 				if (!folder || folder.nId == ZmOrganizer.ID_ROOT) {
435 					continue;
436 				}
437 				var name = folder.getName(false, null, true, true).toLowerCase();
438 				var path = "/" + folder.getPath(false, false, null, true).toLowerCase();
439 				this._folders.push({id: folder.id, type: type, name: name, path: path, accountId: accountId});
440 			}
441 		}
442 	}
443 };
444 
445 ZmChooseFolderDialog.prototype._handleKeyDown =
446 function(ev) {
447 	this._keyPressedInField = true; //see comment in _handleKeyUp
448 };
449 
450 ZmChooseFolderDialog.prototype._handleKeyUp =
451 function(ev) {
452 
453 	// this happens in the case of SearchFolder when the keyboard shortcut "s" was released when this
454 	// field was in focus but it does not affect the field since it was not pressed here.
455 	// (Bug 52983)
456 	// in other words, the sequence that caused the bug is:
457 	// 1. "s" keyDown triggering ZmDialog.prototype.popup()
458 	// 2. ZmDialog.prototype.popup setting focus on the input field
459 	// 3. "s" keyUp called triggering ZmChooseFolderDialog.prototype._handleKeyUp.
460 	// Note that this is reset to false in the popup only, since only one time we need this protection, and it's the simplest.
461 	if (!this._keyPressedInField) {
462 		return;
463 	}
464 
465 	var key = DwtKeyEvent.getCharCode(ev);
466 	if (key === DwtKeyEvent.KEY_TAB) {
467 		return;
468 	}
469     else if (key === DwtKeyEvent.KEY_ARROW_DOWN) {
470 		this._overview[this._curOverviewId].focus();
471 		return;
472 	}
473 
474 	var num = 0, firstMatch, matches = [];
475 	var value = this._inputField.getValue().toLowerCase();
476 	for (var i = 0, len = this._folders.length; i < len; i++) {
477 		var folderInfo = this._folders[i];
478 		var treeView = this._treeView[folderInfo.accountId][folderInfo.type];
479 		var ti = treeView.getTreeItemById(folderInfo.id);
480 		if (ti) {
481 			var testPath = "/" + value.replace(/^\//, "");
482 			var path = folderInfo.path;
483 			if (folderInfo.name.indexOf(value) == 0 ||
484 				(path.indexOf(testPath) == 0 && (path.substr(testPath.length).indexOf("/") == -1))) {
485 
486 				matches.push(ti);
487 				var activeAccountId = appCtxt.getActiveAccount().id;
488 				//choose the FIRST of active account folders. 
489 				if (!firstMatch || (folderInfo.accountId == activeAccountId
490 								&&	firstMatch.accountId != activeAccountId)) {
491                     firstMatch = folderInfo;
492                 }
493 			}
494 		}
495 	}
496 
497 	// now that we know which folders match, hide all items and then show
498 	// the matches, expanding their parent chains as needed
499 	this._resetTreeView(false);
500 
501 	for (var i = 0, len = matches.length; i < len; i++) {
502 		var ti = matches[i];
503 		ti._tree._expandUp(ti);
504 		ti.setVisible(true);
505 	}
506 
507 	if (firstMatch) {
508 		var tv = this._treeView[firstMatch.accountId][firstMatch.type];
509 		var ov = this._getOverview();
510 		if (ov) {
511 			ov.deselectAllTreeViews();
512 		}
513 		tv.setSelected(appCtxt.getById(firstMatch.id), true, true);
514 		if (appCtxt.multiAccounts) {
515 		    var ov = this._getOverview();
516 		    for (var h in ov._headerItems) {
517 			ov._headerItems[h].setExpanded((h == firstMatch.accountId), false, false);
518 		    }
519 		}
520 	}
521 };
522 
523 ZmChooseFolderDialog.prototype._resetTreeView =
524     function(visible) {
525 	for (var i = 0, len = this._folders.length; i < len; i++) {
526 		var folderInfo = this._folders[i];
527 		var tv = this._treeView[folderInfo.accountId][folderInfo.type];
528 		var ti = tv.getTreeItemById(folderInfo.id);
529 		if (ti) {
530 			ti.setVisible(visible);
531 			ti.setChecked(false, true);
532 		}
533 	}
534 };
535 
536 ZmChooseFolderDialog.prototype._getOverview =
537 function() {
538 	var ov;
539 	if (appCtxt.multiAccounts) {
540 		ov = this._opc.getOverviewContainer(this._curOverviewId);
541 	}
542 
543 	// this covers the case where we're in multi-account mode, but dialog was
544 	// popped up in "forceSingle" mode
545 	return (ov || ZmDialog.prototype._getOverview.call(this));
546 };
547 
548 ZmChooseFolderDialog.prototype._treeViewSelectionListener =
549 function(ev) {
550 	if (ev.detail != DwtTree.ITEM_SELECTED &&
551 		ev.detail != DwtTree.ITEM_DBL_CLICKED)
552 	{
553 		return;
554 	}
555 	var treeItem = ev.item;
556 	if (treeItem && !treeItem.isSelectionEnabled()) {
557 		// Selection should have been blocked, but only allow a double click if the item can be selected
558 		return;
559 	}
560 
561 	if (this._getOverview() instanceof ZmAccountOverviewContainer) {
562 		if (ev.detail == DwtTree.ITEM_DBL_CLICKED &&
563 			ev.item instanceof DwtHeaderTreeItem)
564 		{
565 			return;
566 		}
567 
568 		var oc = this._opc.getOverviewContainer(this._curOverviewId);
569 		var overview = oc.getOverview(ev.item.getData(ZmTreeView.KEY_ID));
570 		oc.deselectAll(overview);
571 	}
572 
573 	var organizer = ev.item && ev.item.getData(Dwt.KEY_OBJECT);
574 	if (organizer.id == ZmFolder.ID_LOAD_FOLDERS) {
575 		return;
576 	}
577 	var value = organizer ? organizer.getName(null, null, true) : ev.item.getText();
578 	this._inputField.setValue(value);
579 	if (ev.detail == DwtTree.ITEM_DBL_CLICKED || ev.enter) {
580 		this._okButtonListener();
581 	}
582 };
583 
584 ZmChooseFolderDialog.prototype._enterListener =
585 function(ev) {
586 	this._okButtonListener.call(this, ev);
587 };
588