1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 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) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * @overview
 26  * This file contains a contact helper class.
 27  * 
 28  */
 29 
 30 /**
 31  * Default constructor for helper class.
 32  * @class
 33  * Miscellaneous contacts-related utility functions. So far, mostly things that
 34  * {@link ZmContactPicker} and {@link ZmGroupView} both need to perform a contacts search and
 35  * display results in a list view.
 36  *
 37  * @author Conrad Damon
 38  */
 39 ZmContactsHelper = function() {};
 40 
 41 /**
 42  * Performs a contact search (in either personal contacts or in the GAL) and populates
 43  * the source list view with the results.
 44  *
 45  * @param	{Hash}	params		a hash of parameters
 46  * @param {Object}	params.obj			the object that is doing the search
 47  * @param {String}	params.query			the query string to search on
 48  * @param {String}	params.queryHint		the query hint (i.e. searching shared folders)
 49  * @param {Boolean}	params.ascending		if <code>true</code>, sort in ascending order
 50  * @param {int}	params.lastId		the ID of last item displayed (for pagination)
 51  * @param {String}	params.lastSortVal	the value of sort field for above item
 52  * @param {AjxCallback}	params.respCallback	the callback to call once response comes back from server
 53  * @param {AjxCallback}	params.errorCallback	the callback to call if error returned from server
 54  * @param {String}	params.accountName	the account to make search request on behalf of
 55  * @param {Array}   params.conds	the conds to restrict the search by (array of {attr:"", op:"", value:""} hashes)
 56  */
 57 ZmContactsHelper.search =
 58 function(params) {
 59 	var o = params.obj;
 60 	if (o._searchButton) {
 61 		o._searchButton.setEnabled(false);
 62 	}
 63 
 64 	params.sortBy = params.ascending ? ZmSearch.NAME_ASC : ZmSearch.NAME_DESC;
 65 	params.types = AjxVector.fromArray([ZmItem.CONTACT]);
 66 	params.offset = params.offset || 0;
 67 	params.limit = ZmContactsApp.SEARCHFOR_MAX;
 68 	params.contactSource = o._contactSource;
 69 	params.field = "contact";
 70 
 71 	var search = new ZmSearch(params);
 72 	search.execute({callback:params.respCallback, errorCallback:params.errorCallback});
 73 };
 74 
 75 /**
 76  * Take the contacts and create a list of their email addresses (a contact may have more than one)
 77  * 
 78  * @private
 79  */
 80 ZmContactsHelper._processSearchResponse = 
 81 function(resp, includeContactsWithNoEmail) {
 82 	var vec = resp.getResults(ZmItem.CONTACT);
 83 
 84 	// Take the contacts and create a list of their email addresses (a contact may have more than one)
 85 	var list = [];
 86 	var a = vec.getArray();
 87 	for (var i = 0; i < a.length; i++) {
 88 		var contact = a[i];
 89 		if (contact.isGroup() && !contact.isDL) {
 90 			var members = contact.getGroupMembers().good.toString(AjxEmailAddress.SEPARATOR);
 91 			ZmContactsHelper._addContactToList(list, contact, members, true);
 92 		} else {
 93 			var emails = contact.isGal ? [contact.getEmail()] : contact.getEmails();
 94 			for (var j = 0; j < emails.length; j++) {
 95 				ZmContactsHelper._addContactToList(list, contact, emails[j]);
 96 			}
 97 			if (includeContactsWithNoEmail && emails.length == 0) {
 98 				ZmContactsHelper._addContactToList(list, contact, null);
 99 			}
100 		}
101 	}
102 	
103 	return list;
104 };
105 
106 /**
107  * @private
108  */
109 ZmContactsHelper._addContactToList = 
110 function(list, contact, addr, isGroup) {
111 
112 	var email = ZmContactsHelper._wrapContact(contact, addr, isGroup);  
113 	list.push(email);
114 };
115 
116 /**
117  * wrapps the contact inside a AjxEmailAddress object, and adds a couple extra fields to the AjxEmailAddress instance (value, contact, icon [which I'm not sure is used])
118  *
119  * @param contact
120  * @param addr {String} optional.
121  * @param isGroup
122  */
123 ZmContactsHelper._wrapContact =
124 function(contact, addr, isGroup) {
125 
126 	addr = addr || contact.getEmail();
127 	var fileAs = contact.getFileAs();
128 	var name = (fileAs != addr) ? fileAs : "";  //todo ??? this is weird.
129 	var	type = contact.isGal ? ZmContact.GROUP_GAL_REF : ZmContact.GROUP_CONTACT_REF;
130 	var	value = contact.isGal ? (contact.ref || contact.id) : contact.id;  //defaulting to contact.id in the gal case since from GetContactsResponse the ref is not returned and we can end up with it cached without the ref. Probably need to fix that.
131 	var displayName = contact.getFullNameForDisplay();
132 
133 	var email = new AjxEmailAddress(addr, type, name, displayName, isGroup);
134 
135 	email.value = value;
136 	email.id = Dwt.getNextId();
137 	email.__contact = contact;
138 	email.icon = contact.getIcon();
139 	if (contact.isDL) {
140 		email.isGroup = true;
141 		email.canExpand = contact.canExpand;
142 		var ac = window.parentAppCtxt || window.appCtxt;
143 		ac.setIsExpandableDL(addr, email.canExpand);
144 	}
145 	return email;
146 };
147 
148 /**
149  * wrapps the inline address (there's no real ZmContact object) inside AjxEmailAddress and adds the value attribute to it.
150  * this is so we treat real contacts and inline contacts consistently throughout the rest of the code.
151  *
152  * @param value  {String} - the inline email address and/or name (e.g. "john doe <john@doe.com>" or "john@doe.com")
153  */
154 ZmContactsHelper._wrapInlineContact =
155 function(value) {
156 	var email = AjxEmailAddress.parse(value); //from legacy data at least (not sure about new), the format might be something like "Inigo Montoya <inigo@theprincessbride.com>" so we have to parse.
157 	if (!email) {
158 		//this can happen when creating inline in contact group edit, and the user did not suply email address in the inline value
159 		email = new AjxEmailAddress(value, null, value);
160 	}
161 	email.type = ZmContact.GROUP_INLINE_REF;
162 	email.value = value;
163 	email.id = Dwt.getNextId();
164 	return email;
165 };
166 
167 
168 /**
169  * The items are AjxEmailAddress objects
170  * 
171  * @private
172  */
173 ZmContactsHelper._getEmailField =
174 function(html, idx, item, field, colIdx) {
175 	if (field == ZmItem.F_TYPE) {
176 		html[idx++] = AjxImg.getImageHtml(item.icon);
177 	} else if (field == ZmItem.F_NAME) {
178 		html[idx++] = '<span style="white-space:nowrap">';
179 		html[idx++] = AjxStringUtil.htmlEncode(item.name || ZmMsg.noName);
180 		html[idx++] = "</span>";
181 	} else if (field == ZmItem.F_EMAIL) {
182 		html[idx++] = AjxStringUtil.htmlEncode(item.address);
183 	} else if (field == ZmItem.F_DEPARTMENT) {
184 		if (item.__contact) {
185 			html[idx++] = AjxStringUtil.htmlEncode(ZmContact.getAttr(item.__contact, ZmContact.F_department));
186 		}
187 	}
188 	return idx;
189 };
190