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

[JM-1281] Fixed an exception using a user with a not visible email and CS as a provider.

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/branches@10018 b35dd754-fafc-0310-a699-88a17e54d16e
parent 6d34b403
...@@ -23,22 +23,36 @@ import org.xmpp.packet.JID; ...@@ -23,22 +23,36 @@ import org.xmpp.packet.JID;
import java.util.*; import java.util.*;
/** /**
* @author Daniel Henninger * The ClearspaceUserProvider uses the UserService and ProfileSearchService web service inside of Clearspace
* to retrieve user information and to search for users from Clearspace.
*
* @author Gabriel Guardincerri
*/ */
public class ClearspaceUserProvider implements UserProvider { public class ClearspaceUserProvider implements UserProvider {
// The UserService webservice url prefix
protected static final String USER_URL_PREFIX = "userService/"; protected static final String USER_URL_PREFIX = "userService/";
// The ProfileSearchService webservice url prefix
protected static final String SEARCH_URL_PREFIX = "profileSearchService/"; protected static final String SEARCH_URL_PREFIX = "profileSearchService/";
private ClearspaceManager manager; private ClearspaceManager manager;
// Used to know it CS is a read only user provider
private Boolean readOnly; private Boolean readOnly;
public ClearspaceUserProvider() { public ClearspaceUserProvider() {
// Gets the manager // Gets the manager
manager = ClearspaceManager.getInstance(); manager = ClearspaceManager.getInstance();
loadReadOnly();
} }
/**
* Loads the user using the userService/users GET service. Only loads local users.
* Throws a UserNotFoundException exception if the user could not be found.
*
* @param username the username of the user to load
* @return a user instance with the user information
* @throws UserNotFoundException if the user could not be found
*/
public User loadUser(String username) throws UserNotFoundException { public User loadUser(String username) throws UserNotFoundException {
// Checks if the user is local // Checks if the user is local
if (username.contains("@")) { if (username.contains("@")) {
...@@ -52,6 +66,19 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -52,6 +66,19 @@ public class ClearspaceUserProvider implements UserProvider {
return translate(getUserByUsername(username)); return translate(getUserByUsername(username));
} }
/**
* Creates user using the userService/users POST service. If Clearspace is a read only
* provider throws an UnsupportedOperationException. If there is already a user with
* the username throws a UserAlreadyExistsException.
*
* @param username the username of the user
* @param password the password of the user
* @param name the name of the user (optional)
* @param email the email of the user
* @return an instance of the created user
* @throws UserAlreadyExistsException If there is already a user with the username
* @throws UnsupportedOperationException If Clearspace is a read only provider
*/
public User createUser(String username, String password, String name, String email) throws UserAlreadyExistsException { public User createUser(String username, String password, String name, String email) throws UserAlreadyExistsException {
if (isReadOnly()) { if (isReadOnly()) {
// Reject the operation since the provider is read-only // Reject the operation since the provider is read-only
...@@ -100,6 +127,11 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -100,6 +127,11 @@ public class ClearspaceUserProvider implements UserProvider {
} }
} }
/**
* Creates user using the userService/users DELETE service. If the user is not found returns.
*
* @param username the username of the user to delete
*/
public void deleteUser(String username) { public void deleteUser(String username) {
if (isReadOnly()) { if (isReadOnly()) {
// Reject the operation since the provider is read-only // Reject the operation since the provider is read-only
...@@ -119,6 +151,11 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -119,6 +151,11 @@ public class ClearspaceUserProvider implements UserProvider {
} }
} }
/**
* Gets the user count using the userService/users/count GET service.
*
* @return the user count
*/
public int getUserCount() { public int getUserCount() {
try { try {
String path = USER_URL_PREFIX + "users/count"; String path = USER_URL_PREFIX + "users/count";
...@@ -131,11 +168,21 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -131,11 +168,21 @@ public class ClearspaceUserProvider implements UserProvider {
} }
} }
/**
* Gets all users using the userService/userNames GET service.
*
* @return a list of all users
*/
public Collection<User> getUsers() { public Collection<User> getUsers() {
Collection<String> usernames = getUsernames(); Collection<String> usernames = getUsernames();
return new UserCollection(usernames.toArray(new String[usernames.size()])); return new UserCollection(usernames.toArray(new String[usernames.size()]));
} }
/**
* Gets all usernames using the userService/userNames GET service.
*
* @return a list of all the usernames
*/
public Collection<String> getUsernames() { public Collection<String> getUsernames() {
try { try {
String path = USER_URL_PREFIX + "userNames"; String path = USER_URL_PREFIX + "userNames";
...@@ -148,6 +195,13 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -148,6 +195,13 @@ public class ClearspaceUserProvider implements UserProvider {
} }
} }
/**
* Gets a bounded list of users using the userService/userNames GET service.
*
* @param startIndex the start index
* @param numResults the number of result
* @return a bounded list of users
*/
public Collection<User> getUsers(int startIndex, int numResults) { public Collection<User> getUsers(int startIndex, int numResults) {
String[] usernamesAll = getUsernames().toArray(new String[0]); String[] usernamesAll = getUsernames().toArray(new String[0]);
Collection<String> usernames = new ArrayList<String>(); Collection<String> usernames = new ArrayList<String>();
...@@ -160,6 +214,13 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -160,6 +214,13 @@ public class ClearspaceUserProvider implements UserProvider {
return new UserCollection(usernames.toArray(new String[usernames.size()])); return new UserCollection(usernames.toArray(new String[usernames.size()]));
} }
/**
* Updates the name of the user using the userService/update service.
*
* @param username the username of the user
* @param name the new name of the user
* @throws UserNotFoundException if there is no user with that username
*/
public void setName(String username, String name) throws UserNotFoundException { public void setName(String username, String name) throws UserNotFoundException {
if (isReadOnly()) { if (isReadOnly()) {
// Reject the operation since the provider is read-only // Reject the operation since the provider is read-only
...@@ -177,6 +238,13 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -177,6 +238,13 @@ public class ClearspaceUserProvider implements UserProvider {
updateUser(userUpdateParams); updateUser(userUpdateParams);
} }
/**
* Updates the email of the user using the userService/update service.
*
* @param username the username of the user
* @param email the new email of the user
* @throws UserNotFoundException if the user could not be found
*/
public void setEmail(String username, String email) throws UserNotFoundException { public void setEmail(String username, String email) throws UserNotFoundException {
if (isReadOnly()) { if (isReadOnly()) {
// Reject the operation since the provider is read-only // Reject the operation since the provider is read-only
...@@ -194,6 +262,14 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -194,6 +262,14 @@ public class ClearspaceUserProvider implements UserProvider {
updateUser(userUpdateParams); updateUser(userUpdateParams);
} }
/**
* Updates the creationDate of the user using the userService/update service.
*
* @param username the username of the user
* @param creationDate the new email of the user
* @throws UserNotFoundException if the user could not be found
*/
public void setCreationDate(String username, Date creationDate) throws UserNotFoundException { public void setCreationDate(String username, Date creationDate) throws UserNotFoundException {
if (isReadOnly()) { if (isReadOnly()) {
// Reject the operation since the provider is read-only // Reject the operation since the provider is read-only
...@@ -212,6 +288,13 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -212,6 +288,13 @@ public class ClearspaceUserProvider implements UserProvider {
updateUser(userUpdateParams); updateUser(userUpdateParams);
} }
/**
* Updates the modificationDate of the user using the userService/update service.
*
* @param username the username of the user
* @param modificationDate the new modificationDate of the user
* @throws UserNotFoundException if the user could not be found
*/
public void setModificationDate(String username, Date modificationDate) throws UserNotFoundException { public void setModificationDate(String username, Date modificationDate) throws UserNotFoundException {
if (isReadOnly()) { if (isReadOnly()) {
// Reject the operation since the provider is read-only // Reject the operation since the provider is read-only
...@@ -230,6 +313,13 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -230,6 +313,13 @@ public class ClearspaceUserProvider implements UserProvider {
updateUser(userUpdateParams); updateUser(userUpdateParams);
} }
/**
* Creates the parameters to send in a update user request based on the information of <code>username</code>
*
* @param username the username of the user
* @return the parameters to send in a update user request
* @throws UserNotFoundException if the user could not be found
*/
protected Element getUserUpdateParams(String username) throws UserNotFoundException { protected Element getUserUpdateParams(String username) throws UserNotFoundException {
// Creates the user update params element // Creates the user update params element
Element userUpdateParams = DocumentHelper.createDocument().addElement("updateUser"); Element userUpdateParams = DocumentHelper.createDocument().addElement("updateUser");
...@@ -246,6 +336,12 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -246,6 +336,12 @@ public class ClearspaceUserProvider implements UserProvider {
return userUpdateParams; return userUpdateParams;
} }
/**
* Updates the user using the userService/users PUT service.
*
* @param userUpdateParams the request parameters
* @throws UserNotFoundException if the user could not be found
*/
protected void updateUser(Element userUpdateParams) throws UserNotFoundException { protected void updateUser(Element userUpdateParams) throws UserNotFoundException {
try { try {
String path = USER_URL_PREFIX + "users"; String path = USER_URL_PREFIX + "users";
...@@ -259,32 +355,44 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -259,32 +355,44 @@ public class ClearspaceUserProvider implements UserProvider {
} }
} }
/**
* Clearsapce can search using three fields: username, name and email.
*
* @return a list of username, name and email
* @throws UnsupportedOperationException
*/
public Set<String> getSearchFields() throws UnsupportedOperationException { public Set<String> getSearchFields() throws UnsupportedOperationException {
return new LinkedHashSet<String>(Arrays.asList("Username", "Name", "Email")); return new LinkedHashSet<String>(Arrays.asList("Username", "Name", "Email"));
} }
/**
* Search for the user using the userService/search POST method.
*
* @param fields the fields to search on.
* @param query the query string.
* @return a Collection of users that match the search.
* @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation).
*/
public Collection<User> findUsers(Set<String> fields, String query) throws UnsupportedOperationException { public Collection<User> findUsers(Set<String> fields, String query) throws UnsupportedOperationException {
// Creates the XML with the data // Creates the XML with the data
Document groupDoc = DocumentHelper.createDocument(); Element paramsE = DocumentHelper.createDocument().addElement("search");
Element rootE = groupDoc.addElement("searchProfile");
Element queryE = rootE.addElement("WSProfileSearchQuery"); Element queryE = paramsE.addElement("query");
Element keywords = queryE.addElement("keywords");
keywords.addText(query); queryE.addElement("keywords").addText(query);
Element searchUsername = queryE.addElement("searchUsername");
searchUsername.addText("true"); queryE.addElement("searchUsername").addText("true");
Element searchName = queryE.addElement("searchName"); queryE.addElement("searchName").addText("true");
searchName.addText("true"); queryE.addElement("searchEmail").addText("true");
Element searchEmail = queryE.addElement("searchEmail"); queryE.addElement("searchProfile").addText("false");
searchEmail.addText("true");
Element searchProfile = queryE.addElement("searchProfile");
searchProfile.addText("false");
try { try {
List<String> usernames = new ArrayList<String>(); List<String> usernames = new ArrayList<String>();
String path = SEARCH_URL_PREFIX + "searchProfile";
//TODO create a service on CS to get only the username field //TODO create a service on CS to get only the username field
Element element = manager.executeRequest(GET, path); String path = SEARCH_URL_PREFIX + "searchProfile";
Element element = manager.executeRequest(POST, path, paramsE.asXML());
List<Node> userNodes = (List<Node>) element.selectNodes("return"); List<Node> userNodes = (List<Node>) element.selectNodes("return");
for (Node userNode : userNodes) { for (Node userNode : userNodes) {
...@@ -299,28 +407,40 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -299,28 +407,40 @@ public class ClearspaceUserProvider implements UserProvider {
} }
} }
/**
* Search for the user using the userService/searchBounded POST method.
*
* @param fields the fields to search on.
* @param query the query string.
* @param startIndex the starting index in the search result to return.
* @param numResults the number of users to return in the search result.
* @return a Collection of users that match the search.
* @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation).
*/
public Collection<User> findUsers(Set<String> fields, String query, int startIndex, int numResults) throws UnsupportedOperationException { public Collection<User> findUsers(Set<String> fields, String query, int startIndex, int numResults) throws UnsupportedOperationException {
// Creates the XML with the data // Creates the XML with the data
Document groupDoc = DocumentHelper.createDocument(); Element paramsE = DocumentHelper.createDocument().addElement("searchBounded");
Element rootE = groupDoc.addElement("searchBounded");
Element queryE = rootE.addElement("WSProfileSearchQuery"); Element queryE = paramsE.addElement("query");
Element keywords = queryE.addElement("keywords");
keywords.addText(query); queryE.addElement("keywords").addText(query);
Element searchUsername = queryE.addElement("searchUsername");
searchUsername.addText("true"); queryE.addElement("searchUsername").addText("true");
Element searchName = queryE.addElement("searchName"); queryE.addElement("searchName").addText("true");
searchName.addText("true"); queryE.addElement("searchEmail").addText("true");
Element searchEmail = queryE.addElement("searchEmail"); queryE.addElement("searchProfile").addText("false");
searchEmail.addText("true");
Element searchProfile = queryE.addElement("searchProfile"); paramsE.addElement("startIndex").addText(String.valueOf(startIndex));
searchProfile.addText("false"); paramsE.addElement("numResults").addText(String.valueOf(numResults));
try { try {
List<String> usernames = new ArrayList<String>(); List<String> usernames = new ArrayList<String>();
String path = SEARCH_URL_PREFIX + "searchProfile/" + startIndex + "/" + numResults;
Element element = manager.executeRequest(GET, path);
//TODO create a service on CS to get only the username field //TODO create a service on CS to get only the username field
String path = SEARCH_URL_PREFIX + "searchProfile";
Element element = manager.executeRequest(POST, path, paramsE.asXML());
List<Node> userNodes = (List<Node>) element.selectNodes("return"); List<Node> userNodes = (List<Node>) element.selectNodes("return");
for (Node userNode : userNodes) { for (Node userNode : userNodes) {
String username = userNode.selectSingleNode("username").getText(); String username = userNode.selectSingleNode("username").getText();
...@@ -334,22 +454,44 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -334,22 +454,44 @@ public class ClearspaceUserProvider implements UserProvider {
} }
} }
/**
* Returns true if Clearspace is a read only user provider.
*
* @return true if Clearspace is a read only user provider
*/
public boolean isReadOnly() { public boolean isReadOnly() {
if (readOnly == null) { if (readOnly == null) {
loadReadOnly(); synchronized (this) {
if (readOnly == null) {
loadReadOnly();
}
}
} }
// If it is null returns the most restrictive answer. // If it is null returns the most restrictive answer.
return (readOnly == null ? false : readOnly); return (readOnly == null ? false : readOnly);
} }
/**
* In Clearspace name is optional.
*
* @return false
*/
public boolean isNameRequired() { public boolean isNameRequired() {
return false; return false;
} }
/**
* In Clearspace email is required
*
* @return true
*/
public boolean isEmailRequired() { public boolean isEmailRequired() {
return true; return true;
} }
/**
* Tries to load the read only attribute using the userService/isReadOnly service.
*/
private void loadReadOnly() { private void loadReadOnly() {
try { try {
// See if the is read only // See if the is read only
...@@ -361,6 +503,12 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -361,6 +503,12 @@ public class ClearspaceUserProvider implements UserProvider {
} }
} }
/**
* Translates a Clearspace xml user response into a Openfire User
*
* @param responseNode the Clearspace response
* @return a User instance with its information
*/
private User translate(Node responseNode) { private User translate(Node responseNode) {
String username = null; String username = null;
String name = null; String name = null;
...@@ -376,20 +524,20 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -376,20 +524,20 @@ public class ClearspaceUserProvider implements UserProvider {
// Gets the name if it is visible // Gets the name if it is visible
boolean nameVisible = Boolean.valueOf(userNode.selectSingleNode("nameVisible").getText()); boolean nameVisible = Boolean.valueOf(userNode.selectSingleNode("nameVisible").getText());
if (nameVisible) {
tmpNode = userNode.selectSingleNode("name"); // Gets the name
if (tmpNode != null) { tmpNode = userNode.selectSingleNode("name");
name = tmpNode.getText(); if (tmpNode != null) {
} name = tmpNode.getText();
} }
// Gets the email if it is visible // Gets the email if it is visible
boolean emailVisible = Boolean.valueOf(userNode.selectSingleNode("emailVisible").getText()); boolean emailVisible = Boolean.valueOf(userNode.selectSingleNode("emailVisible").getText());
if (emailVisible) {
tmpNode = userNode.selectSingleNode("email"); // Gets the email
if (tmpNode != null) { tmpNode = userNode.selectSingleNode("email");
email = tmpNode.getText(); if (tmpNode != null) {
} email = tmpNode.getText();
} }
// Gets the creation date // Gets the creation date
...@@ -405,9 +553,19 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -405,9 +553,19 @@ public class ClearspaceUserProvider implements UserProvider {
} }
// Creates the user // Creates the user
return new User(username, name, email, creationDate, modificationDate); User user = new User(username, name, email, creationDate, modificationDate);
user.setNameVisible(nameVisible);
user.setEmailVisible(emailVisible);
return user;
} }
/**
* Gets a user using the userService/users GET service.
*
* @param username the username of the user
* @return the user xml response
* @throws UserNotFoundException if the user could not be found
*/
private Element getUserByUsername(String username) throws UserNotFoundException { private Element getUserByUsername(String username) throws UserNotFoundException {
try { try {
......
...@@ -139,10 +139,10 @@ class ClearspaceVCardTranslator { ...@@ -139,10 +139,10 @@ class ClearspaceVCardTranslator {
/** /**
* Init the fields of clearspace based on they name. * Init the fields of clearspace based on they name.
* *
* @param fieldsId * @param fields the fields information
*/ */
protected void initClearspaceFieldsId(Element fieldsId) { protected void initClearspaceFieldsId(Element fields) {
List<Element> fieldsList = fieldsId.elements("return"); List<Element> fieldsList = fields.elements("return");
for (Element field : fieldsList) { for (Element field : fieldsList) {
String fieldName = field.elementText("name"); String fieldName = field.elementText("name");
long fieldID = Long.valueOf(field.elementText("ID")); long fieldID = Long.valueOf(field.elementText("ID"));
...@@ -155,6 +155,16 @@ class ClearspaceVCardTranslator { ...@@ -155,6 +155,16 @@ class ClearspaceVCardTranslator {
} }
/**
* Translates a VCard of Openfire into profiles, user information and a avatar of Clearspace.
* Returns what can of action has been made over the the profilesElement, userElement and avatarElement/
*
* @param vCardElement the VCard information
* @param profilesElement the profile to add/modify/delete the information
* @param userElement the user to add/modify/delete the information
* @param avatarElement the avatar to add/modify/delete the information
* @return a list of actions performed over the profilesElement, userElement and avatarElement
*/
protected Action[] translateVCard(Element vCardElement, Element profilesElement, Element userElement, Element avatarElement) { protected Action[] translateVCard(Element vCardElement, Element profilesElement, Element userElement, Element avatarElement) {
Action[] actions = new Action[3]; Action[] actions = new Action[3];
...@@ -171,6 +181,13 @@ class ClearspaceVCardTranslator { ...@@ -171,6 +181,13 @@ class ClearspaceVCardTranslator {
return actions; return actions;
} }
/**
* Updates the avatar values based on the vCard values
*
* @param avatarElement the avatar element to update
* @param vCardValues the vCard values with the information
* @return the action performed
*/
private Action updateAvatarValues(Element avatarElement, Map<VCardField, String> vCardValues) { private Action updateAvatarValues(Element avatarElement, Map<VCardField, String> vCardValues) {
Action action = Action.NO_ACTION; Action action = Action.NO_ACTION;
...@@ -201,6 +218,13 @@ class ClearspaceVCardTranslator { ...@@ -201,6 +218,13 @@ class ClearspaceVCardTranslator {
return action; return action;
} }
/**
* Updates the user values based on the vCard values
*
* @param userElement the user element to update
* @param vCardValues the vCard values
* @return the action performed
*/
private Action updateUserValues(Element userElement, Map<VCardField, String> vCardValues) { private Action updateUserValues(Element userElement, Map<VCardField, String> vCardValues) {
Action action = Action.NO_ACTION; Action action = Action.NO_ACTION;
...@@ -228,9 +252,9 @@ class ClearspaceVCardTranslator { ...@@ -228,9 +252,9 @@ class ClearspaceVCardTranslator {
/** /**
* Updates the values of the profiles with the values of the vCard * Updates the values of the profiles with the values of the vCard
* *
* @param profiles * @param profiles the profiles element to update
* @param vCardValues * @param vCardValues the vCard values
* @return * @return the action performed
*/ */
private Action updateProfilesValues(Element profiles, Map<VCardField, String> vCardValues) { private Action updateProfilesValues(Element profiles, Map<VCardField, String> vCardValues) {
Action action = Action.NO_ACTION; Action action = Action.NO_ACTION;
...@@ -259,32 +283,32 @@ class ClearspaceVCardTranslator { ...@@ -259,32 +283,32 @@ class ClearspaceVCardTranslator {
String oldValue; String oldValue;
switch (field) { switch (field) {
case TITLE: case TITLE:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.TITLE)) { if (modifyProfileValue(vCardValues, value, VCardField.TITLE)) {
action = Action.MODIFY; action = Action.MODIFY;
} }
break; break;
case DEPARTMENT: case DEPARTMENT:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.ORG_ORGUNIT)) { if (modifyProfileValue(vCardValues, value, VCardField.ORG_ORGUNIT)) {
action = Action.MODIFY; action = Action.MODIFY;
} }
break; break;
case ADDRESS: case ADDRESS:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.ADR_WORK)) { if (modifyProfileValue(vCardValues, value, VCardField.ADR_WORK)) {
action = Action.MODIFY; action = Action.MODIFY;
} }
break; break;
case HOME_ADDRESS: case HOME_ADDRESS:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.ADR_HOME)) { if (modifyProfileValue(vCardValues, value, VCardField.ADR_HOME)) {
action = Action.MODIFY; action = Action.MODIFY;
} }
break; break;
case TIME_ZONE: case TIME_ZONE:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.TZ)) { if (modifyProfileValue(vCardValues, value, VCardField.TZ)) {
action = Action.MODIFY; action = Action.MODIFY;
} }
break; break;
case URL: case URL:
if (modifyProfileValue(vCardValues, profiles, value, VCardField.URL)) { if (modifyProfileValue(vCardValues, value, VCardField.URL)) {
action = Action.MODIFY; action = Action.MODIFY;
} }
break; break;
...@@ -454,6 +478,14 @@ class ClearspaceVCardTranslator { ...@@ -454,6 +478,14 @@ class ClearspaceVCardTranslator {
return action; return action;
} }
/**
* Adds a profiles element to the profiles if it is not empty
*
* @param profiles the profiles to add a profile to
* @param field the field type to add
* @param newValue the value to add
* @return true if the field was added
*/
private boolean addProfile(Element profiles, ClearspaceField field, String newValue) { private boolean addProfile(Element profiles, ClearspaceField field, String newValue) {
// Don't add empty vales // Don't add empty vales
if (newValue == null || "".equals(newValue.trim())) { if (newValue == null || "".equals(newValue.trim())) {
...@@ -470,7 +502,15 @@ class ClearspaceVCardTranslator { ...@@ -470,7 +502,15 @@ class ClearspaceVCardTranslator {
return true; return true;
} }
private boolean modifyProfileValue(Map<VCardField, String> vCardValues, Element profiles, Element value, VCardField vCardField) { /**
* Modifies the value of a profile if it is different from the original one.
*
* @param vCardValues the vCard values with the new values
* @param value the current value of the profile
* @param vCardField the vCard field
* @return true if the field was modified
*/
private boolean modifyProfileValue(Map<VCardField, String> vCardValues, Element value, VCardField vCardField) {
boolean modified = false; boolean modified = false;
String newValue = vCardValues.get(vCardField); String newValue = vCardValues.get(vCardField);
...@@ -486,6 +526,13 @@ class ClearspaceVCardTranslator { ...@@ -486,6 +526,13 @@ class ClearspaceVCardTranslator {
return modified; return modified;
} }
/**
* Adds the field type to the field value. Returns null if the value is empty.
*
* @param value the value
* @param type the type
* @return the field value with the type
*/
private String addFieldType(String value, String type) { private String addFieldType(String value, String type) {
if (value == null || "".equals(value.trim())) { if (value == null || "".equals(value.trim())) {
return null; return null;
...@@ -493,6 +540,13 @@ class ClearspaceVCardTranslator { ...@@ -493,6 +540,13 @@ class ClearspaceVCardTranslator {
return value + "|" + type; return value + "|" + type;
} }
/**
* Returns the field type of a field. Return null if the field doesn't
* contains a type
*
* @param field the field with the type
* @return the field type
*/
private String getFieldType(String field) { private String getFieldType(String field) {
int i = field.indexOf("|"); int i = field.indexOf("|");
if (i == -1) { if (i == -1) {
...@@ -502,6 +556,13 @@ class ClearspaceVCardTranslator { ...@@ -502,6 +556,13 @@ class ClearspaceVCardTranslator {
} }
} }
/**
* Returns the field value of a field. Return the field if the field doesn't
* contains a type.
*
* @param field the field
* @return the field value
*/
private String getFieldValue(String field) { private String getFieldValue(String field) {
int i = field.indexOf("|"); int i = field.indexOf("|");
if (i == -1) { if (i == -1) {
...@@ -513,10 +574,10 @@ class ClearspaceVCardTranslator { ...@@ -513,10 +574,10 @@ class ClearspaceVCardTranslator {
/** /**
* Collects the vCard values and store them into a map. * Collects the vCard values and store them into a map.
* They are stored with this constants: * They are stored with the VCardField enum.
* *
* @param vCardElement * @param vCardElement the vCard with the information
* @return * @return a map of the value of the vCard.
*/ */
private Map<VCardField, String> collectVCardValues(Element vCardElement) { private Map<VCardField, String> collectVCardValues(Element vCardElement) {
...@@ -602,10 +663,10 @@ class ClearspaceVCardTranslator { ...@@ -602,10 +663,10 @@ class ClearspaceVCardTranslator {
/** /**
* Translates the information from Clearspace into a VCard. * Translates the information from Clearspace into a VCard.
* *
* @param profile * @param profile the profile
* @param user * @param user the user
* @param avatar * @param avatar the avatar
* @return * @return the vCard with the information
*/ */
protected Element translateClearspaceInfo(Element profile, User user, Element avatar) { protected Element translateClearspaceInfo(Element profile, User user, Element avatar) {
...@@ -619,7 +680,12 @@ class ClearspaceVCardTranslator { ...@@ -619,7 +680,12 @@ class ClearspaceVCardTranslator {
return vCard; return vCard;
} }
/**
* Translates the profile information to the vCard
*
* @param profiles the profile information
* @param vCard the vCard to add the information to
*/
private void translateProfileInformation(Element profiles, Element vCard) { private void translateProfileInformation(Element profiles, Element vCard) {
// Translate the profile XML // Translate the profile XML
...@@ -747,15 +813,21 @@ class ClearspaceVCardTranslator { ...@@ -747,15 +813,21 @@ class ClearspaceVCardTranslator {
} }
} }
/**
* Translates the user information to the vCard
*
* @param user the user information
* @param vCard the vCard to add the information to
*/
private void translateUserInformation(User user, Element vCard) { 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) // Only set the name to the VCard if it is visible
if (user.getName() != null && !"".equals(user.getName().trim())) { if (user.isNameVisible()) {
vCard.addElement("FN").setText(user.getName()); vCard.addElement("FN").setText(user.getName());
vCard.addElement("N").addElement("FAMILY").setText(user.getName()); vCard.addElement("N").addElement("FAMILY").setText(user.getName());
} }
// Email is mandatory, but may be invisible // Only set the eamail to the VCard if it is visible
if (user.getEmail() != null && !"".equals(user.getName().trim())) { if (user.isEmailVisible()) {
Element email = vCard.addElement("EMAIL"); Element email = vCard.addElement("EMAIL");
email.addElement("PREF"); email.addElement("PREF");
email.addElement("USERID").setText(user.getEmail()); email.addElement("USERID").setText(user.getEmail());
...@@ -765,6 +837,12 @@ class ClearspaceVCardTranslator { ...@@ -765,6 +837,12 @@ class ClearspaceVCardTranslator {
vCard.addElement("JABBERID").setText(jid); vCard.addElement("JABBERID").setText(jid);
} }
/**
* Translates the avatar information to the vCard.
*
* @param avatarResponse the avatar information
* @param vCard the vCard to add the information to
*/
private void translateAvatarInformation(Element avatarResponse, Element vCard) { private void translateAvatarInformation(Element avatarResponse, Element vCard) {
Element avatar = avatarResponse.element("return"); Element avatar = avatarResponse.element("return");
if (avatar != null) { if (avatar != null) {
...@@ -781,6 +859,12 @@ class ClearspaceVCardTranslator { ...@@ -781,6 +859,12 @@ class ClearspaceVCardTranslator {
} }
} }
/**
* Translates the address string of Clearspace to the vCard format.
*
* @param address the address string of Clearspae
* @param addressE the address element to add the address to
*/
private void translateAddress(String address, Element addressE) { private void translateAddress(String address, Element addressE) {
StringTokenizer strTokenize = new StringTokenizer(address, ","); StringTokenizer strTokenize = new StringTokenizer(address, ",");
while (strTokenize.hasMoreTokens()) { while (strTokenize.hasMoreTokens()) {
...@@ -819,6 +903,12 @@ class ClearspaceVCardTranslator { ...@@ -819,6 +903,12 @@ class ClearspaceVCardTranslator {
} }
/**
* Translates the address form the vCard format to the Clearspace string.
*
* @param addressElement the address in the vCard format
* @return the address int the Clearspace format
*/
private String translateAddress(Element addressElement) { private String translateAddress(Element addressElement) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
...@@ -850,8 +940,16 @@ class ClearspaceVCardTranslator { ...@@ -850,8 +940,16 @@ class ClearspaceVCardTranslator {
return sb.toString(); return sb.toString();
} }
private void translateAddressField(Element addressElement, String elementName, String fieldName, StringBuilder sb) { /**
String field = addressElement.elementTextTrim(elementName); * Translates the address field from the vCard format to the Clearspace format.
*
* @param addressElement the vCard address
* @param vCardFieldName the vCard field name
* @param fieldName the Clearspace field name
* @param sb the string builder to append the field string to
*/
private void translateAddressField(Element addressElement, String vCardFieldName, String fieldName, StringBuilder sb) {
String field = addressElement.elementTextTrim(vCardFieldName);
if (field != null && !"".equals(field)) { if (field != null && !"".equals(field)) {
sb.append(fieldName).append(":").append(field).append(","); sb.append(fieldName).append(":").append(field).append(",");
} }
......
...@@ -47,15 +47,20 @@ import java.util.concurrent.ConcurrentHashMap; ...@@ -47,15 +47,20 @@ import java.util.concurrent.ConcurrentHashMap;
public class User implements Cacheable, Externalizable, Result { public class User implements Cacheable, Externalizable, Result {
private static final String LOAD_PROPERTIES = private static final String LOAD_PROPERTIES =
"SELECT name, propValue FROM jiveUserProp WHERE username=?"; "SELECT name, propValue FROM jiveUserProp WHERE username=?";
private static final String LOAD_PROPERTY = private static final String LOAD_PROPERTY =
"SELECT propValue FROM jiveUserProp WHERE username=? AND name=?"; "SELECT propValue FROM jiveUserProp WHERE username=? AND name=?";
private static final String DELETE_PROPERTY = private static final String DELETE_PROPERTY =
"DELETE FROM jiveUserProp WHERE username=? AND name=?"; "DELETE FROM jiveUserProp WHERE username=? AND name=?";
private static final String UPDATE_PROPERTY = private static final String UPDATE_PROPERTY =
"UPDATE jiveUserProp SET propValue=? WHERE name=? AND username=?"; "UPDATE jiveUserProp SET propValue=? WHERE name=? AND username=?";
private static final String INSERT_PROPERTY = private static final String INSERT_PROPERTY =
"INSERT INTO jiveUserProp (username, name, propValue) VALUES (?, ?, ?)"; "INSERT INTO jiveUserProp (username, name, propValue) VALUES (?, ?, ?)";
// The name of the name visible property
private static final String NAME_VISIBLE_PROPERTY = "name.visible";
// The name of the email visible property
private static final String EMAIL_VISIBLE_PROPERTY = "email.visible";
private String username; private String username;
private String name; private String name;
...@@ -63,13 +68,13 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -63,13 +68,13 @@ public class User implements Cacheable, Externalizable, Result {
private Date creationDate; private Date creationDate;
private Date modificationDate; private Date modificationDate;
private Map<String,String> properties = null; private Map<String, String> properties = null;
/** /**
* Returns the value of the specified property for the given username. This method is * Returns the value of the specified property for the given username. This method is
* an optimization to avoid loading a user to get a specific property. * an optimization to avoid loading a user to get a specific property.
* *
* @param username the username of the user to get a specific property value. * @param username the username of the user to get a specific property value.
* @param propertyName the name of the property to return its value. * @param propertyName the name of the property to return its value.
* @return the value of the specified property for the given username. * @return the value of the specified property for the given username.
*/ */
...@@ -92,10 +97,18 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -92,10 +97,18 @@ public class User implements Cacheable, Externalizable, Result {
Log.error(sqle); Log.error(sqle);
} }
finally { finally {
try { if (pstmt != null) pstmt.close(); } try {
catch (Exception e) { Log.error(e); } if (pstmt != null) pstmt.close();
try { if (con != null) con.close(); } }
catch (Exception e) { Log.error(e); } catch (Exception e) {
Log.error(e);
}
try {
if (con != null) con.close();
}
catch (Exception e) {
Log.error(e);
}
} }
return propertyValue; return propertyValue;
} }
...@@ -113,15 +126,14 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -113,15 +126,14 @@ public class User implements Cacheable, Externalizable, Result {
* Typically, User objects should not be constructed by end-users of the API. * Typically, User objects should not be constructed by end-users of the API.
* Instead, user objects should be retrieved using {@link UserManager#getUser(String)}. * Instead, user objects should be retrieved using {@link UserManager#getUser(String)}.
* *
* @param username the username. * @param username the username.
* @param name the name. * @param name the name.
* @param email the email address. * @param email the email address.
* @param creationDate the date the user was created. * @param creationDate the date the user was created.
* @param modificationDate the date the user was last modified. * @param modificationDate the date the user was last modified.
*/ */
public User(String username, String name, String email, Date creationDate, public User(String username, String name, String email, Date creationDate,
Date modificationDate) Date modificationDate) {
{
if (username == null) { if (username == null) {
throw new NullPointerException("Username cannot be null"); throw new NullPointerException("Username cannot be null");
} }
...@@ -132,7 +144,7 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -132,7 +144,7 @@ public class User implements Cacheable, Externalizable, Result {
this.name = name; this.name = name;
if (UserManager.getUserProvider().isEmailRequired() && !StringUtils.isValidEmailAddress(email)) { if (UserManager.getUserProvider().isEmailRequired() && !StringUtils.isValidEmailAddress(email)) {
throw new IllegalArgumentException("Invalid or empty email address specified with provider that requires email address. User: " throw new IllegalArgumentException("Invalid or empty email address specified with provider that requires email address. User: "
+ username + " Email: " + email); + username + " Email: " + email);
} }
this.email = email; this.email = email;
this.creationDate = creationDate; this.creationDate = creationDate;
...@@ -162,7 +174,7 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -162,7 +174,7 @@ public class User implements Cacheable, Externalizable, Result {
AuthFactory.getAuthProvider().setPassword(username, password); AuthFactory.getAuthProvider().setPassword(username, password);
// Fire event. // Fire event.
Map<String,Object> params = new HashMap<String,Object>(); Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "passwordModified"); params.put("type", "passwordModified");
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified, UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
params); params);
...@@ -194,7 +206,7 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -194,7 +206,7 @@ public class User implements Cacheable, Externalizable, Result {
this.name = name; this.name = name;
// Fire event. // Fire event.
Map<String,Object> params = new HashMap<String,Object>(); Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "nameModified"); params.put("type", "nameModified");
params.put("originalValue", originalName); params.put("originalValue", originalName);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified, UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
...@@ -205,6 +217,24 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -205,6 +217,24 @@ public class User implements Cacheable, Externalizable, Result {
} }
} }
/**
* Returns true if name is visible to everyone or not.
*
* @return true if name is visible to everyone, false if not.
*/
public boolean isNameVisible() {
return !getProperties().containsKey(NAME_VISIBLE_PROPERTY) || Boolean.valueOf(getProperties().get(NAME_VISIBLE_PROPERTY));
}
/**
* Sets if name is visible to everyone or not.
*
* @param visible true if name is visible, false if not.
*/
public void setNameVisible(boolean visible) {
getProperties().put(NAME_VISIBLE_PROPERTY, String.valueOf(visible));
}
/** /**
* Returns the email address of the user or <tt>null</tt> if none is defined. * Returns the email address of the user or <tt>null</tt> if none is defined.
* *
...@@ -224,11 +254,11 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -224,11 +254,11 @@ public class User implements Cacheable, Externalizable, Result {
} }
try { try {
String originalEmail= this.email; String originalEmail = this.email;
UserManager.getUserProvider().setEmail(username, email); UserManager.getUserProvider().setEmail(username, email);
this.email = email; this.email = email;
// Fire event. // Fire event.
Map<String,Object> params = new HashMap<String,Object>(); Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "emailModified"); params.put("type", "emailModified");
params.put("originalValue", originalEmail); params.put("originalValue", originalEmail);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified, UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
...@@ -239,6 +269,24 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -239,6 +269,24 @@ public class User implements Cacheable, Externalizable, Result {
} }
} }
/**
* Returns true if email is visible to everyone or not.
*
* @return true if email is visible to everyone, false if not.
*/
public boolean isEmailVisible() {
return !getProperties().containsKey(EMAIL_VISIBLE_PROPERTY) || Boolean.valueOf(getProperties().get(EMAIL_VISIBLE_PROPERTY));
}
/**
* Sets if the email is visible to everyone or not.
*
* @param visible true if the email is visible, false if not.
*/
public void setEmailVisible(boolean visible) {
getProperties().put(EMAIL_VISIBLE_PROPERTY, String.valueOf(visible));
}
public Date getCreationDate() { public Date getCreationDate() {
return creationDate; return creationDate;
} }
...@@ -254,7 +302,7 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -254,7 +302,7 @@ public class User implements Cacheable, Externalizable, Result {
this.creationDate = creationDate; this.creationDate = creationDate;
// Fire event. // Fire event.
Map<String,Object> params = new HashMap<String,Object>(); Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "creationDateModified"); params.put("type", "creationDateModified");
params.put("originalValue", originalCreationDate); params.put("originalValue", originalCreationDate);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified, UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
...@@ -280,7 +328,7 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -280,7 +328,7 @@ public class User implements Cacheable, Externalizable, Result {
this.modificationDate = modificationDate; this.modificationDate = modificationDate;
// Fire event. // Fire event.
Map<String,Object> params = new HashMap<String,Object>(); Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "nameModified"); params.put("type", "nameModified");
params.put("originalValue", originalModificationDate); params.put("originalValue", originalModificationDate);
UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified, UserEventDispatcher.dispatchEvent(this, UserEventDispatcher.EventType.user_modified,
...@@ -298,7 +346,7 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -298,7 +346,7 @@ public class User implements Cacheable, Externalizable, Result {
* *
* @return the extended properties. * @return the extended properties.
*/ */
public Map<String,String> getProperties() { public Map<String, String> getProperties() {
synchronized (this) { synchronized (this) {
if (properties == null) { if (properties == null) {
properties = new ConcurrentHashMap<String, String>(); properties = new ConcurrentHashMap<String, String>();
...@@ -352,9 +400,8 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -352,9 +400,8 @@ public class User implements Cacheable, Externalizable, Result {
return true; return true;
} }
if (object != null && object instanceof User) { if (object != null && object instanceof User) {
return username.equals(((User)object).getUsername()); return username.equals(((User) object).getUsername());
} } else {
else {
return false; return false;
} }
} }
...@@ -365,22 +412,21 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -365,22 +412,21 @@ public class User implements Cacheable, Externalizable, Result {
private class PropertiesMap extends AbstractMap { private class PropertiesMap extends AbstractMap {
public Object put(Object key, Object value) { public Object put(Object key, Object value) {
Map<String,Object> eventParams = new HashMap<String,Object>(); Map<String, Object> eventParams = new HashMap<String, Object>();
Object answer; Object answer;
String keyString = (String) key; String keyString = (String) key;
synchronized (keyString.intern()) { synchronized (keyString.intern()) {
if (properties.containsKey(keyString)) { if (properties.containsKey(keyString)) {
String originalValue = properties.get(keyString); String originalValue = properties.get(keyString);
answer = properties.put(keyString, (String)value); answer = properties.put(keyString, (String) value);
updateProperty(keyString, (String)value); updateProperty(keyString, (String) value);
// Configure event. // Configure event.
eventParams.put("type", "propertyModified"); eventParams.put("type", "propertyModified");
eventParams.put("propertyKey", key); eventParams.put("propertyKey", key);
eventParams.put("originalValue", originalValue); eventParams.put("originalValue", originalValue);
} } else {
else { answer = properties.put(keyString, (String) value);
answer = properties.put(keyString, (String)value); insertProperty(keyString, (String) value);
insertProperty(keyString, (String)value);
// Configure event. // Configure event.
eventParams.put("type", "propertyAdded"); eventParams.put("type", "propertyAdded");
eventParams.put("propertyKey", key); eventParams.put("propertyKey", key);
...@@ -417,7 +463,7 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -417,7 +463,7 @@ public class User implements Cacheable, Externalizable, Result {
} }
public Object next() { public Object next() {
current = (Map.Entry)iter.next(); current = (Map.Entry) iter.next();
return current; return current;
} }
...@@ -425,15 +471,15 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -425,15 +471,15 @@ public class User implements Cacheable, Externalizable, Result {
if (current == null) { if (current == null) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
String key = (String)current.getKey(); String key = (String) current.getKey();
deleteProperty(key); deleteProperty(key);
iter.remove(); iter.remove();
// Fire event. // Fire event.
Map<String,Object> params = new HashMap<String,Object>(); Map<String, Object> params = new HashMap<String, Object>();
params.put("type", "propertyDeleted"); params.put("type", "propertyDeleted");
params.put("propertyKey", key); params.put("propertyKey", key);
UserEventDispatcher.dispatchEvent(User.this, UserEventDispatcher.dispatchEvent(User.this,
UserEventDispatcher.EventType.user_modified, params); UserEventDispatcher.EventType.user_modified, params);
} }
}; };
} }
...@@ -456,10 +502,18 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -456,10 +502,18 @@ public class User implements Cacheable, Externalizable, Result {
Log.error(sqle); Log.error(sqle);
} }
finally { finally {
try { if (pstmt != null) pstmt.close(); } try {
catch (Exception e) { Log.error(e); } if (pstmt != null) pstmt.close();
try { if (con != null) con.close(); } }
catch (Exception e) { Log.error(e); } catch (Exception e) {
Log.error(e);
}
try {
if (con != null) con.close();
}
catch (Exception e) {
Log.error(e);
}
} }
} }
...@@ -478,10 +532,18 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -478,10 +532,18 @@ public class User implements Cacheable, Externalizable, Result {
Log.error(e); Log.error(e);
} }
finally { finally {
try { if (pstmt != null) pstmt.close(); } try {
catch (Exception e) { Log.error(e); } if (pstmt != null) pstmt.close();
try { if (con != null) con.close(); } }
catch (Exception e) { Log.error(e); } catch (Exception e) {
Log.error(e);
}
try {
if (con != null) con.close();
}
catch (Exception e) {
Log.error(e);
}
} }
} }
...@@ -500,10 +562,18 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -500,10 +562,18 @@ public class User implements Cacheable, Externalizable, Result {
Log.error(e); Log.error(e);
} }
finally { finally {
try { if (pstmt != null) pstmt.close(); } try {
catch (Exception e) { Log.error(e); } if (pstmt != null) pstmt.close();
try { if (con != null) con.close(); } }
catch (Exception e) { Log.error(e); } catch (Exception e) {
Log.error(e);
}
try {
if (con != null) con.close();
}
catch (Exception e) {
Log.error(e);
}
} }
} }
...@@ -521,10 +591,18 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -521,10 +591,18 @@ public class User implements Cacheable, Externalizable, Result {
Log.error(e); Log.error(e);
} }
finally { finally {
try { if (pstmt != null) pstmt.close(); } try {
catch (Exception e) { Log.error(e); } if (pstmt != null) pstmt.close();
try { if (con != null) con.close(); } }
catch (Exception e) { Log.error(e); } catch (Exception e) {
Log.error(e);
}
try {
if (con != null) con.close();
}
catch (Exception e) {
Log.error(e);
}
} }
} }
...@@ -548,13 +626,12 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -548,13 +626,12 @@ public class User implements Cacheable, Externalizable, Result {
creationDate = new Date(ExternalizableUtil.getInstance().readLong(in)); creationDate = new Date(ExternalizableUtil.getInstance().readLong(in));
modificationDate = new Date(ExternalizableUtil.getInstance().readLong(in)); modificationDate = new Date(ExternalizableUtil.getInstance().readLong(in));
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see org.jivesoftware.util.resultsetmanager.Result#getUID() * @see org.jivesoftware.util.resultsetmanager.Result#getUID()
*/ */
public String getUID() public String getUID() {
{ return username;
return username; }
}
} }
...@@ -22,11 +22,11 @@ import java.util.concurrent.ConcurrentHashMap; ...@@ -22,11 +22,11 @@ import java.util.concurrent.ConcurrentHashMap;
* entities (i.e. users) the {@link User} name is used. For remote entities the following logic * entities (i.e. users) the {@link User} name is used. For remote entities the following logic
* is used: * is used:
* <ol> * <ol>
* <li>Check if a {@link UserNameProvider} is registered for the entity's domain. If a provider * <li>Check if a {@link UserNameProvider} is registered for the entity's domain. If a provider
* was found then use it to get the entity's name</li> * was found then use it to get the entity's name</li>
* <li>If no provider was found then retrieve the vCard of the entity and return the name as * <li>If no provider was found then retrieve the vCard of the entity and return the name as
* defined in the vCard. <i>This is not implemented yet.</i></li> * defined in the vCard. <i>This is not implemented yet.</i></li>
* <li>If no vCard was found then return the string representation of the entity's JID.</li> * <li>If no vCard was found then return the string representation of the entity's JID.</li>
* </ol> * </ol>
* *
* @author Gaston Dombiak * @author Gaston Dombiak
...@@ -72,7 +72,7 @@ public class UserNameManager { ...@@ -72,7 +72,7 @@ public class UserNameManager {
* @param entity the JID of the entity to get its name. * @param entity the JID of the entity to get its name.
* @return the name of the XMPP entity. * @return the name of the XMPP entity.
* @throws UserNotFoundException if the jid belongs to the local server but no user was * @throws UserNotFoundException if the jid belongs to the local server but no user was
* found for that jid. * found for that jid.
*/ */
public static String getUserName(JID entity) throws UserNotFoundException { public static String getUserName(JID entity) throws UserNotFoundException {
return getUserName(entity, entity.toString()); return getUserName(entity, entity.toString());
...@@ -85,19 +85,18 @@ public class UserNameManager { ...@@ -85,19 +85,18 @@ public class UserNameManager {
* the vCard of the entity might be requested and if none was found then a string * the vCard of the entity might be requested and if none was found then a string
* representation of the entity's JID will be returned. * representation of the entity's JID will be returned.
* *
* @param entity the JID of the entity to get its name. * @param entity the JID of the entity to get its name.
* @param defaultName default name to return when no name was found. * @param defaultName default name to return when no name was found.
* @return the name of the XMPP entity. * @return the name of the XMPP entity.
* @throws UserNotFoundException if the jid belongs to the local server but no user was * @throws UserNotFoundException if the jid belongs to the local server but no user was
* found for that jid. * found for that jid.
*/ */
public static String getUserName(JID entity, String defaultName) throws UserNotFoundException { public static String getUserName(JID entity, String defaultName) throws UserNotFoundException {
if (server.isLocal(entity)) { if (server.isLocal(entity)) {
// Contact is a local entity so search for his user name // Contact is a local entity so search for his user name
User localUser = UserManager.getInstance().getUser(entity.getNode()); User localUser = UserManager.getInstance().getUser(entity.getNode());
return "".equals(localUser.getName()) ? entity.getNode() : localUser.getName(); return !localUser.isNameVisible() || "".equals(localUser.getName()) ? entity.getNode() : localUser.getName();
} } else {
else {
UserNameProvider provider = providersByDomain.get(entity.getDomain()); UserNameProvider provider = providersByDomain.get(entity.getDomain());
if (provider != null) { if (provider != null) {
return provider.getUserName(entity); return provider.getUserName(entity);
......
...@@ -40,25 +40,25 @@ import java.io.File; ...@@ -40,25 +40,25 @@ import java.io.File;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
/** /**
* Provides support for Jabber Search * Provides support for Jabber Search
* (<a href="http://www.xmpp.org/extensions/xep-0055.html">XEP-0055</a>).<p> * (<a href="http://www.xmpp.org/extensions/xep-0055.html">XEP-0055</a>).<p>
* * <p/>
* The basic functionality is to query an information repository * The basic functionality is to query an information repository
* regarding the possible search fields, to send a search query, * regarding the possible search fields, to send a search query,
* and to receive search results. This implementation was primarily designed to use * and to receive search results. This implementation was primarily designed to use
* <a href="http://www.xmpp.org/extensions/xep-0004.html">Data Forms</a>, but * <a href="http://www.xmpp.org/extensions/xep-0004.html">Data Forms</a>, but
* also supports non-dataform searches. * also supports non-dataform searches.
* <p/> * <p/>
* *
* @author <a href="mailto:ryan@version2software.com">Ryan Graham</a> * @author <a href="mailto:ryan@version2software.com">Ryan Graham</a>
*/ */
public class SearchPlugin implements Component, Plugin, PropertyEventListener { public class SearchPlugin implements Component, Plugin, PropertyEventListener {
public static final String NAMESPACE_JABBER_IQ_SEARCH = "jabber:iq:search"; public static final String NAMESPACE_JABBER_IQ_SEARCH = "jabber:iq:search";
public static final String SERVICENAME = "plugin.search.serviceName"; public static final String SERVICENAME = "plugin.search.serviceName";
public static final String SERVICEENABLED = "plugin.search.serviceEnabled"; public static final String SERVICEENABLED = "plugin.search.serviceEnabled";
public static final String EXCLUDEDFIELDS = "plugin.search.excludedFields"; public static final String EXCLUDEDFIELDS = "plugin.search.excludedFields";
private UserManager userManager; private UserManager userManager;
private ComponentManager componentManager; private ComponentManager componentManager;
private PluginManager pluginManager; private PluginManager pluginManager;
...@@ -66,35 +66,36 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -66,35 +66,36 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
private String serviceName; private String serviceName;
private boolean serviceEnabled; private boolean serviceEnabled;
private Collection<String> exculudedFields; private Collection<String> exculudedFields;
private static String serverName; private static String serverName;
private TreeMap<String, String> fieldLookup = new TreeMap<String, String>(new CaseInsensitiveComparator()); private TreeMap<String, String> fieldLookup = new TreeMap<String, String>(new CaseInsensitiveComparator());
private Map<String, String> reverseFieldLookup = new HashMap<String, String>(); private Map<String, String> reverseFieldLookup = new HashMap<String, String>();
/** /**
* A list of field names that are valid in jabber:iq:search * A list of field names that are valid in jabber:iq:search
*/ */
public final static Collection<String> validSearchRequestFields = new ArrayList<String>(); public final static Collection<String> validSearchRequestFields = new ArrayList<String>();
static {
validSearchRequestFields.add("first"); static {
validSearchRequestFields.add("last"); validSearchRequestFields.add("first");
validSearchRequestFields.add("nick"); validSearchRequestFields.add("last");
validSearchRequestFields.add("email"); validSearchRequestFields.add("nick");
validSearchRequestFields.add("x"); // extended info validSearchRequestFields.add("email");
validSearchRequestFields.add("x"); // extended info
// result set management (XEP-0059)
validSearchRequestFields.add("set"); // result set management (XEP-0059)
} validSearchRequestFields.add("set");
}
public SearchPlugin() { public SearchPlugin() {
serviceName = JiveGlobals.getProperty(SERVICENAME, "search"); serviceName = JiveGlobals.getProperty(SERVICENAME, "search");
serviceEnabled = JiveGlobals.getBooleanProperty(SERVICEENABLED, true); serviceEnabled = JiveGlobals.getBooleanProperty(SERVICEENABLED, true);
exculudedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, "")); exculudedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, ""));
serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain(); serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
userManager = UserManager.getInstance(); userManager = UserManager.getInstance();
// Some clients, such as Miranda, are hard-coded to search specific fields, // Some clients, such as Miranda, are hard-coded to search specific fields,
// so we map those fields to the fields that Openfire actually supports. // so we map those fields to the fields that Openfire actually supports.
fieldLookup.put("jid", "Username"); fieldLookup.put("jid", "Username");
...@@ -105,34 +106,34 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -105,34 +106,34 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
fieldLookup.put("name", "Name"); fieldLookup.put("name", "Name");
fieldLookup.put("email", "Email"); fieldLookup.put("email", "Email");
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.xmpp.component.Component#getName() * @see org.xmpp.component.Component#getName()
*/ */
public String getName() { public String getName() {
return pluginManager.getName(this); return pluginManager.getName(this);
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.xmpp.component.Component#getDescription() * @see org.xmpp.component.Component#getDescription()
*/ */
public String getDescription() { public String getDescription() {
return pluginManager.getDescription(this); return pluginManager.getDescription(this);
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware.openfire.container.PluginManager, * @see org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware.openfire.container.PluginManager,
* java.io.File) * java.io.File)
*/ */
public void initializePlugin(PluginManager manager, File pluginDirectory) { public void initializePlugin(PluginManager manager, File pluginDirectory) {
pluginManager = manager; pluginManager = manager;
componentManager = ComponentManagerFactory.getComponentManager(); componentManager = ComponentManagerFactory.getComponentManager();
try { try {
componentManager.addComponent(serviceName, this); componentManager.addComponent(serviceName, this);
...@@ -142,29 +143,29 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -142,29 +143,29 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} }
PropertyEventDispatcher.addListener(this); PropertyEventDispatcher.addListener(this);
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.xmpp.component.Component#initialize(org.xmpp.packet.JID, * @see org.xmpp.component.Component#initialize(org.xmpp.packet.JID,
* org.xmpp.component.ComponentManager) * org.xmpp.component.ComponentManager)
*/ */
public void initialize(JID jid, ComponentManager componentManager) { public void initialize(JID jid, ComponentManager componentManager) {
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.xmpp.component.Component#start() * @see org.xmpp.component.Component#start()
*/ */
public void start() { public void start() {
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.jivesoftware.openfire.container.Plugin#destroyPlugin() * @see org.jivesoftware.openfire.container.Plugin#destroyPlugin()
*/ */
public void destroyPlugin() { public void destroyPlugin() {
PropertyEventDispatcher.removeListener(this); PropertyEventDispatcher.removeListener(this);
pluginManager = null; pluginManager = null;
...@@ -185,19 +186,19 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -185,19 +186,19 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
reverseFieldLookup = null; reverseFieldLookup = null;
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.xmpp.component.Component#shutdown() * @see org.xmpp.component.Component#shutdown()
*/ */
public void shutdown() { public void shutdown() {
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.xmpp.component.Component#processPacket(org.xmpp.packet.Packet) * @see org.xmpp.component.Component#processPacket(org.xmpp.packet.Packet)
*/ */
public void processPacket(Packet p) { public void processPacket(Packet p) {
if (!(p instanceof IQ)) { if (!(p instanceof IQ)) {
return; return;
...@@ -229,8 +230,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -229,8 +230,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* that <strong>all</strong> IQ request stanza's (type 'get' or 'set') MUST * that <strong>all</strong> IQ request stanza's (type 'get' or 'set') MUST
* be replied to. * be replied to.
* *
* @param iq * @param iq The IQ stanza that forms the request.
* The IQ stanza that forms the request.
* @return The response to the request. * @return The response to the request.
*/ */
private IQ handleIQRequest(IQ iq) { private IQ handleIQRequest(IQ iq) {
...@@ -284,8 +284,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -284,8 +284,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/** /**
* Creates a response specific to the search plugin to Disco#Info requests. * Creates a response specific to the search plugin to Disco#Info requests.
* *
* @param iq * @param iq The IQ stanza that contains the request.
* The IQ stanza that contains the request.
* @return An IQ stanza, formulated as an answer to the received request. * @return An IQ stanza, formulated as an answer to the received request.
*/ */
private static IQ handleDiscoInfo(IQ iq) { private static IQ handleDiscoInfo(IQ iq) {
...@@ -323,15 +322,15 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -323,15 +322,15 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} }
switch (packet.getType()) { switch (packet.getType()) {
case get: case get:
return processGetPacket(packet); return processGetPacket(packet);
case set: case set:
return processSetPacket(packet); return processSetPacket(packet);
default: default:
// we can safely ignore 'error' and 'result' typed iq stanzas. // we can safely ignore 'error' and 'result' typed iq stanzas.
return null; return null;
} }
} }
...@@ -340,8 +339,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -340,8 +339,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* provided as an argument. The stanza tells the recipient that this service * provided as an argument. The stanza tells the recipient that this service
* is currently unavailable. * is currently unavailable.
* *
* @param packet * @param packet The request IQ stanza to which a result will be returned.
* The request IQ stanza to which a result will be returned.
* @return A result stanza, telling the user that this service is * @return A result stanza, telling the user that this service is
* unavailable. * unavailable.
*/ */
...@@ -363,8 +361,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -363,8 +361,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* Processes an IQ stanza of type 'get', which in the context of 'Jabber * Processes an IQ stanza of type 'get', which in the context of 'Jabber
* Search' is a request for available search fields. * Search' is a request for available search fields.
* *
* @param packet * @param packet An IQ stanza of type 'get'
* An IQ stanza of type 'get'
* @return A result IQ stanza that contains the possbile search fields. * @return A result IQ stanza that contains the possbile search fields.
*/ */
private IQ processGetPacket(IQ packet) { private IQ processGetPacket(IQ packet) {
...@@ -409,7 +406,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -409,7 +406,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
field.setType(FormField.TYPE_BOOLEAN); field.setType(FormField.TYPE_BOOLEAN);
field.addValue("1"); field.addValue("1");
field.setLabel(LocaleUtils.getLocalizedString( field.setLabel(LocaleUtils.getLocalizedString(
"advance.user.search." + searchField.toLowerCase(), "search")); "advance.user.search." + searchField.toLowerCase(), "search"));
field.setRequired(false); field.setRequired(false);
searchForm.addField(field); searchForm.addField(field);
} }
...@@ -424,8 +421,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -424,8 +421,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* Processes an IQ stanza of type 'set', which in the context of 'Jabber * Processes an IQ stanza of type 'set', which in the context of 'Jabber
* Search' is a search request. * Search' is a search request.
* *
* @param packet * @param packet An IQ stanza of type 'get'
* An IQ stanza of type 'get'
* @return A result IQ stanza that contains the possbile search fields. * @return A result IQ stanza that contains the possbile search fields.
*/ */
private IQ processSetPacket(IQ packet) { private IQ processSetPacket(IQ packet) {
...@@ -497,8 +493,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -497,8 +493,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* <li>if the stanza child element is has valid children itself.</li> * <li>if the stanza child element is has valid children itself.</li>
* </ul> * </ul>
* *
* @param iq * @param iq The IQ object that should include a jabber:iq:search request.
* The IQ object that should include a jabber:iq:search request.
* @return ''true'' if the supplied IQ stanza is a spec compliant search * @return ''true'' if the supplied IQ stanza is a spec compliant search
* request, ''false'' otherwise. * request, ''false'' otherwise.
*/ */
...@@ -553,8 +548,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -553,8 +548,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/** /**
* Performs a search based on form data, and returns the search results. * Performs a search based on form data, and returns the search results.
* *
* @param incomingForm * @param incomingForm The form containing the search data
* The form containing the search data
* @return A set of users that matches the search criteria. * @return A set of users that matches the search criteria.
*/ */
private Set<User> performSearch(Element incomingForm) { private Set<User> performSearch(Element incomingForm) {
...@@ -593,7 +587,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -593,7 +587,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* defined as a set of key->value pairs, where the key denotes a search * defined as a set of key->value pairs, where the key denotes a search
* field, and the value contains the value that was filled out by the user * field, and the value contains the value that was filled out by the user
* for that field. * for that field.
* * <p/>
* The query can be specified in one of two ways. The first way is a query * The query can be specified in one of two ways. The first way is a query
* is formed is by filling out any of the the standard search fields. The * is formed is by filling out any of the the standard search fields. The
* other search method makes use of extended data forms. Search queries that * other search method makes use of extended data forms. Search queries that
...@@ -601,8 +595,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -601,8 +595,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* of this last method get forwarded to * of this last method get forwarded to
* {@link #extractExtendedSearchQuery(Element)}. * {@link #extractExtendedSearchQuery(Element)}.
* *
* @param incomingForm * @param incomingForm The form from which to extract the query
* The form from which to extract the query
* @return The search query for a particular user search request. * @return The search query for a particular user search request.
*/ */
private Hashtable<String, String> extractSearchQuery(Element incomingForm) { private Hashtable<String, String> extractSearchQuery(Element incomingForm) {
...@@ -637,8 +630,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -637,8 +630,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* specify the search request. This 'extended' way of constructing a search * specify the search request. This 'extended' way of constructing a search
* request is documented in XEP-0055, chapter 3. * request is documented in XEP-0055, chapter 3.
* *
* @param incomingForm * @param incomingForm The form from which to extract the query
* The form from which to extract the query
* @return The search query for a particular user search request. * @return The search query for a particular user search request.
* @see #extractSearchQuery(Element) * @see #extractSearchQuery(Element)
*/ */
...@@ -677,7 +669,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -677,7 +669,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/** /**
* Constructs a query that is returned as an IQ packet that contains the search results. * Constructs a query that is returned as an IQ packet that contains the search results.
* *
* @param users set of users that will be used to construct the search results * @param users set of users that will be used to construct the search results
* @param packet the IQ packet sent by the client * @param packet the IQ packet sent by the client
* @return the iq packet that contains the search results * @return the iq packet that contains the search results
*/ */
...@@ -695,7 +687,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -695,7 +687,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
for (String fieldName : getFilteredSearchFields()) { for (String fieldName : getFilteredSearchFields()) {
field = new XFormFieldImpl(fieldName); field = new XFormFieldImpl(fieldName);
field.setLabel(LocaleUtils.getLocalizedString( field.setLabel(LocaleUtils.getLocalizedString(
"advance.user.search." + fieldName.toLowerCase(), "search")); "advance.user.search." + fieldName.toLowerCase(), "search"));
searchResults.addReportedField(field); searchResults.addReportedField(field);
} }
...@@ -713,11 +705,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -713,11 +705,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
items.add(fieldUsername); items.add(fieldUsername);
XFormFieldImpl fieldName = new XFormFieldImpl(LocaleUtils.getLocalizedString("advance.user.search.name", "search")); XFormFieldImpl fieldName = new XFormFieldImpl(LocaleUtils.getLocalizedString("advance.user.search.name", "search"));
fieldName.addValue(removeNull(user.getName())); fieldName.addValue((user.isNameVisible() ? removeNull(user.getName()) : ""));
items.add(fieldName); items.add(fieldName);
XFormFieldImpl fieldEmail = new XFormFieldImpl(LocaleUtils.getLocalizedString("advance.user.search.email", "search")); XFormFieldImpl fieldEmail = new XFormFieldImpl(LocaleUtils.getLocalizedString("advance.user.search.email", "search"));
fieldEmail.addValue(removeNull(user.getEmail())); fieldEmail.addValue((user.isEmailVisible() ? removeNull(user.getEmail()) : ""));
items.add(fieldEmail); items.add(fieldEmail);
searchResults.addItemFields(items); searchResults.addItemFields(items);
...@@ -734,7 +726,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -734,7 +726,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/** /**
* Constructs a query that is returned as an IQ packet that contains the search results. * Constructs a query that is returned as an IQ packet that contains the search results.
* *
* @param users set of users that will be used to construct the search results * @param users set of users that will be used to construct the search results
* @param packet the IQ packet sent by the client * @param packet the IQ packet sent by the client
* @return the iq packet that contains the search results * @return the iq packet that contains the search results
*/ */
...@@ -759,13 +751,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -759,13 +751,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
if ("Name".equals(field)) { if ("Name".equals(field)) {
Element element = item.addElement(reverseFieldLookup Element element = item.addElement(reverseFieldLookup
.get(field)); .get(field));
element.addText(removeNull(user.getName())); element.addText(user.isNameVisible() ? removeNull(user.getName()) : "");
} }
if ("Email".equals(field)) { if ("Email".equals(field)) {
Element element = item.addElement(reverseFieldLookup Element element = item.addElement(reverseFieldLookup
.get(field)); .get(field));
element.addText(removeNull(user.getEmail())); element.addText(user.isEmailVisible() ? removeNull(user.getEmail()) : "");
} }
} }
} }
...@@ -781,7 +773,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -781,7 +773,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
public String getServiceName() { public String getServiceName() {
return serviceName; return serviceName;
} }
/** /**
* Sets the service name of this component, which is "search" by default. If the name * Sets the service name of this component, which is "search" by default. If the name
* is different than the existing name the plugin will remove itself from the ComponentManager * is different than the existing name the plugin will remove itself from the ComponentManager
...@@ -793,125 +785,121 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -793,125 +785,121 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
changeServiceName(name); changeServiceName(name);
JiveGlobals.setProperty(SERVICENAME, name); JiveGlobals.setProperty(SERVICENAME, name);
} }
/** /**
* Checks if the search service is enabled. * Checks if the search service is enabled.
* *
* @return true if search service is enabled. * @return true if search service is enabled.
*/ */
public boolean getServiceEnabled() { public boolean getServiceEnabled() {
return serviceEnabled; return serviceEnabled;
} }
/** /**
* Enables or disables the search service. When disabled, when a client tries * Enables or disables the search service. When disabled, when a client tries
* to do a search they will receive an XForm informing that the service is * to do a search they will receive an XForm informing that the service is
* unavailable. * unavailable.
* *
* @param enabled true if group permission checking should be disabled. * @param enabled true if group permission checking should be disabled.
*/ */
public void setServiceEnabled(boolean enabled) { public void setServiceEnabled(boolean enabled) {
serviceEnabled = enabled; serviceEnabled = enabled;
JiveGlobals.setProperty(SERVICEENABLED, enabled ? "true" : "false"); JiveGlobals.setProperty(SERVICEENABLED, enabled ? "true" : "false");
} }
/** /**
* Returns the collection of searchable field names that does not include the fields * Returns the collection of searchable field names that does not include the fields
* listed in the EXCLUDEDFIELDS property list. * listed in the EXCLUDEDFIELDS property list.
* *
* @return collection of searchable field names. * @return collection of searchable field names.
*/ */
public Collection<String> getFilteredSearchFields() { public Collection<String> getFilteredSearchFields() {
Collection<String> searchFields; Collection<String> searchFields;
// See if the installed provider supports searching. If not, workaround // See if the installed provider supports searching. If not, workaround
// by providing our own searching. // by providing our own searching.
try { try {
searchFields = new ArrayList<String>(userManager.getSearchFields()); searchFields = new ArrayList<String>(userManager.getSearchFields());
} }
catch (UnsupportedOperationException uoe) { catch (UnsupportedOperationException uoe) {
// Use a SearchPluginUserManager instead. // Use a SearchPluginUserManager instead.
searchFields = getSearchPluginUserManagerSearchFields(); searchFields = getSearchPluginUserManagerSearchFields();
} }
searchFields.removeAll(exculudedFields); searchFields.removeAll(exculudedFields);
return searchFields; return searchFields;
} }
/** /**
* Restricts which fields can be searched on and shown to clients. This can be used * Restricts which fields can be searched on and shown to clients. This can be used
* in the case of preventing users email addresses from being revealed as part of * in the case of preventing users email addresses from being revealed as part of
* the search results. * the search results.
* *
* @param exculudedFields fields that can not be searched on or shown to the client * @param exculudedFields fields that can not be searched on or shown to the client
*/ */
public void setExcludedFields(Collection<String> exculudedFields) { public void setExcludedFields(Collection<String> exculudedFields) {
this.exculudedFields = exculudedFields; this.exculudedFields = exculudedFields;
JiveGlobals.setProperty(EXCLUDEDFIELDS, StringUtils.collectionToString(exculudedFields)); JiveGlobals.setProperty(EXCLUDEDFIELDS, StringUtils.collectionToString(exculudedFields));
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.jivesoftware.util.PropertyEventListener#propertySet(java.lang.String, * @see org.jivesoftware.util.PropertyEventListener#propertySet(java.lang.String,
* java.util.Map) * java.util.Map)
*/ */
public void propertySet(String property, Map<String, Object> params) { public void propertySet(String property, Map<String, Object> params) {
if (property.equals(SERVICEENABLED)) { if (property.equals(SERVICEENABLED)) {
this.serviceEnabled = Boolean.parseBoolean((String)params.get("value")); this.serviceEnabled = Boolean.parseBoolean((String) params.get("value"));
} } else if (property.equals(SERVICENAME)) {
else if (property.equals(SERVICENAME)) { changeServiceName((String) params.get("value"));
changeServiceName((String)params.get("value")); } else if (property.equals(EXCLUDEDFIELDS)) {
} exculudedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, (String) params.get("value")));
else if (property.equals(EXCLUDEDFIELDS)) {
exculudedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, (String)params.get("value")));
} }
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.jivesoftware.util.PropertyEventListener#propertyDeleted(java.lang.String, * @see org.jivesoftware.util.PropertyEventListener#propertyDeleted(java.lang.String,
* java.util.Map) * java.util.Map)
*/ */
public void propertyDeleted(String property, Map<String, Object> params) { public void propertyDeleted(String property, Map<String, Object> params) {
if (property.equals(SERVICEENABLED)) { if (property.equals(SERVICEENABLED)) {
this.serviceEnabled = true; this.serviceEnabled = true;
} } else if (property.equals(SERVICENAME)) {
else if (property.equals(SERVICENAME)) {
changeServiceName("search"); changeServiceName("search");
} } else if (property.equals(EXCLUDEDFIELDS)) {
else if (property.equals(EXCLUDEDFIELDS)) { exculudedFields = new ArrayList<String>();
exculudedFields = new ArrayList<String>();
} }
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.jivesoftware.util.PropertyEventListener#xmlPropertySet(java.lang.String, * @see org.jivesoftware.util.PropertyEventListener#xmlPropertySet(java.lang.String,
* java.util.Map) * java.util.Map)
*/ */
public void xmlPropertySet(String property, Map<String, Object> params) { public void xmlPropertySet(String property, Map<String, Object> params) {
// not used // not used
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.jivesoftware.util.PropertyEventListener#xmlPropertyDeleted(java.lang.String, * @see org.jivesoftware.util.PropertyEventListener#xmlPropertyDeleted(java.lang.String,
* java.util.Map) * java.util.Map)
*/ */
public void xmlPropertyDeleted(String property, Map<String, Object> params) { public void xmlPropertyDeleted(String property, Map<String, Object> params) {
// not used // not used
} }
private void changeServiceName(String serviceName) { private void changeServiceName(String serviceName) {
if (serviceName == null) { if (serviceName == null) {
throw new NullPointerException("Service name cannot be null"); throw new NullPointerException("Service name cannot be null");
} }
if (this.serviceName.equals(serviceName)) { if (this.serviceName.equals(serviceName)) {
return; return;
} }
...@@ -923,40 +911,39 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -923,40 +911,39 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
catch (Exception e) { catch (Exception e) {
componentManager.getLog().error(e); componentManager.getLog().error(e);
} }
try { try {
componentManager.addComponent(serviceName, this); componentManager.addComponent(serviceName, this);
} }
catch (Exception e) { catch (Exception e) {
componentManager.getLog().error(e); componentManager.getLog().error(e);
} }
this.serviceName = serviceName; this.serviceName = serviceName;
} }
/** /**
* Comparator that compares String objects, ignoring capitalization. * Comparator that compares String objects, ignoring capitalization.
*/ */
private class CaseInsensitiveComparator implements Comparator<String> { private class CaseInsensitiveComparator implements Comparator<String> {
public int compare(String s1, String s2) { public int compare(String s1, String s2) {
return s1.compareToIgnoreCase(s2); return s1.compareToIgnoreCase(s2);
} }
} }
/** /**
* Returns the trimmed argument, or an empty String object of null was * Returns the trimmed argument, or an empty String object of null was
* supplied as an argument. * supplied as an argument.
* *
* @param s * @param s The String to be trimmed.
* The String to be trimmed. * @return String object that does not start or end with whitespace
* @return String object that does not start or end with whitespace * characters.
* characters. */
*/
private String removeNull(String s) { private String removeNull(String s) {
if (s == null) { if (s == null) {
return ""; return "";
} }
return s.trim(); return s.trim();
} }
...@@ -976,7 +963,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -976,7 +963,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* field name of "email" and query of "jsmith@example.com" would search for * field name of "email" and query of "jsmith@example.com" would search for
* the user with that email address. Wildcard (*) characters are allowed as * the user with that email address. Wildcard (*) characters are allowed as
* part of queries. * part of queries.
* * <p/>
* A possible future improvement would be to have a third parameter that * A possible future improvement would be to have a third parameter that
* sets the maximum number of users returned and/or the number of users * sets the maximum number of users returned and/or the number of users
* that are searched. * that are searched.
...@@ -1004,33 +991,30 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -1004,33 +991,30 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
catch (UserNotFoundException e) { catch (UserNotFoundException e) {
Log.error("Error getting user", e); Log.error("Error getting user", e);
} }
} } else if (field.equals("Name")) {
else if (field.equals("Name")) { if (user.isNameVisible()) {
if (query.equalsIgnoreCase(user.getName())) { if (query.equalsIgnoreCase(user.getName())) {
foundUsers.add(user); foundUsers.add(user);
}
} }
} } else if (field.equals("Email")) {
else if (field.equals("Email")) { if (user.isEmailVisible() && user.getEmail() != null) {
if (user.getEmail() != null) {
if (query.equalsIgnoreCase(user.getEmail())) { if (query.equalsIgnoreCase(user.getEmail())) {
foundUsers.add(user); foundUsers.add(user);
} }
} }
} }
} }
} } else {
else {
String prefix = query.substring(0, index); String prefix = query.substring(0, index);
Collection<User> users = userManager.getUsers(); Collection<User> users = userManager.getUsers();
for (User user : users) { for (User user : users) {
String userInfo = ""; String userInfo = "";
if (field.equals("Username")) { if (field.equals("Username")) {
userInfo = user.getUsername(); userInfo = user.getUsername();
} } else if (field.equals("Name")) {
else if (field.equals("Name")) {
userInfo = user.getName(); userInfo = user.getName();
} } else if (field.equals("Email")) {
else if (field.equals("Email")) {
userInfo = user.getEmail() == null ? "" : user.getEmail(); userInfo = user.getEmail() == null ? "" : user.getEmail();
} }
......
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