Commit c829309e authored by Gabriel Guardincerri's avatar Gabriel Guardincerri Committed by gguardin

Created VCardProvider to integrate with Clearspace. JM-1227

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/branches@9995 b35dd754-fafc-0310-a699-88a17e54d16e
parent eac1138a
......@@ -48,7 +48,7 @@ public class ClearspaceUserProvider implements UserProvider {
username = username.substring(0, username.lastIndexOf("@"));
}
// Translate the response
// Translate the response
return translate(getUserByUsername(username));
}
......@@ -63,7 +63,7 @@ public class ClearspaceUserProvider implements UserProvider {
String path = USER_URL_PREFIX + "users/";
// Creates the XML with the data
Document groupDoc = DocumentHelper.createDocument();
Document groupDoc = DocumentHelper.createDocument();
Element rootE = groupDoc.addElement("createUserWithUser");
Element userE = rootE.addElement("user");
......@@ -89,7 +89,7 @@ public class ClearspaceUserProvider implements UserProvider {
Element enabledE = userE.addElement("enabled");
enabledE.addText("true");
Element user = manager.executeRequest(POST, path, groupDoc.asXML());
return translate(user);
......@@ -112,7 +112,7 @@ public class ClearspaceUserProvider implements UserProvider {
manager.executeRequest(DELETE, path);
} catch (UserNotFoundException gnfe) {
// it is ok, the user doesn't exist "anymore"
// it is OK, the user doesn't exist "anymore"
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Unexpected error", e);
......@@ -153,11 +153,10 @@ public class ClearspaceUserProvider implements UserProvider {
Collection<String> usernames = new ArrayList<String>();
// Filters the user
//TODO they aren't in alphabetical order.
for (int i = startIndex; (i < startIndex + numResults) && (i < usernamesAll.length); i++) {
usernames.add(usernamesAll[i]);
usernames.add(usernamesAll[i]);
}
return new UserCollection(usernames.toArray(new String[usernames.size()]));
}
......@@ -167,91 +166,106 @@ public class ClearspaceUserProvider implements UserProvider {
throw new UnsupportedOperationException();
}
try {
Element user = getUserByUsername(username);
Element modifiedUser = modifyUser(user.element("return"), "name", name);
// Creates the params
Element userUpdateParams = getUserUpdateParams(username);
String path = USER_URL_PREFIX + "users";
manager.executeRequest(PUT, path, modifiedUser.asXML());
// Modifies the attribute of the user
String[] path = new String[]{"user", "name"};
WSUtils.modifyElementText(userUpdateParams, path, name);
} catch (UserNotFoundException e) {
throw new UserNotFoundException("User with name " + username + " not found.");
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Unexpected error", e);
}
// Updates the user
updateUser(userUpdateParams);
}
public void setEmail(String username, String email) throws UserNotFoundException {
try {
Element user = getUserByUsername(username);
Element modifiedUser = modifyUser(user.element("return"), "email", email);
if (isReadOnly()) {
// Reject the operation since the provider is read-only
throw new UnsupportedOperationException();
}
String path = USER_URL_PREFIX + "users";
manager.executeRequest(PUT, path, modifiedUser.asXML());
// Creates the params
Element userUpdateParams = getUserUpdateParams(username);
} catch (UserNotFoundException e) {
throw new UserNotFoundException("User with name " + username + " not found.");
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Unexpected error", e);
}
// Modifies the attribute of the user
String[] path = new String[]{"user", "email"};
WSUtils.modifyElementText(userUpdateParams, path, email);
// Updates the user
updateUser(userUpdateParams);
}
public void setCreationDate(String username, Date creationDate) throws UserNotFoundException {
try {
Element user = getUserByUsername(username);
Element modifiedUser = modifyUser(user.element("return"), "creationDate", WSUtils.formatDate(creationDate));
if (isReadOnly()) {
// Reject the operation since the provider is read-only
throw new UnsupportedOperationException();
}
String path = USER_URL_PREFIX + "users";
manager.executeRequest(PUT, path, modifiedUser.asXML());
// Creates the params
Element userUpdateParams = getUserUpdateParams(username);
} catch (UserNotFoundException e) {
throw new UserNotFoundException("User with name " + username + " not found.");
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Unexpected error", e);
}
// Modifies the attribute of the user
String[] path = new String[]{"user", "creationDate"};
String newValue = WSUtils.formatDate(creationDate);
WSUtils.modifyElementText(userUpdateParams, path, newValue);
// Updates the user
updateUser(userUpdateParams);
}
public void setModificationDate(String username, Date modificationDate) throws UserNotFoundException {
try {
Element user = getUserByUsername(username);
Element modifiedUser = modifyUser(user.element("return"), "modificationDate", WSUtils.formatDate(modificationDate));
if (isReadOnly()) {
// Reject the operation since the provider is read-only
throw new UnsupportedOperationException();
}
// Creates the params
Element userUpdateParams = getUserUpdateParams(username);
// Modifies the attribute of the user
String[] path = new String[]{"user", "modificationDate"};
String newValue = WSUtils.formatDate(modificationDate);
WSUtils.modifyElementText(userUpdateParams, path, newValue);
// Updates the user
updateUser(userUpdateParams);
}
protected Element getUserUpdateParams(String username) throws UserNotFoundException {
// Creates the user update params element
Element userUpdateParams = DocumentHelper.createDocument().addElement("updateUser");
Element newUser = userUpdateParams.addElement("user");
// Gets the current user information
Element currentUser = getUserByUsername(username).element("return");
List<Element> userAttributes = currentUser.elements();
for (Element userAttribute : userAttributes) {
newUser.addElement(userAttribute.getName()).setText(userAttribute.getText());
}
return userUpdateParams;
}
protected void updateUser(Element userUpdateParams) throws UserNotFoundException {
try {
String path = USER_URL_PREFIX + "users";
manager.executeRequest(PUT, path, modifiedUser.asXML());
manager.executeRequest(PUT, path, userUpdateParams.asXML());
} catch (UserNotFoundException e) {
throw new UserNotFoundException("User with name " + username + " not found.");
throw new UserNotFoundException("User not found.");
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Unexpected error", e);
}
}
private Element modifyUser(Element user, String attributeName, String newValue) {
Document groupDoc = DocumentHelper.createDocument();
Element rootE = groupDoc.addElement("updateUser");
Element newUser = rootE.addElement("user");
List<Element> userAttributes = user.elements();
for (Element userAttribute : userAttributes) {
if (userAttribute.getName().equals(attributeName)) {
newUser.addElement(userAttribute.getName()).setText(newValue);
} else {
newUser.addElement(userAttribute.getName()).setText(userAttribute.getText());
}
}
return rootE;
}
public Set<String> getSearchFields() throws UnsupportedOperationException {
return new LinkedHashSet<String>(Arrays.asList("Username", "Name", "Email"));
}
public Collection<User> findUsers(Set<String> fields, String query) throws UnsupportedOperationException {
// Creates the XML with the data
Document groupDoc = DocumentHelper.createDocument();
Document groupDoc = DocumentHelper.createDocument();
Element rootE = groupDoc.addElement("searchProfile");
Element queryE = rootE.addElement("WSProfileSearchQuery");
Element keywords = queryE.addElement("keywords");
......@@ -269,8 +283,7 @@ public class ClearspaceUserProvider implements UserProvider {
List<String> usernames = new ArrayList<String>();
String path = SEARCH_URL_PREFIX + "searchProfile";
//TODO they aren't in alphabetical order.
//TODO get only the username field
//TODO create a service on CS to get only the username field
Element element = manager.executeRequest(GET, path);
List<Node> userNodes = (List<Node>) element.selectNodes("return");
......@@ -288,7 +301,7 @@ public class ClearspaceUserProvider implements UserProvider {
public Collection<User> findUsers(Set<String> fields, String query, int startIndex, int numResults) throws UnsupportedOperationException {
// Creates the XML with the data
Document groupDoc = DocumentHelper.createDocument();
Document groupDoc = DocumentHelper.createDocument();
Element rootE = groupDoc.addElement("searchBounded");
Element queryE = rootE.addElement("WSProfileSearchQuery");
Element keywords = queryE.addElement("keywords");
......@@ -306,10 +319,8 @@ public class ClearspaceUserProvider implements UserProvider {
List<String> usernames = new ArrayList<String>();
String path = SEARCH_URL_PREFIX + "searchProfile/" + startIndex + "/" + numResults;
//TODO they aren't in alphabetical order.
//TODO get only the username field
Element element = manager.executeRequest(GET, path);
//TODO create a service on CS to get only the username field
List<Node> userNodes = (List<Node>) element.selectNodes("return");
for (Node userNode : userNodes) {
String username = userNode.selectSingleNode("username").getText();
......@@ -327,7 +338,7 @@ public class ClearspaceUserProvider implements UserProvider {
if (readOnly == null) {
loadReadOnly();
}
// If it is null returns the most restrictive anwser.
// If it is null returns the most restrictive answer.
return (readOnly == null ? false : readOnly);
}
......@@ -346,7 +357,7 @@ public class ClearspaceUserProvider implements UserProvider {
Element element = manager.executeRequest(GET, path);
readOnly = Boolean.valueOf(getReturn(element));
} catch (Exception e) {
// if there is a problem, keep it null, maybe in the next call succes.
// if there is a problem, keep it null, maybe in the next call success.
}
}
......@@ -359,7 +370,7 @@ public class ClearspaceUserProvider implements UserProvider {
Node userNode = responseNode.selectSingleNode("return");
Node tmpNode;
// Gets the username
username = userNode.selectSingleNode("username").getText();
......@@ -394,9 +405,7 @@ public class ClearspaceUserProvider implements UserProvider {
}
// Creates the user
User user = new User(username, name, email, creationDate, modificationDate);
//TODO add other attributes, like user properties
return user;
return new User(username, name, email, creationDate, modificationDate);
}
private Element getUserByUsername(String username) throws UserNotFoundException {
......@@ -416,6 +425,4 @@ public class ClearspaceUserProvider implements UserProvider {
throw new UserNotFoundException("Error loading the user", e);
}
}
}
......@@ -12,524 +12,504 @@ package org.jivesoftware.openfire.clearspace;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import static org.jivesoftware.openfire.clearspace.ClearspaceManager.HttpType.*;
import static org.jivesoftware.openfire.clearspace.ClearspaceManager.HttpType.GET;
import static org.jivesoftware.openfire.clearspace.ClearspaceManager.HttpType.POST;
import static org.jivesoftware.openfire.clearspace.ClearspaceVCardTranslator.Action.DELETE;
import static org.jivesoftware.openfire.clearspace.ClearspaceVCardTranslator.Action.NO_ACTION;
import static org.jivesoftware.openfire.clearspace.WSUtils.getReturn;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.openfire.vcard.VCardProvider;
import org.jivesoftware.util.AlreadyExistsException;
import org.jivesoftware.util.NotFoundException;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.NotFoundException;
import java.util.List;
import java.util.StringTokenizer;
/**
* @author Daniel Henninger
* The ClearspaceLockOutProvider uses the UserService web service inside of Clearspace
* to retrieve, edit and delete user information from Clearspace. With this information the provider
* builds user's VCard.
*
* @author Gabriel Guardincerri
*/
public class ClearspaceVCardProvider implements VCardProvider {
private static final int CS_FIELD_ID_TITLE = 1;
private static final int CS_FIELD_ID_DEPARTMENT = 2;
private static final int CS_FIELD_ID_TIME_ZONE = 9;
private static final int CS_FIELD_ID_WORK_ADDRESS = 11;
private static final int CS_FIELD_ID_HOME_ADDRESS = 3;
private static final int CS_FIELD_ID_ALTERNATE_EMAIL = 10;
private static final int CS_FIELD_ID_URL = 5;
protected static final String PROFILE_URL_PREFIX = "profileService/";
protected static final String PROFILE_FIELDS_URL_PREFIX = "profileFieldService/";
protected static final String AVATAR_URL_PREFIX = "avatarService/";
private ClearspaceManager manager;
private Boolean avatarReadOnly;
private Boolean readOnly;
private boolean fieldsIDLoaded;
public ClearspaceVCardProvider() {
this.manager = ClearspaceManager.getInstance();
loadReadOnly();
// Tries to load the avatar read only information
loadAvatarReadOnly();
// Tries to load the default profile fields
loadDefaultProfileFields();
}
/**
* Loads the VCard with information from CS. It uses information from the user, the user profile and the avatar.
* With this 3 sources of informations it builds the VCard.
*
* @param username username of user to load VCard of
* @return the user's VCard
*/
public Element loadVCard(String username) {
// if the fields id are not loaded
if (!fieldsIDLoaded) {
// try to load them
loadDefaultProfileFields();
// if still not loaded then the operation could no be perform
if (!fieldsIDLoaded) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Error loading the profiles IDs");
}
}
try {
// Gets the user
User user = UserManager.getInstance().getUser(username);
// TODO should use the the user insted of requesting again
long userID = manager.getUserID(username);
// Requests the user profile
String path = PROFILE_URL_PREFIX + "profiles/" + userID;
Element profile = manager.executeRequest(GET, path);
// Gets the profiles information
Element profiles = getProfiles(userID);
// Gets the avatar information
Element avatar = getAvatar(userID);
// Translate the response
return translate(profile, user, avatar);
return ClearspaceVCardTranslator.getInstance().translateClearspaceInfo(profiles, user, avatar);
} catch (UnsupportedOperationException e) {
throw e;
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Error loading the user", e);
throw new UnsupportedOperationException("Error loading the vCard", e);
}
}
private Element getAvatar(long userID) throws Exception {
// Requests the user active avatar
String path = AVATAR_URL_PREFIX + "activeAvatar/" + userID;
Element avatar = manager.executeRequest(GET, path);
return avatar;
}
/**
* Creates the user's VCard. CS always has some information of users. So creating it is actually updating.
* Throws an UnsupportedOperationException if Clearspace can't save some changes. Returns the VCard after the change.
*
* @param username the username
* @param vCardElement the vCard to save.
* @return vCard as it is after the provider has a chance to adjust it.
* @throws AlreadyExistsException it's never throw by this implementation
* @throws UnsupportedOperationException if the provider does not support the
* operation.
*/
public Element createVCard(String username, Element vCardElement) throws AlreadyExistsException {
return saveVCard(username, vCardElement);
}
/**
* Updates the user vcard in Clearspace. Throws an UnsupportedOperationException if Clearspace can't
* save some changes. Returns the VCard after the change.
*
* @param username the username.
* @param vCardElement the vCard to save.
* @return vCard as it is after the provider has a chance to adjust it.
* @throws NotFoundException if the vCard to update does not exist.
* @throws UnsupportedOperationException if the provider does not support the
* operation.
*/
public Element updateVCard(String username, Element vCardElement) throws NotFoundException {
return saveVCard(username, vCardElement);
}
/**
* Always return false since Clearspace always support some changes.
*
* @return true
*/
public boolean isReadOnly() {
// Return always false, since some changes are always allowed
return false;
}
/**
* Returns true the user can modify the Avatar of Clearspace.
*
* @return if the Avatar of Clearspace can be modified.
*/
private boolean isAvatarReadOnly() {
if (avatarReadOnly == null) {
loadAvatarReadOnly();
}
return avatarReadOnly;
}
/**
* Saves the vCard of the user. First check if the change can be made,
* if not throws an UnsupportedOperationException.
* The VCard information is divided into 3 parts. First the preferred
* email and the user full name are stored into Clearspace user information.
* Second the avatar is stored into Clearspace avatar information. If the avatar was
* new or it was modified, a new avatar is created in Clearspace. If the avatar was
* deleted, in Clearspace the user won't have an active avatar.
*
* @param username the username of the user to update the avatar info to
* @param vCardElement the vCard with the new information
* @return the VCard with the updated information
* @throws UnsupportedOperationException if the provider does not support some changes.
*/
private Element saveVCard(String username, Element vCardElement) {
Log.debug("Saving VCARD: "+vCardElement.asXML());
if (Log.isDebugEnabled()) {
Log.debug("Saving VCARD: " + vCardElement.asXML());
}
Document profilesDoc = DocumentHelper.createDocument();
Element rootE = profilesDoc.addElement("setProfile");
if (!fieldsIDLoaded) {
loadDefaultProfileFields();
if (!fieldsIDLoaded) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Error loading the profiles IDs");
}
}
try {
long userID = manager.getUserID(username);
ClearspaceUserProvider userProvider = (ClearspaceUserProvider) UserManager.getUserProvider();
// Add the userID param
rootE.addElement("userID").setText(String.valueOf(userID));
// Gets the user params that can be used to update it
Element userUpdateParams = userProvider.getUserUpdateParams(username);
// Gets the element that contains the user information
Element userElement = userUpdateParams.element("user");
// Add all the profiles elements
Element profiles = rootE.addElement("profiles");
// Gets the profiles params that can be used to update them
Element profilesUpdateParams = getProfilesUpdateParams(userID);
//Element profilesElement = profilesUpdateParams.element("profiles");
// Add the Title
addNotEmptyProfile(vCardElement, "TITLE", CS_FIELD_ID_TITLE, profiles);
// Get the avatar params that can be used to create it. It doesn't have an avatar sub element.
Element avatarCreateParams = getAvatarCreateParams(userID);
// Add the Department
Element tmpElement = vCardElement.element("ORG");
if (tmpElement != null) {
addNotEmptyProfile(tmpElement, "ORGUNIT", CS_FIELD_ID_DEPARTMENT, profiles);
}
// Modifies the profile, user and avatar elements according to the VCard information.
ClearspaceVCardTranslator.Action[] actions;
actions = ClearspaceVCardTranslator.getInstance().translateVCard(vCardElement, profilesUpdateParams, userElement, avatarCreateParams);
// Add the home and work address
List<Element> addressElements = (List<Element>) vCardElement.elements("ADR");
if (addressElements != null) {
for (Element address : addressElements) {
if (address.element("WORK") != null) {
addProfile(CS_FIELD_ID_WORK_ADDRESS, marshallAddress(address), profiles);
} else if (address.element("HOME") != null) {
addProfile(CS_FIELD_ID_HOME_ADDRESS, marshallAddress(address), profiles);
}
}
// Throws an exception if the changes implies to modify something that is read only
if ((actions[1] != NO_ACTION && userProvider.isReadOnly()) || (actions[2] != NO_ACTION && isAvatarReadOnly())) {
throw new UnsupportedOperationException("ClearspaceVCardProvider: Invalid vcard changes.");
}
// Add the URL
addNotEmptyProfile(vCardElement, "URL", CS_FIELD_ID_URL, profiles);
// Add the prefered and alternative email address
List<Element> emailsElement = (List<Element>) vCardElement.elements("EMAIL");
if (emailsElement != null) {
for (Element email : emailsElement) {
if (email.element("PREF") == null) {
addNotEmptyProfile(email, "USERID", CS_FIELD_ID_ALTERNATE_EMAIL, profiles);
} else {
String emailAddress = email.elementTextTrim("USERID");
if (emailAddress != null && !"".equals(emailAddress)) {
// The prefered email is stored in the user
UserManager.getUserProvider().setEmail(username, emailAddress);
}
}
}
// Updates the profiles
if (actions[0] != NO_ACTION) {
updateProfiles(profilesUpdateParams);
}
// Add the Full name to the user
String fullName = vCardElement.elementTextTrim("FN");
if (fullName != null && !"".equals(fullName)) {
UserManager.getUserProvider().setName(username, fullName);
// Updates the user
if (actions[1] != NO_ACTION) {
userProvider.updateUser(userUpdateParams);
}
try {
String path = PROFILE_URL_PREFIX + "profiles";
Element group = manager.executeRequest(POST, path, rootE.asXML());
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Unexpected error", e);
}
try {
// TODO save some avatars in a cache to avoid getting them all the time
// Gets the user's current avatar
Element currAvatar = getAvatar(userID);
String[] currAvatarData = getAvatarContentTypeAndImage(currAvatar);
Element photoElement = vCardElement.element("PHOTO");
if (photoElement != null) {
String contentType = photoElement.elementTextTrim("TYPE");
String data = photoElement.elementTextTrim("BINVAL");
if (contentType == null && currAvatarData[0] != null) {
// new avatar
long avatarID = createAvatar(contentType, data, userID, username);
setActiveAvatar(userID, avatarID);
} else if (contentType != null && currAvatarData[0] == null) {
// delete
setActiveAvatar(userID, -1);
} else if ((contentType != null && !contentType.equals(currAvatarData[0])) ||
(data != null && !data.equals(currAvatarData[1]))) {
// modify
long avatarID = createAvatar(contentType, data, userID, username);
setActiveAvatar(userID, avatarID);
}
// Updates the avatar
if (actions[2] != NO_ACTION) {
// Set no active avatar to delete
if (actions[2] == DELETE) {
setActiveAvatar(userID, -1);
} else {
// else it was created or updated, on both cases it needs to be created and assigned as the active avatar.
long avatarID = createAvatar(avatarCreateParams);
setActiveAvatar(userID, avatarID);
}
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Error loading the user", e);
}
} catch (UserNotFoundException e) {
throw new UnsupportedOperationException("User not found", e);
} catch (UnsupportedOperationException e) {
throw e;
} catch (Exception e) {
throw new UnsupportedOperationException("Error saving the VCard", e);
}
return loadVCard(username);
}
private void setActiveAvatar(long userID, long avatarID) throws UserNotFoundException {
/**
* Deletes the profiles and avatar information of the user.
*
* @param username the username.
*/
public void deleteVCard(String username) {
ClearspaceUserProvider userProvider = (ClearspaceUserProvider) UserManager.getUserProvider();
if (userProvider.isReadOnly() || isAvatarReadOnly()) {
// Reject the operation since the provider is read-only
throw new UnsupportedOperationException();
}
long userID;
try {
Document profilesDoc = DocumentHelper.createDocument();
Element rootE = profilesDoc.addElement("setActiveAvatar");
rootE.addElement("userID").setText(String.valueOf(userID));
rootE.addElement("avatarID").setText(String.valueOf(avatarID));
userID = manager.getUserID(username);
} catch (UserNotFoundException gnfe) {
// it is OK, the user doesn't exist "anymore"
return;
}
// Requests the user active avatar
String path = AVATAR_URL_PREFIX + "activeAvatar/" + userID;
manager.executeRequest(POST, path, rootE.asXML());
deleteAvatar(userID);
} catch (UserNotFoundException e) {
throw e;
deleteProfiles(userID);
}
/**
* Deletes the profiles of the user.
*
* @param userID the user id.
*/
private void deleteProfiles(long userID) {
try {
String path = PROFILE_URL_PREFIX + "profiles/" + userID;
manager.executeRequest(ClearspaceManager.HttpType.DELETE, path);
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Error creating the avatar", e);
throw new UnsupportedOperationException("Unexpected error", e);
}
}
private long createAvatar(String contentType, String data, long userID, String username) throws UserNotFoundException {
/**
* Deletes the avatar of the user.
*
* @param userID the user id.
*/
private void deleteAvatar(long userID) {
try {
Document profilesDoc = DocumentHelper.createDocument();
Element rootE = profilesDoc.addElement("createAvatar");
rootE.addElement("ownerID").setText(String.valueOf(userID));
rootE.addElement("name").setText(String.valueOf(username));
rootE.addElement("contentType").setText(String.valueOf(contentType));
rootE.addElement("data").setText(String.valueOf(data));
// Requests the user active avatar
String path = AVATAR_URL_PREFIX + "createAvatar/" + userID;
Element avatar = manager.executeRequest(POST, path, rootE.asXML());
long id = Long.valueOf(avatar.element("return").element("WSAvatar").elementTextTrim("id"));
return id;
} catch (UserNotFoundException e) {
throw e;
String path = AVATAR_URL_PREFIX + "avatar/" + userID;
manager.executeRequest(ClearspaceManager.HttpType.DELETE, path);
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Error creating the avatar", e);
throw new UnsupportedOperationException("Unexpected error", e);
}
}
private void addNotEmptyProfile(Element elements, String elementName, int fieldID, Element profiles) {
String value = elements.elementTextTrim(elementName);
if (value != null && !"".equals(value)) {
addProfile(fieldID, value, profiles);
/**
* Makes the request to the webservice of Clearspace to update the profiles information.
*
* @param profilesUpdateParams the profiles params to use with the request.
*/
private void updateProfiles(Element profilesUpdateParams) {
// Try to save the profile changes
try {
String path = PROFILE_URL_PREFIX + "profiles";
manager.executeRequest(POST, path, profilesUpdateParams.asXML());
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Unexpected error", e);
}
}
private void addProfile(int fieldID, String value, Element profiles) {
Element profile = profiles.addElement("WSUserProfile");
profile.addElement("fieldID").setText(String.valueOf(fieldID));
profile.addElement("value").setText(value);
}
private String marshallAddress(Node address) {
StringBuilder sb = new StringBuilder();
addField(address, "STREET", "street1", sb);
addField(address, "EXTADD", "street2", sb);
addField(address, "LOCALITY", "city", sb);
addField(address, "REGION", "state", sb);
addField(address, "CTRY", "country", sb);
addField(address, "PCODE", "zip", sb);
Node tmpNode = address.selectSingleNode("HOME");
if (tmpNode != null) {
sb.append("type:HOME");
}
tmpNode = address.selectSingleNode("WORK");
if (tmpNode != null) {
sb.append("type:WORK");
}
/**
* Set the active avatar of the user.
*
* @param userID the userID
* @param avatarID the avatarID
*/
private void setActiveAvatar(long userID, long avatarID) {
try {
Document profilesDoc = DocumentHelper.createDocument();
Element rootE = profilesDoc.addElement("setActiveAvatar");
rootE.addElement("userID").setText(String.valueOf(userID));
rootE.addElement("avatarID").setText(String.valueOf(avatarID));
return sb.toString();
}
// Requests the user active avatar
String path = AVATAR_URL_PREFIX + "activeAvatar/" + userID;
private void addField(Node node, String nodeName, String fieldName, StringBuilder sb) {
Node tmpNode = node.selectSingleNode(nodeName);
if (tmpNode != null) {
sb.append(fieldName).append(":").append(tmpNode.getText());
manager.executeRequest(POST, path, rootE.asXML());
} catch (Exception e) {
throw new UnsupportedOperationException("Error setting the user's " + userID + " active avatar " + avatarID, e);
}
}
public void deleteVCard(String username) {
if (isReadOnly()) {
// Reject the operation since the provider is read-only
throw new UnsupportedOperationException();
}
long userID = -1;
/**
* Creates the avatar.
*
* @param avatarCreateParams the avatar information
* @return the new avatarID
*/
private long createAvatar(Element avatarCreateParams) {
try {
userID = manager.getUserID(username);
} catch (UserNotFoundException gnfe) {
// it is ok, the user doesn't exist "anymore"
return;
}
if (!isAvatarReadOnly()) {
try {
String path = AVATAR_URL_PREFIX + "avatar/" + userID;
manager.executeRequest(DELETE, path);
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Unexpected error", e);
}
// Requests the user active avatar
String path = AVATAR_URL_PREFIX + "createAvatar";
Element avatar = manager.executeRequest(POST, path, avatarCreateParams.asXML());
return Long.valueOf(avatar.element("return").element("WSAvatar").elementTextTrim("id"));
} catch (Exception e) {
throw new UnsupportedOperationException("Error creating the avatar", e);
}
}
/**
* Returns the profiles of the user.
*
* @param userID the user id.
* @return the profiles.
*/
private Element getProfiles(long userID) {
try {
// Requests the user profile
String path = PROFILE_URL_PREFIX + "profiles/" + userID;
manager.executeRequest(DELETE, path);
return manager.executeRequest(GET, path);
} catch (Exception e) {
// It is not supported exception, wrap it into an UnsupportedOperationException
throw new UnsupportedOperationException("Unexpected error", e);
throw new UnsupportedOperationException("Error getting the profiles of user: " + userID, e);
}
}
public boolean isReadOnly() {/*
if (readOnly == null) {
loadReadOnly();
/**
* Return the avatar of the user.
*
* @param userID the user id.
* @return the avatar.
*/
private Element getAvatar(long userID) {
try {
// Requests the user active avatar
String path = AVATAR_URL_PREFIX + "activeAvatar/" + userID;
return manager.executeRequest(GET, path);
} catch (Exception e) {
throw new UnsupportedOperationException("Error getting the avatar of user: " + userID, e);
}
return readOnly == null ? false : readOnly;*/
return true;
}
private void loadReadOnly() {
boolean userReadOnly = UserManager.getUserProvider().isReadOnly();
/**
* Tries to load the avatar read only info.
*/
private void loadAvatarReadOnly() {
try {
// See if the is read only
String path = AVATAR_URL_PREFIX + "userAvatarsEnabled";
Element element = manager.executeRequest(GET, path);
avatarReadOnly = !Boolean.valueOf(getReturn(element));
} catch (Exception e) {
// if there is a problem, keep it null, maybe in the next call succes.
return;
}
readOnly = userReadOnly || avatarReadOnly;
}
private Element translate(Element profile, User user, Element avatar) {
Document vCardDoc = DocumentHelper.createDocument();
Element vCard = vCardDoc.addElement("vCard", "vcard-temp");
addUserInformation(user, vCard);
addProfileInformation(profile, vCard);
addAvatarInformation(avatar, vCard);
return vCard;
}
private void addProfileInformation(Element profile, Element vCard) {
// Translate the profile XML
List<Node> fields = (List<Node>) profile.selectNodes("return");
/* Profile response sample
<ns1:getProfileResponse xmlns:ns1="http://jivesoftware.com/clearspace/webservices">
<return>
<fieldID>2</fieldID>
<value>RTC</value>
</return>
<return>
<fieldID>9</fieldID>
<value>-300</value>
</return>
<return>
<fieldID>11</fieldID>
<value>street1:San Martin,street2:1650,city:Cap Fed,state:Buenos Aires,country:Argentina,zip:1602,type:HOME</value>
</return>
<return>
<fieldID>1</fieldID>
<value>Mr.</value>
</return>
<return>
<fieldID>3</fieldID>
<value>street1:Alder 2345,city:Portland,state:Oregon,country:USA,zip:32423,type:WORK</value>
</return>
<return>
<fieldID>10</fieldID>
<values>gguardin@gmail.com|work</values>
</return>
<return>
<fieldID>5</fieldID>
<values>http://www.gguardin.com.ar</values>
</return>
</ns1:getProfileResponse>
*/
for (Node field : fields) {
int fieldID = Integer.valueOf(field.selectSingleNode("fieldID").getText());
// The value name of the value field could be value or values
Node fieldNode = field.selectSingleNode("value");
if (fieldNode == null) {
fieldNode = field.selectSingleNode("values");
}
// if it is an empty field, continue with the next field
if (fieldNode == null) {
continue;
}
// get the field value
String fieldValue = fieldNode.getText();
switch (fieldID) {
case CS_FIELD_ID_TITLE:
vCard.addElement("TITLE").setText(fieldValue);
break;
case CS_FIELD_ID_DEPARTMENT:
vCard.addElement("ORG").addElement("ORGUNIT").setText(fieldValue);
break;
case CS_FIELD_ID_TIME_ZONE:
//TODO check if the time zone is ISO
vCard.addElement("TZ").setText(fieldValue);
break;
case CS_FIELD_ID_WORK_ADDRESS:
Element workAdr = vCard.addElement("ADR");
workAdr.addElement("WORK");
addAddress(fieldValue, workAdr);
break;
case CS_FIELD_ID_HOME_ADDRESS:
Element homeAdr = vCard.addElement("ADR");
homeAdr.addElement("HOME");
addAddress(fieldValue, homeAdr);
break;
case CS_FIELD_ID_URL:
vCard.addElement("URL").setText(fieldValue);
break;
case CS_FIELD_ID_ALTERNATE_EMAIL:
vCard.addElement("EMAIL").addElement("USERID").setText(fieldValue);
break;
}
// if there is a problem, keep it null, maybe next call success.
Log.warn("Error loading the avatar read only information", e);
}
}
private void addUserInformation(User user, Element vCard) {
// The name could be null (if in Clearspace the name is not visible in Openfire it is null)
if (user.getName() != null && !"".equals(user.getName().trim())) {
vCard.addElement("FN").setText(user.getName());
}
/**
* Tries to load the default profiles fields info.
*/
private void loadDefaultProfileFields() {
try {
String path = PROFILE_FIELDS_URL_PREFIX + "fields";
Element defaultFields = manager.executeRequest(GET, path);
// Email is mandatory, but may be invisible
if (user.getEmail() != null && !"".equals(user.getName().trim())) {
Element email = vCard.addElement("EMAIL");
email.addElement("PREF");
email.addElement("USERID").setText(user.getEmail());
// TODO emails == jabber id? jabber id is ok?
vCard.addElement("JABBERID").setText(user.getEmail());
ClearspaceVCardTranslator.getInstance().initClearspaceFieldsId(defaultFields);
fieldsIDLoaded = true;
} catch (Exception e) {
// if there is a problem, keep it null, maybe next call success.
Log.warn("Error loading the default profiles fields", e);
}
}
private void addAvatarInformation(Element avatarResponse, Element vCard) {
String[] avatarData = getAvatarContentTypeAndImage(avatarResponse);
/**
* Returns an element that can be used as a parameter to create an avatar.
* This element has the user's avatar information.
*
* @param userID the id of user.
* @return the element with that can be used to create an Avatar.
* @throws UserNotFoundException if the userID is invalid.
* @throws Exception if there is problem doing the request.
*/
private Element getAvatarCreateParams(long userID) throws Exception {
if (avatarData[0] != null && avatarData[1] != null) {
// Add the avatar to the vCard
Element photo = vCard.addElement("PHOTO");
photo.addElement("TYPE").setText(avatarData[0]);
photo.addElement("BINVAL").setText(avatarData[1]);
}
}
// Creates response element
Element avatarCreateParams = DocumentHelper.createDocument().addElement("createAvatar");
private String[] getAvatarContentTypeAndImage(Element avatarResponse) {
String[] result = new String[2];
// Gets current avatar
Element avatarResponse = getAvatar(userID);
// Translates from the response to create params
Element avatar = avatarResponse.element("return");
if (avatar != null) {
// Sets the owner
avatarCreateParams.addElement("ownerID").setText(avatar.elementText("owner"));
// Sets the attachment values
Element attachment = avatar.element("attachment");
if (attachment != null) {
result[0] = attachment.element("contentType").getText();
result[1] = attachment.element("data").getText();
avatarCreateParams.addElement("name").setText(attachment.elementText("name"));
avatarCreateParams.addElement("contentType").setText(attachment.elementText("contentType"));
avatarCreateParams.addElement("data").setText(attachment.elementText("data"));
}
}
return result;
return avatarCreateParams;
}
private void addAddress(String address, Element addressE) {
StringTokenizer strTokenize = new StringTokenizer(address, ",");
while(strTokenize.hasMoreTokens()) {
String token = strTokenize.nextToken();
int index = token.indexOf(":");
String field = token.substring(0, index);
String value = token.substring(index + 1);
/**
* Returns an element that can be used as a parameter to modify the user profiles.
* This element has the user's avatar information.
*
* @param userID the id of user.
* @return the element with that can be used to create an Avatar.
* @throws UserNotFoundException if the userID is invalid.
* @throws Exception if there is problem doing the request.
*/
private Element getProfilesUpdateParams(long userID) throws Exception {
Element params = DocumentHelper.createDocument().addElement("setProfile");
if ("street1".equals(field)) {
addressE.addElement("STREET").setText(value);
// Add the userID param
params.addElement("userID").setText(String.valueOf(userID));
} else if ("street2".equals(field)) {
addressE.addElement("EXTADD").setText(value);
// Gets current profiles to merge the information
Element currentProfile = getProfiles(userID);
} else if ("city".equals(field)) {
addressE.addElement("LOCALITY").setText(value);
// Adds the current profiles to the new profile
addProfiles(currentProfile, params);
} else if ("state".equals(field)) {
addressE.addElement("REGION").setText(value);
return params;
}
} else if ("country".equals(field)) {
addressE.addElement("CTRY").setText(value);
/**
* Adds the profiles elements from one profile to the other one.
*
* @param currentProfile the profile with the information.
* @param newProfiles the profile to copy the information to.
*/
private void addProfiles(Element currentProfile, Element newProfiles) {
} else if ("zip".equals(field)) {
addressE.addElement("PCODE").setText(value);
// Gets current fields
List<Element> fields = (List<Element>) currentProfile.elements("return");
} else if ("type".equals(field)) {
if ("HOME".equals(value)) {
addressE.addElement("HOME");
} else if ("WORK".equals(value)) {
addressE.addElement("WORK");
// Iterate over current fields
for (Element field : fields) {
// Get the fieldID and values
String fieldID = field.elementText("fieldID");
// The value name of the value field could be value or values
Element value = field.element("value");
boolean multiValues = false;
if (value == null) {
value = field.element("values");
if (value != null) {
multiValues = true;
}
}
}
}
public boolean isAvatarReadOnly() {
return avatarReadOnly;
// Don't add empty field. Field id 0 means no field.
if ("0".equals(fieldID)) {
continue;
}
// Adds the profile to the new profiles element
Element newProfile = newProfiles.addElement("profiles");
newProfile.addElement("fieldID").setText(fieldID);
// adds the value if it is not empty
if (value != null) {
if (multiValues) {
newProfile.addElement("values").setText(value.getText());
} else {
newProfile.addElement("value").setText(value.getText());
}
}
}
}
}
/**
* $Revision$
* $Date$
*
* Copyright (C) 2006 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.openfire.clearspace;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.user.User;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
* @author Gabriel Guardincerri
*/
class ClearspaceVCardTranslator {
// Represents the type of action that was performed.
enum Action {
MODIFY, CREATE, UPDATE, DELETE, NO_ACTION
}
/**
* Represents the default fields of Clearspace, this is only a subset of the fields. It only includes
* the fields that have in common with VCards.
*/
enum ClearspaceField {
TITLE("Title"),
DEPARTMENT("Department"),
TIME_ZONE("Time Zone"),
ADDRESS("Address"),
HOME_ADDRESS("Home Address"),
ALT_EMAIL("Alternate Email", true), //multiple - primary comes from user obj
URL("URL", true), //multiple with primary
PHONE("Phone Number", true); //multiple with primary
// Used to get a field from its ID
private static Map<Long, ClearspaceField> idMap = new HashMap<Long, ClearspaceField>();
// Used to get a field from its name
private static Map<String, ClearspaceField> nameMap = new HashMap<String, ClearspaceField>();
static {
nameMap.put(TITLE.getName(), TITLE);
nameMap.put(DEPARTMENT.getName(), DEPARTMENT);
nameMap.put(TIME_ZONE.getName(), TIME_ZONE);
nameMap.put(ADDRESS.getName(), ADDRESS);
nameMap.put(HOME_ADDRESS.getName(), HOME_ADDRESS);
nameMap.put(ALT_EMAIL.getName(), ALT_EMAIL);
nameMap.put(URL.getName(), URL);
nameMap.put(PHONE.getName(), PHONE);
}
// The name is fixed and can be used as ID
private final String name;
// The id may change, so it is updated
private long id;
// True if the field supports multiple values.
private final boolean multipleValues;
/**
* Constructs a new field with a name
*
* @param name the name of the field
*/
ClearspaceField(String name) {
this(name, false);
}
/**
* Constructs a new field with a name and if it has multiple values
*
* @param name the name of the field
* @param multipleValues true if it has multiple values
*/
ClearspaceField(String name, boolean multipleValues) {
this.name = name;
this.multipleValues = multipleValues;
}
public String getName() {
return name;
}
public long getId() {
return id;
}
public boolean isMultipleValues() {
return multipleValues;
}
public void setId(long id) {
this.id = id;
idMap.put(id, this);
}
public static ClearspaceField valueOf(long id) {
return idMap.get(id);
}
public static ClearspaceField valueOfName(String name) {
return nameMap.get(name);
}
}
/**
* Represents the fields of the VCard, this is only a subset of the fields. It only includes
* the fields that have in common with Clearspace.
*/
enum VCardField {
TITLE, ORG_ORGUNIT, ADR_WORK, ADR_HOME, EMAIL_USERID, EMAIL_PREF_USERID, FN,
PHOTO_TYPE, PHOTO_BINVAL, URL, TZ, PHONE_HOME, PHONE_WORK, FAX_WORK, MOBILE_WORK, PAGER_WORK
}
private static ClearspaceVCardTranslator instance = new ClearspaceVCardTranslator();
/**
* Returns the instance of the translator
*
* @return the instance.
*/
protected static ClearspaceVCardTranslator getInstance() {
return instance;
}
/**
* Init the fields of clearspace based on they name.
*
* @param fieldsId
*/
protected void initClearspaceFieldsId(Element fieldsId) {
List<Element> fieldsList = fieldsId.elements("return");
for (Element field : fieldsList) {
String fieldName = field.elementText("name");
long fieldID = Long.valueOf(field.elementText("ID"));
ClearspaceField f = ClearspaceField.valueOfName(fieldName);
if (f != null) {
f.setId(fieldID);
}
}
}
protected Action[] translateVCard(Element vCardElement, Element profilesElement, Element userElement, Element avatarElement) {
Action[] actions = new Action[3];
// Gets the vCard values
Map<VCardField, String> vCardValues = collectVCardValues(vCardElement);
// Updates the profiles values with the vCard values
actions[0] = updateProfilesValues(profilesElement, vCardValues);
actions[1] = updateUserValues(userElement, vCardValues);
actions[2] = updateAvatarValues(avatarElement, vCardValues);
return actions;
}
private Action updateAvatarValues(Element avatarElement, Map<VCardField, String> vCardValues) {
Action action = Action.NO_ACTION;
// Gets the current avatar information
String currContentType = avatarElement.elementText("contentType");
String currdata = avatarElement.elementText("data");
// Gets the vCard photo information
String newContentType = vCardValues.get(VCardField.PHOTO_TYPE);
String newData = vCardValues.get(VCardField.PHOTO_BINVAL);
// Compares them
if (currContentType == null && newContentType != null) {
// new avatar
avatarElement.addElement("contentType").setText(newContentType);
avatarElement.addElement("data").setText(newData);
action = Action.CREATE;
} else if (currContentType != null && newContentType == null) {
// delete
action = Action.DELETE;
} else if (currdata != null && !currdata.equals(newData)) {
// modify
avatarElement.element("contentType").setText(newContentType);
avatarElement.element("data").setText(newData);
action = Action.MODIFY;
}
return action;
}
private Action updateUserValues(Element userElement, Map<VCardField, String> vCardValues) {
Action action = Action.NO_ACTION;
String fullName = vCardValues.get(VCardField.FN);
boolean emptyName = fullName != null && !"".equals(fullName.trim());
// if the new value is not empty then update. The name can't be deleted by an empty string
if (!emptyName) {
WSUtils.modifyElementText(userElement, "name", fullName);
action = Action.MODIFY;
}
String email = vCardValues.get(VCardField.EMAIL_PREF_USERID);
boolean emptyEmail = email != null && !"".equals(email.trim());
// if the new value is not empty then update. The email can't be deleted by an empty string
if (!emptyEmail) {
WSUtils.modifyElementText(userElement, "email", email);
action = Action.MODIFY;
}
return action;
}
/**
* Updates the values of the profiles with the values of the vCard
*
* @param profiles
* @param vCardValues
* @return
*/
private Action updateProfilesValues(Element profiles, Map<VCardField, String> vCardValues) {
Action action = Action.NO_ACTION;
List<Element> profilesList = profiles.elements("profiles");
// Modify or delete current profiles
for (Element profile : profilesList) {
int fieldID = Integer.valueOf(profile.elementText("fieldID"));
ClearspaceField field = ClearspaceField.valueOf(fieldID);
// If the field is unknown, then continue with the next one
if (field == null) {
continue;
}
// Gets the field value, it could have "value" of "values"
Element value = profile.element("value");
if (value == null) {
value = profile.element("values");
// It's OK if the value still null. It could need to be modified
}
// Modify or delete each field type. If newValue is null it will empty the field.
String newValue;
String oldValue;
switch (field) {
case TITLE:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.TITLE)) {
action = Action.MODIFY;
}
break;
case DEPARTMENT:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.ORG_ORGUNIT)) {
action = Action.MODIFY;
}
break;
case ADDRESS:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.ADR_WORK)) {
action = Action.MODIFY;
}
break;
case HOME_ADDRESS:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.ADR_HOME)) {
action = Action.MODIFY;
}
break;
case TIME_ZONE:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.TZ)) {
action = Action.MODIFY;
}
break;
case URL:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.URL)) {
action = Action.MODIFY;
}
break;
case ALT_EMAIL:
// Get the new value
newValue = vCardValues.get(VCardField.EMAIL_USERID);
// Get the old value
oldValue = value.getTextTrim();
// Get the mail type, i.e. HOME or WORK
String mailType = getFieldType(oldValue);
// Adds the mail type to the new value
newValue = addFieldType(newValue, mailType);
// Now the old and new values can be compared
if (!oldValue.equalsIgnoreCase(newValue)) {
value.setText(newValue == null ? "" : newValue);
action = Action.MODIFY;
}
// Removes the value from the map to mark that is was used
vCardValues.remove(VCardField.EMAIL_USERID);
break;
case PHONE:
// Get all the phones numbers
String newHomePhone = vCardValues.get(VCardField.PHONE_HOME);
String newWorkPhone = vCardValues.get(VCardField.PHONE_WORK);
String newWorkFax = vCardValues.get(VCardField.FAX_WORK);
String newWorkMobile = vCardValues.get(VCardField.MOBILE_WORK);
String newWorkPager = vCardValues.get(VCardField.PAGER_WORK);
newValue = null;
oldValue = value.getTextTrim();
String oldType = getFieldType(oldValue);
// Modifies the phone field that is of the same type
if ("work".equalsIgnoreCase(oldType)) {
newValue = addFieldType(newWorkPhone, oldType);
} else if ("home".equalsIgnoreCase(oldType)) {
newValue = addFieldType(newHomePhone, oldType);
} else if ("fax".equalsIgnoreCase(oldType)) {
newValue = addFieldType(newWorkFax, oldType);
} else if ("mobile".equalsIgnoreCase(oldType)) {
newValue = addFieldType(newWorkMobile, oldType);
} else if ("pager".equalsIgnoreCase(oldType)) {
newValue = addFieldType(newWorkPager, oldType);
} else if ("other".equalsIgnoreCase(oldType)) {
// No phone to update
// Removes the values from the map to mark that is was used
vCardValues.remove(VCardField.PHONE_HOME);
vCardValues.remove(VCardField.PHONE_WORK);
vCardValues.remove(VCardField.FAX_WORK);
vCardValues.remove(VCardField.MOBILE_WORK);
vCardValues.remove(VCardField.PAGER_WORK);
break;
}
// If newValue and oldValue are different the update the field
if (!oldValue.equals(newValue)) {
value.setText(newValue == null ? "" : newValue);
action = Action.MODIFY;
}
// Removes the values from the map to mark that is was used
vCardValues.remove(VCardField.PHONE_HOME);
vCardValues.remove(VCardField.PHONE_WORK);
vCardValues.remove(VCardField.FAX_WORK);
vCardValues.remove(VCardField.MOBILE_WORK);
vCardValues.remove(VCardField.PAGER_WORK);
break;
}
}
// Add new profiles that remains in the vCardValues, those are new profiles.
if (vCardValues.containsKey(VCardField.TITLE)) {
String newValue = vCardValues.get(VCardField.TITLE);
addProfile(profiles, ClearspaceField.TITLE, newValue);
}
if (vCardValues.containsKey(VCardField.ORG_ORGUNIT)) {
String newValue = vCardValues.get(VCardField.ORG_ORGUNIT);
addProfile(profiles, ClearspaceField.DEPARTMENT, newValue);
}
if (vCardValues.containsKey(VCardField.ADR_WORK)) {
String newValue = vCardValues.get(VCardField.ADR_WORK);
addProfile(profiles, ClearspaceField.ADDRESS, newValue);
}
if (vCardValues.containsKey(VCardField.ADR_HOME)) {
String newValue = vCardValues.get(VCardField.ADR_HOME);
addProfile(profiles, ClearspaceField.HOME_ADDRESS, newValue);
}
if (vCardValues.containsKey(VCardField.TZ)) {
String newValue = vCardValues.get(VCardField.TZ);
addProfile(profiles, ClearspaceField.TIME_ZONE, newValue);
}
if (vCardValues.containsKey(VCardField.URL)) {
String newValue = vCardValues.get(VCardField.URL);
addProfile(profiles, ClearspaceField.URL, newValue);
}
if (vCardValues.containsKey(VCardField.EMAIL_USERID)) {
String newValue = vCardValues.get(VCardField.EMAIL_USERID);
newValue = addFieldType(newValue, "work");
addProfile(profiles, ClearspaceField.ALT_EMAIL, newValue);
}
// Adds just one phone number, the first one. Clearspace doesn't support more than one.
if (vCardValues.containsKey(VCardField.PHONE_WORK)) {
String newValue = vCardValues.get(VCardField.PHONE_WORK);
newValue = addFieldType(newValue, "work");
addProfile(profiles, ClearspaceField.PHONE, newValue);
} else if (vCardValues.containsKey(VCardField.PHONE_HOME)) {
String newValue = vCardValues.get(VCardField.PHONE_HOME);
newValue = addFieldType(newValue, "home");
addProfile(profiles, ClearspaceField.PHONE, newValue);
} else if (vCardValues.containsKey(VCardField.FAX_WORK)) {
String newValue = vCardValues.get(VCardField.FAX_WORK);
newValue = addFieldType(newValue, "fax");
addProfile(profiles, ClearspaceField.PHONE, newValue);
} else if (vCardValues.containsKey(VCardField.MOBILE_WORK)) {
String newValue = vCardValues.get(VCardField.MOBILE_WORK);
newValue = addFieldType(newValue, "mobile");
addProfile(profiles, ClearspaceField.PHONE, newValue);
} else if (vCardValues.containsKey(VCardField.PAGER_WORK)) {
String newValue = vCardValues.get(VCardField.PAGER_WORK);
newValue = addFieldType(newValue, "pager");
addProfile(profiles, ClearspaceField.PHONE, newValue);
}
return action;
}
private void addProfile(Element profiles, ClearspaceField field, String newValue) {
// Don't add empty vales
if (newValue == null || "".equals(newValue.trim())) {
return;
}
Element profile = profiles.addElement("profiles");
profile.addElement("fieldID").setText(String.valueOf(field.getId()));
if (field.isMultipleValues()) {
profile.addElement("values").setText(newValue);
} else {
profile.addElement("value").setText(newValue);
}
}
private boolean modifyProfileValue(Map<VCardField, String> vCardValues, Element profiles, Element value, VCardField vCardField) {
boolean modified = false;
String newValue = vCardValues.get(vCardField);
// Modifies or deletes the value
if (!value.getTextTrim().equals(newValue)) {
value.setText(newValue == null ? "" : newValue);
modified = true;
}
// Remove the vCard value to mark that it was used
vCardValues.remove(vCardField);
return modified;
}
private String addFieldType(String value, String type) {
if (value == null || "".equals(value.trim())) {
return null;
}
return value + "|" + type;
}
private String getFieldType(String field) {
int i = field.indexOf("|");
if (i == -1) {
return null;
} else {
return field.substring(i + 1);
}
}
private String getFieldValue(String field) {
int i = field.indexOf("|");
if (i == -1) {
return field;
} else {
return field.substring(0, i);
}
}
/**
* Collects the vCard values and store them into a map.
* They are stored with this constants:
*
* @param vCardElement
* @return
*/
private Map<VCardField, String> collectVCardValues(Element vCardElement) {
Map<VCardField, String> vCardValues = new HashMap<VCardField, String>();
// Add the Title
vCardValues.put(VCardField.TITLE, vCardElement.elementText("TITLE"));
// Add the Department
Element orgElement = vCardElement.element("ORG");
if (orgElement != null) {
vCardValues.put(VCardField.ORG_ORGUNIT, orgElement.elementText("ORGUNIT"));
}
// Add the home and work address
List<Element> addressElements = (List<Element>) vCardElement.elements("ADR");
if (addressElements != null) {
for (Element address : addressElements) {
if (address.element("WORK") != null) {
vCardValues.put(VCardField.ADR_WORK, translateAddress(address));
} else if (address.element("HOME") != null) {
vCardValues.put(VCardField.ADR_HOME, translateAddress(address));
}
}
}
// Add the URL
vCardValues.put(VCardField.URL, vCardElement.elementText("URL"));
// Add the preferred and alternative email address
List<Element> emailsElement = (List<Element>) vCardElement.elements("EMAIL");
if (emailsElement != null) {
for (Element emailElement : emailsElement) {
if (emailElement.element("PREF") == null) {
vCardValues.put(VCardField.EMAIL_USERID, emailElement.elementText("USERID"));
} else {
vCardValues.put(VCardField.EMAIL_PREF_USERID, emailElement.elementText("USERID"));
}
}
}
// Add the full name
vCardValues.put(VCardField.FN, vCardElement.elementText("FN"));
// Add the time zone
vCardValues.put(VCardField.TZ, vCardElement.elementText("TZ"));
// Add the photo
Element photoElement = vCardElement.element("PHOTO");
if (photoElement != null) {
vCardValues.put(VCardField.PHOTO_TYPE, photoElement.elementText("TYPE"));
vCardValues.put(VCardField.PHOTO_BINVAL, photoElement.elementText("BINVAL"));
}
// Add the home and work tel
List<Element> telElements = (List<Element>) vCardElement.elements("TEL");
if (telElements != null) {
for (Element tel : telElements) {
String number = tel.elementText("NUMBER");
if (tel.element("WORK") != null) {
if (tel.element("VOICE") != null) {
vCardValues.put(VCardField.PHONE_WORK, number);
} else if (tel.element("FAX") != null) {
vCardValues.put(VCardField.FAX_WORK, number);
} else if (tel.element("CELL") != null) {
vCardValues.put(VCardField.MOBILE_WORK, number);
} else if (tel.element("PAGER") != null) {
vCardValues.put(VCardField.PAGER_WORK, number);
}
} else if (tel.element("HOME") != null && tel.element("VOICE") != null) {
vCardValues.put(VCardField.PHONE_HOME, number);
}
}
}
return vCardValues;
}
/**
* Translates the information from Clearspace into a VCard.
*
* @param profile
* @param user
* @param avatar
* @return
*/
protected Element translateClearspaceInfo(Element profile, User user, Element avatar) {
Document vCardDoc = DocumentHelper.createDocument();
Element vCard = vCardDoc.addElement("vCard", "vcard-temp");
translateUserInformation(user, vCard);
translateProfileInformation(profile, vCard);
translateAvatarInformation(avatar, vCard);
return vCard;
}
private void translateProfileInformation(Element profiles, Element vCard) {
// Translate the profile XML
/* Profile response sample
<ns1:getProfileResponse xmlns:ns1="http://jivesoftware.com/clearspace/webservices">
<return>
<fieldID>2</fieldID>
<value>RTC</value>
</return>
<return>
<fieldID>9</fieldID>
<value>-300</value>
</return>
<return>
<fieldID>11</fieldID>
<value>street1:San Martin,street2:1650,city:Cap Fed,state:Buenos Aires,country:Argentina,zip:1602,type:HOME</value>
</return>
<return>
<fieldID>1</fieldID>
<value>Mr.</value>
</return>
<return>
<fieldID>3</fieldID>
<value>street1:Alder 2345,city:Portland,state:Oregon,country:USA,zip:32423,type:WORK</value>
</return>
<return>
<fieldID>10</fieldID>
<values>gguardin@gmail.com|work</values>
</return>
<return>
<fieldID>5</fieldID>
<values>http://www.gguardin.com.ar</values>
</return>
</ns1:getProfileResponse>
*/
List<Element> profilesList = (List<Element>) profiles.elements("return");
for (Element profileElement : profilesList) {
long fieldID = Long.valueOf(profileElement.elementText("fieldID"));
ClearspaceField field = ClearspaceField.valueOf(fieldID);
// If the field is not known, skip it
if (field == null) {
continue;
}
// The value name of the value field could be value or values
String fieldText = profileElement.elementText("value");
if (fieldText == null) {
fieldText = profileElement.elementText("values");
// if it is an empty field, continue with the next field
if (fieldText == null) {
continue;
}
}
String fieldType = getFieldType(fieldText);
String fieldValue = getFieldValue(fieldText);
switch (field) {
case TITLE:
vCard.addElement("TITLE").setText(fieldValue);
break;
case DEPARTMENT:
vCard.addElement("ORG").addElement("ORGUNIT").setText(fieldValue);
break;
case TIME_ZONE:
vCard.addElement("TZ").setText(fieldValue);
break;
case ADDRESS:
Element workAdr = vCard.addElement("ADR");
workAdr.addElement("WORK");
translateAddress(fieldValue, workAdr);
break;
case HOME_ADDRESS:
Element homeAdr = vCard.addElement("ADR");
homeAdr.addElement("HOME");
translateAddress(fieldValue, homeAdr);
break;
case URL:
vCard.addElement("URL").setText(fieldValue);
break;
case ALT_EMAIL:
fieldValue = getFieldValue(fieldValue);
Element email = vCard.addElement("EMAIL");
email.addElement("USERID").setText(fieldValue);
email.addElement("INTERNET").setText(fieldValue);
if ("work".equalsIgnoreCase(fieldType)) {
email.addElement("WORK");
} else if ("home".equalsIgnoreCase(fieldType)) {
email.addElement("HOME");
}
break;
case PHONE:
Element tel = vCard.addElement("TEL");
tel.addElement("NUMBER").setText(fieldValue);
if ("home".equalsIgnoreCase(fieldType)) {
tel.addElement("HOME");
tel.addElement("VOICE");
} else if ("work".equalsIgnoreCase(fieldType)) {
tel.addElement("WORK");
tel.addElement("VOICE");
} else if ("fax".equalsIgnoreCase(fieldType)) {
tel.addElement("WORK");
tel.addElement("FAX");
} else if ("mobile".equalsIgnoreCase(fieldType)) {
tel.addElement("WORK");
tel.addElement("CELL");
} else if ("pager".equalsIgnoreCase(fieldType)) {
tel.addElement("WORK");
tel.addElement("PAGER");
} else if ("other".equalsIgnoreCase(fieldType)) {
// don't send
}
break;
}
}
}
private void translateUserInformation(User user, Element vCard) {
// The name could be null (if in Clearspace the name is not visible in Openfire it is null)
if (user.getName() != null && !"".equals(user.getName().trim())) {
vCard.addElement("FN").setText(user.getName());
vCard.addElement("N").addElement("FAMILY").setText(user.getName());
}
// Email is mandatory, but may be invisible
if (user.getEmail() != null && !"".equals(user.getName().trim())) {
Element email = vCard.addElement("EMAIL");
email.addElement("PREF");
email.addElement("USERID").setText(user.getEmail());
}
String jid = XMPPServer.getInstance().createJID(user.getUsername(), null).toBareJID();
vCard.addElement("JABBERID").setText(jid);
}
private void translateAvatarInformation(Element avatarResponse, Element vCard) {
Element avatar = avatarResponse.element("return");
if (avatar != null) {
Element attachment = avatar.element("attachment");
if (attachment != null) {
String contentType = attachment.elementText("contentType");
String data = attachment.elementText("data");
// Add the avatar to the vCard
Element photo = vCard.addElement("PHOTO");
photo.addElement("TYPE").setText(contentType);
photo.addElement("BINVAL").setText(data);
}
}
}
private void translateAddress(String address, Element addressE) {
StringTokenizer strTokenize = new StringTokenizer(address, ",");
while (strTokenize.hasMoreTokens()) {
String token = strTokenize.nextToken();
int index = token.indexOf(":");
String field = token.substring(0, index);
String value = token.substring(index + 1);
if ("street1".equals(field)) {
addressE.addElement("STREET").setText(value);
} else if ("street2".equals(field)) {
addressE.addElement("EXTADD").setText(value);
} else if ("city".equals(field)) {
addressE.addElement("LOCALITY").setText(value);
} else if ("state".equals(field)) {
addressE.addElement("REGION").setText(value);
} else if ("country".equals(field)) {
addressE.addElement("CTRY").setText(value);
} else if ("zip".equals(field)) {
addressE.addElement("PCODE").setText(value);
} else if ("type".equals(field)) {
if ("HOME".equals(value)) {
addressE.addElement("HOME");
} else if ("WORK".equals(value)) {
addressE.addElement("WORK");
}
}
}
}
private String translateAddress(Element addressElement) {
StringBuilder sb = new StringBuilder();
translateAddressField(addressElement, "STREET", "street1", sb);
translateAddressField(addressElement, "EXTADD", "street2", sb);
translateAddressField(addressElement, "LOCALITY", "city", sb);
translateAddressField(addressElement, "REGION", "state", sb);
translateAddressField(addressElement, "CTRY", "country", sb);
translateAddressField(addressElement, "PCODE", "zip", sb);
// if there is no address return an empty string
if (sb.length() == 0) {
return "";
}
// if there is an address add the home or work type
if (addressElement.element("HOME") != null) {
sb.append("type:HOME");
} else if (addressElement.element("WORK") != null) {
sb.append("type:WORK");
}
return sb.toString();
}
private void translateAddressField(Element addressElement, String elementName, String fieldName, StringBuilder sb) {
String field = addressElement.elementTextTrim(elementName);
if (field != null && !"".equals(field)) {
sb.append(fieldName).append(":").append(field).append(",");
}
}
}
......@@ -31,6 +31,7 @@ public class WSUtils {
/**
* Returns the text of the first an element with name 'return'.
*
* @param element the element to search for a return element.
* @return the text of the return element.
*/
......@@ -40,6 +41,7 @@ public class WSUtils {
/**
* Returns the text of the first an element with name 'name'.
*
* @param node the element to search for a "name" element.
* @param name the name of the element to search
* @return the text of the corresponding element
......@@ -54,8 +56,9 @@ public class WSUtils {
/**
* Modifies the text of the elmement with name 'name'.
* @param node the element to search
* @param name the name to search
*
* @param node the element to search
* @param name the name to search
* @param newValue the new value of the text
*/
protected static void modifyElementText(Node node, String name, String newValue) {
......@@ -63,14 +66,24 @@ public class WSUtils {
n.setText(newValue);
}
protected static void modifyElementText(Element element, String[] path, String newValue) {
Element e = element;
for (String s : path) {
e = e.element(s);
}
e.setText(newValue);
}
/**
* Parse REST responses of the type String[], that are XML of the form:
*
* <p/>
* <something>
* <return>text1</return>
* <return>text2</return>
* <return>text3</return>
* <return>text1</return>
* <return>text2</return>
* <return>text3</return>
* </something>
*
* @param element
* @return
*/
......@@ -90,12 +103,13 @@ public class WSUtils {
result += s + ",";
}
return result.substring(0, result.length() -1);
return result.substring(0, result.length() - 1);
}
/**
* Parses a date of the form 1969-12-31T21:00:00-03:00, or 2008-02-13T18:54:29.147-03:00.
* If the string is null or there is a problem parsing the date, returns null.
*
* @param date the string to parse
* @return the corresponding date, or null if t
*/
......@@ -106,13 +120,12 @@ public class WSUtils {
// REST writes dates time zone with ':', somthing like -3:00
// to parse it they should be removed
int index = date.lastIndexOf(":");
date = date.substring(0, index) + date.substring(index +1);
date = date.substring(0, index) + date.substring(index + 1);
Date d = null;
try {
if (date.length() == 24) {
d = dateFormatNoMil.parse(date);
}
else {
} else {
d = dateFormatMil.parse(date);
}
} catch (ParseException e) {
......@@ -123,6 +136,7 @@ public class WSUtils {
/**
* Formats a date into yyyy-MM-dd'T'HH:mm:ss.SSSZ, for example 2008-02-13T18:54:29.147-03:00
*
* @param date the date to format
* @return a string representation of the date
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment