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 new organizer dialog.
 30  * @class
 31  * This class represents a new organizer dialog.
 32  * 
 33  * @param	{DwtControl}	parent		the parent
 34  * @param	{String}	className		the class name
 35  * @param	{String}	title		the title
 36  * @param	{constant}	type		the organizer type
 37  * 
 38  * @extends		ZmDialog
 39  */
 40 ZmNewOrganizerDialog = function(parent, className, title, type, extraButtons) {
 41 	if (arguments.length == 0) return;
 42 
 43 	this._organizerType = type;
 44 	ZmDialog.call(this, {parent:parent, className:className, title:title, id:"CreateNewFolderDialog", extraButtons: extraButtons});
 45 	this._setupControls();
 46 };
 47 
 48 ZmNewOrganizerDialog.prototype = new ZmDialog;
 49 ZmNewOrganizerDialog.prototype.constructor = ZmNewOrganizerDialog;
 50 
 51 ZmNewOrganizerDialog.prototype.isZmNewOrganizerDialog = true;
 52 ZmNewOrganizerDialog.prototype.toString = function() { return "ZmNewOrganizerDialog"; };
 53 
 54 //override the following if needed
 55 ZmNewOrganizerDialog.prototype._folderLocationLabel = ZmMsg.newFolderParent;
 56 ZmNewOrganizerDialog.prototype._folderNameAlreadyExistsMsg = ZmMsg.errorAlreadyExists;
 57 
 58 // Public methods
 59 
 60 /**
 61  * Pops-up the dialog.
 62  * 
 63  * @param {ZmOrganizer|hash}	params      popup parameters
 64  * @param	{ZmAccount}	account		the account
 65  */
 66 ZmNewOrganizerDialog.prototype.popup =
 67 function(params, account) {
 68 
 69 	params = params || {};
 70     var folder = params instanceof ZmOrganizer ? params : (params && params.organizer);
 71 
 72 	var parentLabelCell = document.getElementById(this._htmlElId + '_parentLabel');
 73 	var parentValueCell = document.getElementById(this._htmlElId + '_parentValue');
 74 	this._parentFolder = null;
 75 
 76 	// if the user has already implicitly selected a parent folder, don't show overview
 77 	if (folder && folder.id != ZmOrganizer.ID_ROOT) {
 78 		this._parentFolder = folder;
 79 		this._makeOverviewVisible();    // hide all overviews
 80 		if (parentLabelCell) {
 81 			parentLabelCell.colSpan = 1;
 82 			parentLabelCell.innerHTML = ZmMsg.parentFolderLabel;
 83 			parentValueCell.innerHTML = folder.getName();
 84 		}
 85 	}
 86 	else {
 87 		if (this._folderTreeCellId) {
 88 			if (parentLabelCell) {
 89 				parentLabelCell.innerHTML = this._folderLocationLabel;
 90 				parentLabelCell.colSpan = 2;
 91 				parentValueCell.innerHTML = '';
 92 			}
 93 			var overviewParams = {
 94 				appName:		params.appName,
 95 				overviewId:		this.toString() + (params.appName || ""),
 96 				treeIds:		this._treeIds,
 97 				omit:			this._omit,
 98 				fieldId:		this._folderTreeCellId,
 99 				overviewTrees:	[this._organizerType],
100 	            treeStyle:      this._treeStyle
101 			};
102 			var overview = this._setOverview(overviewParams);
103 			overview.removeAttribute('aria-label');
104 			overview.setAttribute('aria-labelledby', this._htmlElId + '_parentLabel');
105 
106 			if (this._folderTreeView) {
107 				// bug #18533 - always make sure header item is visible in "New" dialog
108 				this._folderTreeView.getHeaderItem().setVisible(true, true);
109 
110 				if (!folder || this._omit[folder.nId] || folder.nId == ZmOrganizer.ID_ROOT) {
111 					folder = appCtxt.getFolderTree().root; //default to root if no folder passed, the folder is omitted from the overview. (I don't get the last option, but it was there so I keep it - it's already root)
112 				}
113 				var ti = this._folderTreeView.getTreeItemById(folder.id);
114 				if (ti) {
115 					this._folderTreeView.setSelection(ti, true, null, true);
116 				}
117 				if (folder.nId == ZmOrganizer.ID_ROOT) {
118 					var sid = ZmOrganizer.getSystemId(folder.id);
119 					var ti = this._folderTreeView.getTreeItemById(sid);
120 					if (ti) {
121 						ti.setExpanded(true);
122 					}
123 				}
124 			}
125 		}
126 	}
127 
128     if (this._colorSelect) {
129         var defaultColorCode = ZmOrganizer.DEFAULT_COLOR[this._organizerType],
130             defaultColor = ZmOrganizer.COLOR_VALUES[defaultColorCode],
131             colorMenu = this._colorSelect.getMenu(),
132             moreColorMenu;
133         if(colorMenu) {
134             moreColorMenu = (colorMenu.toString() == "ZmMoreColorMenu") ? colorMenu : colorMenu._getMoreColorMenu();
135             if(moreColorMenu) moreColorMenu.setDefaultColor(defaultColor);
136         }
137 
138         var icon = null;
139         var orgType = this._organizerType;
140         var orgClass = ZmOrganizer.ORG_CLASS[orgType];
141         if (orgClass) {
142 			//to fix bug 55320 - got rid of the calling getIcon on the prototype hack - that caused isRemote to set _isRemote on the prototype thus causing every object to have it by default set.
143             //bug 55491: pass tmp. organizer id to make sure this._isRemote is not true by default.
144 			var sample = new window[orgClass]({id:Dwt.getNextId()}); //get a sample object just for the icon
145 			icon = sample.getIcon();
146         }
147 
148         this._colorSelect.setImage(icon);
149         this._colorSelect.setValue(ZmOrganizer.DEFAULT_COLOR[orgType]);
150     }
151 
152 	var ovContainer = appCtxt.multiAccounts && this._opc.getOverviewContainer(this.toString());
153 	if (ovContainer) {
154 		if (!folder || (folder && folder.nId == ZmOrganizer.ID_ROOT)) {
155 			var acct = account || appCtxt.getActiveAccount();
156 			ovContainer.setSelection(ovContainer.getHeaderItem(acct));
157 		} else {
158 			var overviewId = appCtxt.getOverviewId(this.toString(), account);
159 			var overview = ovContainer.getOverview(overviewId);
160 			var treeView = overview && overview.getTreeView(this._organizerType);
161 			if (treeView) {
162 				ovContainer.deselectAll();
163 				var ti = treeView.getTreeItemById(folder.id);
164 				treeView.setSelection(ti);
165 			}
166 		}
167 
168 		ovContainer.expandAccountOnly(account);
169 	}
170 
171 	ZmDialog.prototype.popup.call(this);
172 };
173 
174 /**
175  * Resets the dialog.
176  * 
177  * @param	{ZmAccount}	account		the account
178  */
179 ZmNewOrganizerDialog.prototype.reset = function(account) {
180 
181 	ZmDialog.prototype.reset.apply(this, arguments);
182 
183 	if (this._remoteCheckboxField) {
184 		this._remoteCheckboxField.checked = false;
185 		var urlRow = document.getElementById(this._remoteCheckboxFieldId + "URLrow");
186 		if (urlRow) {
187 			urlRow.style.display = "none";
188 		}
189 	}
190 
191 	if (this._urlField) {
192 		this._urlField.value = "";
193 		this._urlField.noTab = true;
194 	}
195 
196 	if (appCtxt.multiAccounts) {
197 		this._account = account;
198 	} else {
199 		this._account = null;
200 	}
201 };
202 
203 
204 //
205 // Protected methods
206 //
207 
208 ZmNewOrganizerDialog.prototype._getRemoteLabel =
209 function() {
210 	return ZmMsg.subscribeToFeed;
211 };
212 
213 // create html
214 
215 ZmNewOrganizerDialog.prototype._contentHtml = 
216 function() {
217 	var html = [];
218 	var idx = 0;
219 	html[idx++] = "<table class='ChooserDialog ZPropertySheet' cellspacing='6' >";
220 	idx = this._createStandardContentHtml(html, idx);
221 	idx = this._createExtraContentHtml(html, idx);
222 	html[idx++] = "</table>";
223 	return html.join("");
224 };
225 
226 ZmNewOrganizerDialog.prototype._createStandardContentHtml =
227 function(html, idx) {
228 	idx = this._createNameContentHtml(html, idx);
229 	if (this._organizerType != ZmOrganizer.FOLDER || (this._organizerType == ZmOrganizer.FOLDER && appCtxt.get(ZmSetting.MAIL_FOLDER_COLORS_ENABLED))) {
230 		idx = this._createColorContentHtml(html, idx);
231 	}
232 	return idx;
233 };
234 
235 ZmNewOrganizerDialog.prototype._createNameContentHtml =
236 function(html, idx) {
237 	this._nameFieldId = this._htmlElId + "_name";
238 	html[idx++] = AjxTemplate.expand("share.Dialogs#ZmNewOrgDialogName", {id:this._htmlElId});
239 	return idx;
240 };
241 
242 ZmNewOrganizerDialog.prototype._createColorContentHtml =
243 function(html, idx) {
244 	this._colorSelectId = this._htmlElId + "_colorSelect";
245 	html[idx++] = AjxTemplate.expand("share.Dialogs#ZmNewOrgDialogColor", {id:this._htmlElId});
246 	return idx;
247 };
248 
249 ZmNewOrganizerDialog.prototype._createExtraContentHtml =
250 function(html, idx) {
251 	idx = this._createRemoteContentHtml(html, idx);
252 	idx = this._createFolderContentHtml(html, idx);
253 	return idx;
254 };
255 
256 ZmNewOrganizerDialog.prototype._createRemoteContentHtml = function(html, idx) {
257 
258 	this._remoteCheckboxFieldId = this._htmlElId + "_remote";
259 
260 	var subs = {
261 		id: this._htmlElId,
262 		remoteLabel: this._getRemoteLabel()
263 	};
264 	html[idx++] = AjxTemplate.expand("share.Dialogs#ZmNewOrgDialogRemote", subs);
265 	return idx;
266 };
267 
268 ZmNewOrganizerDialog.prototype._createFolderContentHtml =
269 function(html, idx) {
270 	this._folderTreeCellId = this._htmlElId + "_folderTree";
271 	html[idx++] = AjxTemplate.expand("share.Dialogs#ZmNewOrgDialogFolder", {id:this._htmlElId});
272 	return idx;
273 };
274 
275 // setup dwt controls
276 
277 ZmNewOrganizerDialog.prototype._setupControls =
278 function() {
279 	this._setupStandardControls();
280 	this._setupExtraControls();
281 };
282 
283 ZmNewOrganizerDialog.prototype._setupStandardControls =
284 function() {
285 	this._setupNameControl();
286 	this._setupColorControl();
287 };
288 
289 ZmNewOrganizerDialog.prototype._setupNameControl =
290 function() {
291 	this._setNameField(this._nameFieldId);
292 };
293 
294 ZmNewOrganizerDialog.prototype._setupColorControl =
295 function() {
296     var el = document.getElementById(this._colorSelectId);
297 	this._colorSelect = new ZmColorButton({
298 		parent:         this,
299 		parentElement:  el,
300 		labelId:        this._htmlElId + '_lblColor'
301 	});
302 };
303 
304 ZmNewOrganizerDialog.prototype._setupExtraControls =
305 function() {
306 	this._setupRemoteControl();
307 	this._setupFolderControl();
308 };
309 
310 ZmNewOrganizerDialog.prototype._setupRemoteControl =
311 function() {
312 	this._remoteCheckboxField = document.getElementById(this._remoteCheckboxFieldId);
313 	if (this._remoteCheckboxField) {
314 		this._urlField = document.getElementById(this._remoteCheckboxFieldId + "URLfield");
315 		Dwt.setHandler(this._remoteCheckboxField, DwtEvent.ONCLICK, this._handleCheckbox.bind(this));
316 	}
317 };
318 
319 ZmNewOrganizerDialog.prototype._setupFolderControl =
320 function() {
321 	if (!this._folderTreeCellId) { return; }
322 	
323 	this._treeIds = [this._organizerType];
324 
325 	this._omit = {};
326 	this._omit[ZmFolder.ID_SPAM] = true;
327 	this._omit[ZmFolder.ID_DRAFTS] = true;
328 	this._omit[ZmFolder.ID_SYNC_FAILURES] = true;
329 	this._omit[ZmFolder.ID_OUTBOX] = true;
330 
331 	//Bug#68799: no special handling needed for sync issues folder
332 	/*var folderTree = appCtxt.getFolderTree();
333 	var syncIssuesFolder = folderTree ? folderTree.getByName(ZmFolder.SYNC_ISSUES) : null;
334 	if (syncIssuesFolder) {
335 		this._omit[syncIssuesFolder.id] = true;
336 	}*/
337 	this._omit[ZmOrganizer.ID_ZIMLET] = true;
338 };
339 
340 // other
341 
342 ZmNewOrganizerDialog.prototype._renderOverview =
343 function(overview, treeIds, omit, noRootSelect) {
344 	this._setupFolderControl();	// reset in case we changed accounts (family mailbox)
345 	ZmDialog.prototype._renderOverview.apply(this, arguments);
346 	this._folderTreeView = overview.getTreeView(this._organizerType);
347 };
348 
349 ZmNewOrganizerDialog.prototype._getOverviewOrOverviewContainer =
350 function() {
351 	if (appCtxt.multiAccounts) {
352 		return this._opc.getOverviewContainer(this.toString());
353 	}
354 	return this._opc.getOverview(this._curOverviewId);
355 
356 };
357 
358 
359 /** 
360  * Checks the input for validity and returns the following array of values:
361  * <ul>
362  * <li> parentFolder
363  * <li> name
364  * <li> color
365  * <li> URL
366  * </ul>
367  */
368 ZmNewOrganizerDialog.prototype._getFolderData =
369 function() {
370 	// make sure a parent was selected
371 	var ov = this._getOverviewOrOverviewContainer();
372 
373 	var parentFolder = this._parentFolder || (ov && ov.getSelected()) || appCtxt.getFolderTree(this._account).root;
374 
375 	if (this._isGlobalSearch) {
376 		//special case for global search (only possible if this is ZmNewSearchDialog
377 		parentFolder = appCtxt.getById(ZmOrganizer.ID_ROOT);
378 	}
379 
380 	// check name for presence and validity
381 	var name = AjxStringUtil.trim(this._nameField.value);
382 	var msg = ZmFolder.checkName(name, parentFolder);
383 
384 	// make sure parent doesn't already have a child by this name
385 	if (!msg && parentFolder.hasChild(name)) {
386         var folderType = appCtxt.getFolderTree(appCtxt.getActiveAccount()).getFolderTypeByName(name);
387 		msg = AjxMessageFormat.format(this._folderNameAlreadyExistsMsg, [name,ZmMsg[folderType.toLowerCase()]]);
388 	}
389 
390 	var color = null;
391 	if (!msg && this._colorSelectId) {
392 		color = this._colorSelect.getValue();
393 	}
394 
395 	var url = null;
396 	if (!msg && this._remoteCheckboxField) {
397 		url = this._remoteCheckboxField.checked ? this._urlField.value : null;
398 		if (url || url != null) {
399 			msg = ZmOrganizer.checkUrl(url);
400 		}
401 	}
402 
403 	if (!msg && parentFolder.disallowSubFolder) {
404 		msg = AjxMessageFormat.format(ZmMsg.errorSubFolderNotAllowed, parentFolder.name);
405 	}
406 
407     if (msg) {
408         return this._showError(msg);
409     }
410 
411 	var account = appCtxt.multiAccounts ? parentFolder.getAccount() : null;
412 	var params = {l:parentFolder.id, name:name, color:color, url:url, account:account};
413     if (String(color).match(/^#/)) {
414         params.rgb = color;
415         delete params.color;
416     }
417     return params;
418 };
419 
420 ZmNewOrganizerDialog.prototype._getTabGroupMembers =
421 function() {
422 	var list = [this._nameField];
423 	if (this._colorSelect) {
424 		list.push(this._colorSelect);
425 	}
426 	if (this._remoteCheckboxField) {
427 		list.push(this._remoteCheckboxField);
428 		if (this._urlField) {
429 			list.push(this._urlField);
430 		}
431 	}
432 	if (this._overview[this._curOverviewId]) {
433 		list.push(this._overview[this._curOverviewId]);
434 	}
435 	return list;
436 };
437 
438 // dwt event listeners
439 
440 ZmNewOrganizerDialog.prototype._okButtonListener =
441 function(ev) {
442 	var results = this._getFolderData();
443 	if (results) {
444 		DwtDialog.prototype._buttonListener.call(this, ev, results);
445 	}
446 };
447 
448 ZmNewOrganizerDialog.prototype._enterListener =
449 function(ev) {
450 	var results = this._getFolderData();
451 	if (results) {
452 		this._runEnterCallback(results);
453 	}
454 };
455 
456 
457 // html event handlers
458 
459 ZmNewOrganizerDialog.prototype._handleCheckbox = function(event) {
460 
461 	event = event || window.event;
462 	var target = DwtUiEvent.getTarget(event);
463 	var urlRow = document.getElementById(target.id + "URLrow");
464 	urlRow.style.display = target.checked ? (AjxEnv.isIE ? "block" : "table-row") : "none";
465 	if (this._urlField) {
466 		if (target.checked) {
467 			this._urlField.focus();
468 		}
469 		this._urlField.noTab = !target.checked;
470 	}
471 };
472 
473 ZmNewOrganizerDialog.prototype.setRemoteURL =
474 function(url) {
475     this._remoteCheckboxField.checked = true;
476     this._urlField.value = url;
477     var urlRow = document.getElementById(this._remoteCheckboxFieldId + "URLrow");
478 	var urlField= document.getElementById(this._remoteCheckboxFieldId + "URLfield");
479 	urlRow.style.display = AjxEnv.isIE ? "block" : "table-row";
480 
481 };
482