Commit 71627198 authored by Leon Roy's avatar Leon Roy Committed by leonroy

RESOLVED - issue OF-681: Add ability to search plugin to be able to restrict...

RESOLVED - issue OF-681: Add ability to search plugin to be able to restrict searching for users to only the group a user is in
http://issues.igniterealtime.org/browse/OF-681

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13705 b35dd754-fafc-0310-a699-88a17e54d16e
parent 6a4af76e
......@@ -44,6 +44,11 @@
Search Plugin Changelog
</h1>
<p><b>1.5.2</b> -- July 4, 2013</p>
<ul>
<li>OF-681 - Add ability to search plugin to be able to restrict searching for users to only the group a user is in.</li>
</ul>
<p><b>1.5.1</b> -- January 2, 2010</p>
<ul>
<li>[<a href='http://www.igniterealtime.org/issues/browse/OF-45'>OF-45</a>] - Added Lithuanian translation by Rytis Umbrasas.</li>
......
......@@ -5,8 +5,8 @@
<name>Search</name>
<description>Provides support for Jabber Search (XEP-0055)</description>
<author>Ryan Graham</author>
<version>1.5.1</version>
<date>1/2/2010</date>
<version>1.5.2</version>
<date>7/4/2013</date>
<minServerVersion>3.7.0</minServerVersion>
<adminconsole>
......
......@@ -68,4 +68,11 @@ search.props.edit.form.searchable_fields_details=Use the form below to enable wh
search.props.edit.form.fields=Fields
search.props.edit.form.save_properties=Save Properties
search.props.edit.form.search_scope=Search Scope
search.props.edit.form.search_scope_directions=Use the form below to restrict the search scope so users can search for anyone or only users in the same group.
search.props.edit.form.search_scope_anyone=Anyone
search.props.edit.form.search_scope_anyone_details=Clients will be able to search for all users.
search.props.edit.form.search_scope_groups=Group
search.props.edit.form.search_scope_groups_details=Clients will be able to only search for users in their group.
search.service_unavailable=This service is unavailable.
......@@ -69,5 +69,12 @@ search.props.edit.form.searchable_fields_details=\u017demiau esan\u010dia forma
search.props.edit.form.fields=Laukai
search.props.edit.form.save_properties=I\u0161saugoti nuostatas
search.props.edit.form.search_scope=Search Scope
search.props.edit.form.search_scope_directions=Use the form below to restrict the search scope so users can search for anyone or only users in the same group.
search.props.edit.form.search_scope_anyone=Anyone
search.props.edit.form.search_scope_anyone_details=Clients will be able to search for all users.
search.props.edit.form.search_scope_groups=Group
search.props.edit.form.search_scope_groups_details=Clients will be able to only search for users in their group.
search.service_unavailable=Paslauga negalima.
......@@ -48,4 +48,11 @@ search.props.edit.form.searchable_fields_details=Utilize o formul\u00e1rio abaix
search.props.edit.form.fields=Campos
search.props.edit.form.save_properties=Gravar Op\u00e7\u00f5es
search.props.edit.form.search_scope=Search Scope
search.props.edit.form.search_scope_directions=Use the form below to restrict the search scope so users can search for anyone or only users in the same group.
search.props.edit.form.search_scope_anyone=Anyone
search.props.edit.form.search_scope_anyone_details=Clients will be able to search for all users.
search.props.edit.form.search_scope_groups=Group
search.props.edit.form.search_scope_groups_details=Clients will be able to only search for users in their group.
search.service_unavailable=Este servi\u00e7o est\u00e1 indispon\u00edvel.
......@@ -27,9 +27,9 @@ import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
......@@ -39,6 +39,8 @@ import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.disco.IQDiscoInfoHandler;
import org.jivesoftware.openfire.disco.IQDiscoItemsHandler;
import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupManager;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.util.JiveGlobals;
......@@ -55,23 +57,21 @@ import org.xmpp.component.ComponentManagerFactory;
import org.xmpp.forms.DataForm;
import org.xmpp.forms.FormField;
import org.xmpp.packet.IQ;
import org.xmpp.packet.IQ.Type;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.IQ.Type;
import org.xmpp.packet.PacketError.Condition;
import org.xmpp.resultsetmanagement.ResultSet;
import org.xmpp.resultsetmanagement.ResultSetImpl;
/**
* Provides support for Jabber Search
* (<a href="http://www.xmpp.org/extensions/xep-0055.html">XEP-0055</a>).<p>
*
* The basic functionality is to query an information repository
* regarding the possible search fields, to send a search query,
* 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
* also supports non-dataform searches.
* Provides support for Jabber Search (<a href="http://www.xmpp.org/extensions/xep-0055.html">XEP-0055</a>).
* <p>
*
* The basic functionality is to query an information repository regarding the possible search fields, to send a search query, 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 also supports non-dataform searches.
* <p/>
*
* @author <a href="mailto:ryan@version2software.com">Ryan Graham</a>
......@@ -84,6 +84,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
public static final String SERVICENAME = "plugin.search.serviceName";
public static final String SERVICEENABLED = "plugin.search.serviceEnabled";
public static final String EXCLUDEDFIELDS = "plugin.search.excludedFields";
public static final String GROUPONLY = "plugin.search.groupOnly";
private UserManager userManager;
private ComponentManager componentManager;
......@@ -91,7 +92,9 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
private String serviceName;
private boolean serviceEnabled;
private Collection<String> exculudedFields;
private Collection<String> excludedFields;
private boolean groupOnly;
private static String serverName;
......@@ -116,7 +119,8 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
public SearchPlugin() {
serviceName = JiveGlobals.getProperty(SERVICENAME, "search");
serviceEnabled = JiveGlobals.getBooleanProperty(SERVICEENABLED, true);
exculudedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, ""));
excludedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, ""));
groupOnly = JiveGlobals.getBooleanProperty(GROUPONLY);
serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
userManager = UserManager.getInstance();
......@@ -153,8 +157,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/*
* (non-Javadoc)
*
* @see org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware.openfire.container.PluginManager,
* java.io.File)
* @see org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware.openfire.container.PluginManager, java.io.File)
*/
public void initializePlugin(PluginManager manager, File pluginDirectory) {
pluginManager = manager;
......@@ -162,8 +165,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
componentManager = ComponentManagerFactory.getComponentManager();
try {
componentManager.addComponent(serviceName, this);
}
catch (ComponentException e) {
} catch (ComponentException e) {
Log.error(e.getMessage(), e);
}
PropertyEventDispatcher.addListener(this);
......@@ -172,8 +174,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/*
* (non-Javadoc)
*
* @see org.xmpp.component.Component#initialize(org.xmpp.packet.JID,
* org.xmpp.component.ComponentManager)
* @see org.xmpp.component.Component#initialize(org.xmpp.packet.JID, org.xmpp.component.ComponentManager)
*/
public void initialize(JID jid, ComponentManager componentManager) {
}
......@@ -197,15 +198,14 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
try {
componentManager.removeComponent(serviceName);
componentManager = null;
}
catch (Exception e) {
} catch (Exception e) {
if (componentManager != null) {
Log.error(e.getMessage(), e);
}
}
serviceName = null;
userManager = null;
exculudedFields = null;
excludedFields = null;
serverName = null;
fieldLookup = null;
reverseFieldLookup = null;
......@@ -230,8 +230,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
final IQ packet = (IQ) p;
if (packet.getType().equals(IQ.Type.error)
|| packet.getType().equals(IQ.Type.result)) {
if (packet.getType().equals(IQ.Type.error) || packet.getType().equals(IQ.Type.result)) {
return;
}
......@@ -248,12 +247,9 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
/**
* Handles IQ requests. This method throws an IllegalArgumentException if an
* IQ stanza is supplied that is not a request (if the stanza is not of type
* 'get' or 'set'). This method will either throw an Exception, or return a
* non-null IQ stanza of type 'error' or 'result', as XMPP Core specifies
* that <strong>all</strong> IQ request stanza's (type 'get' or 'set') MUST
* be replied to.
* Handles IQ requests. This method throws an IllegalArgumentException if an IQ stanza is supplied that is not a request (if the stanza
* is not of type 'get' or 'set'). This method will either throw an Exception, or return a non-null IQ stanza of type 'error' or
* 'result', as XMPP Core specifies that <strong>all</strong> IQ request stanza's (type 'get' or 'set') MUST be replied to.
*
* @param iq
* The IQ stanza that forms the request.
......@@ -268,17 +264,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
final IQ.Type type = iq.getType();
if (type != IQ.Type.get && type != IQ.Type.set) {
throw new IllegalArgumentException(
"Argument 'iq' must be of type 'get' or 'set'");
throw new IllegalArgumentException("Argument 'iq' must be of type 'get' or 'set'");
}
final Element childElement = iq.getChildElement();
if (childElement == null) {
replyPacket = IQ.createResultIQ(iq);
replyPacket
.setError(new PacketError(
Condition.bad_request,
org.xmpp.packet.PacketError.Type.modify,
replyPacket.setError(new PacketError(Condition.bad_request, org.xmpp.packet.PacketError.Type.modify,
"IQ stanzas of type 'get' and 'set' MUST contain one and only one child element (RFC 3920 section 9.2.3)."));
return replyPacket;
}
......@@ -296,8 +288,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
replyPacket = handleDiscoInfo(iq);
} else if (namespace.equals(IQDiscoItemsHandler.NAMESPACE_DISCO_ITEMS)) {
replyPacket = IQ.createResultIQ(iq);
replyPacket.setChildElement("query",
IQDiscoItemsHandler.NAMESPACE_DISCO_ITEMS);
replyPacket.setChildElement("query", IQDiscoItemsHandler.NAMESPACE_DISCO_ITEMS);
} else {
// don't known what to do with this.
replyPacket = IQ.createResultIQ(iq);
......@@ -319,26 +310,18 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
throw new IllegalArgumentException("Argument 'iq' cannot be null.");
}
if (!iq.getChildElement().getNamespaceURI().equals(
IQDiscoInfoHandler.NAMESPACE_DISCO_INFO)
|| iq.getType() != Type.get) {
throw new IllegalArgumentException(
"This is not a valid disco#info request.");
if (!iq.getChildElement().getNamespaceURI().equals(IQDiscoInfoHandler.NAMESPACE_DISCO_INFO) || iq.getType() != Type.get) {
throw new IllegalArgumentException("This is not a valid disco#info request.");
}
final IQ replyPacket = IQ.createResultIQ(iq);
final Element responseElement = replyPacket.setChildElement("query",
IQDiscoInfoHandler.NAMESPACE_DISCO_INFO);
responseElement.addElement("identity").addAttribute("category",
"directory").addAttribute("type", "user").addAttribute("name",
"User Search");
responseElement.addElement("feature").addAttribute("var",
NAMESPACE_JABBER_IQ_SEARCH);
responseElement.addElement("feature").addAttribute("var",
IQDiscoInfoHandler.NAMESPACE_DISCO_INFO);
responseElement.addElement("feature").addAttribute("var",
ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT);
final Element responseElement = replyPacket.setChildElement("query", IQDiscoInfoHandler.NAMESPACE_DISCO_INFO);
responseElement.addElement("identity").addAttribute("category", "directory").addAttribute("type", "user")
.addAttribute("name", "User Search");
responseElement.addElement("feature").addAttribute("var", NAMESPACE_JABBER_IQ_SEARCH);
responseElement.addElement("feature").addAttribute("var", IQDiscoInfoHandler.NAMESPACE_DISCO_INFO);
responseElement.addElement("feature").addAttribute("var", ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT);
return replyPacket;
}
......@@ -362,32 +345,26 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
/**
* Constructs a IQ result stanza, based on the request stanza that is
* provided as an argument. The stanza tells the recipient that this service
* is currently unavailable.
* Constructs a IQ result stanza, based on the request stanza that is provided as an argument. The stanza tells the recipient that this
* service is currently unavailable.
*
* @param packet
* The request IQ stanza to which a result will be returned.
* @return A result stanza, telling the user that this service is
* unavailable.
* @return A result stanza, telling the user that this service is unavailable.
*/
private static IQ replyDisabled(IQ packet) {
IQ replyPacket = IQ.createResultIQ(packet);
Element reply = replyPacket.setChildElement("query",
NAMESPACE_JABBER_IQ_SEARCH);
Element reply = replyPacket.setChildElement("query", NAMESPACE_JABBER_IQ_SEARCH);
final DataForm unavailableForm = new DataForm(DataForm.Type.cancel);
unavailableForm.setTitle(LocaleUtils.getLocalizedString(
"advance.user.search.title", "search"));
unavailableForm.addInstruction(LocaleUtils.getLocalizedString(
"search.service_unavailable", "search"));
unavailableForm.setTitle(LocaleUtils.getLocalizedString("advance.user.search.title", "search"));
unavailableForm.addInstruction(LocaleUtils.getLocalizedString("search.service_unavailable", "search"));
reply.add(unavailableForm.getElement());
return replyPacket;
}
/**
* Processes an IQ stanza of type 'get', which in the context of 'Jabber
* Search' is a request for available search fields.
* Processes an IQ stanza of type 'get', which in the context of 'Jabber Search' is a request for available search fields.
*
* @param packet
* An IQ stanza of type 'get'
......@@ -395,16 +372,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
*/
private IQ processGetPacket(IQ packet) {
if (!packet.getType().equals(IQ.Type.get)) {
throw new IllegalArgumentException(
"This method only accepts 'get' typed IQ stanzas as an argument.");
throw new IllegalArgumentException("This method only accepts 'get' typed IQ stanzas as an argument.");
}
IQ replyPacket = IQ.createResultIQ(packet);
Element queryResult = DocumentHelper.createElement(QName.get("query",
NAMESPACE_JABBER_IQ_SEARCH));
Element queryResult = DocumentHelper.createElement(QName.get("query", NAMESPACE_JABBER_IQ_SEARCH));
String instructions = LocaleUtils.getLocalizedString(
"advance.user.search.details", "search");
String instructions = LocaleUtils.getLocalizedString("advance.user.search.details", "search");
// non-data form
queryResult.addElement("instructions").addText(instructions);
......@@ -414,26 +388,20 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
queryResult.addElement("email");
DataForm searchForm = new DataForm(DataForm.Type.form);
searchForm.setTitle(LocaleUtils.getLocalizedString(
"advance.user.search.title", "search"));
searchForm.setTitle(LocaleUtils.getLocalizedString("advance.user.search.title", "search"));
searchForm.addInstruction(instructions);
searchForm.addField("FORM_TYPE", null, FormField.Type.hidden)
.addValue(NAMESPACE_JABBER_IQ_SEARCH);
searchForm.addField("FORM_TYPE", null, FormField.Type.hidden).addValue(NAMESPACE_JABBER_IQ_SEARCH);
searchForm.addField("search",
LocaleUtils.getLocalizedString("advance.user.search.search", "search"),
FormField.Type.text_single)
searchForm.addField("search", LocaleUtils.getLocalizedString("advance.user.search.search", "search"), FormField.Type.text_single)
.setRequired(true);
for (String searchField : getFilteredSearchFields()) {
final FormField field = searchForm.addField();
field.setVariable(searchField);
field.setType(FormField.Type.boolean_type);
field.addValue("1");
field.setLabel(LocaleUtils.getLocalizedString(
"advance.user.search." + searchField.toLowerCase(), "search"));
field.setLabel(LocaleUtils.getLocalizedString("advance.user.search." + searchField.toLowerCase(), "search"));
field.setRequired(false);
}
......@@ -444,8 +412,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
/**
* Processes an IQ stanza of type 'set', which in the context of 'Jabber
* Search' is a search request.
* Processes an IQ stanza of type 'set', which in the context of 'Jabber Search' is a search request.
*
* @param packet
* An IQ stanza of type 'get'
......@@ -453,10 +420,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
*/
private IQ processSetPacket(IQ packet) {
if (!packet.getType().equals(IQ.Type.set)) {
throw new IllegalArgumentException(
"This method only accepts 'set' typed IQ stanzas as an argument.");
throw new IllegalArgumentException("This method only accepts 'set' typed IQ stanzas as an argument.");
}
JID fromJID = packet.getFrom();
final IQ resultIQ;
// check if the request complies to the XEP-0055 standards
......@@ -469,25 +437,37 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
final Element incomingForm = packet.getChildElement();
final boolean isDataFormQuery = (incomingForm.element(QName.get("x", "jabber:x:data")) != null);
final Element rsmElement = incomingForm.element(QName.get("set",
ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT));
final Element rsmElement = incomingForm.element(QName.get("set", ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT));
if (rsmElement != null) {
final Element maxElement = rsmElement.element("max");
final Element startIndexElement = rsmElement.element("index");
int startIndex = 0;
if(startIndexElement != null) {
if (startIndexElement != null) {
startIndex = Integer.parseInt(startIndexElement.getTextTrim());
}
int max = -1;
if(maxElement != null) {
if (maxElement != null) {
max = Integer.parseInt(maxElement.getTextTrim());
}
final Set<User> searchResults = performSearch(incomingForm, startIndex, max);
if (groupOnly) {
Collection<Group> groups = GroupManager.getInstance().getGroups(fromJID);
Set<User> allSearchResults = new HashSet<User>(searchResults);
searchResults.clear();
for (User user : allSearchResults) {
for (Group group : groups) {
if (group.isUser(user.getUID())) {
searchResults.add(user);
}
}
}
}
// apply RSM
final List<User> rsmResults;
final ResultSet<User> rs = new ResultSetImpl<User>(searchResults);
......@@ -510,6 +490,20 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} else {
final Set<User> searchResults = performSearch(incomingForm);
if (groupOnly) {
Collection<Group> groups = GroupManager.getInstance().getGroups(fromJID);
Set<User> allSearchResults = new HashSet<User>(searchResults);
searchResults.clear();
for (User user : allSearchResults) {
for (Group group : groups) {
if (group.isUser(user.getUID())) {
searchResults.add(user);
}
}
}
}
// don't apply RSM
if (isDataFormQuery) {
resultIQ = replyDataFormResult(searchResults, packet);
......@@ -521,21 +515,34 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
return resultIQ;
}
public Set<User> filterGroupSearchResults(JID jid, Set<User> searchResults) {
if (groupOnly) {
Collection<Group> groups = GroupManager.getInstance().getGroups(jid);
Set<User> allSearchResults = new HashSet<User>(searchResults);
searchResults.clear();
for (User user : allSearchResults) {
for (Group group : groups) {
if (group.isUser(user.getUID())) {
searchResults.add(user);
}
}
}
}
return searchResults;
}
/**
* This method checks if the search request that was received is a valid
* JABBER:IQ:SEARCH request. In other words, it checks if the search request
* is spec compliant (XEP-0055). It does this by checking:
* This method checks if the search request that was received is a valid JABBER:IQ:SEARCH request. In other words, it checks if the
* search request is spec compliant (XEP-0055). It does this by checking:
* <ul>
* <li>if the IQ stanza is of type 'set';</li>
* <li>if a child element identified by the jabber:iq:search namespace is
* supplied;</li>
* <li>if a child element identified by the jabber:iq:search namespace is supplied;</li>
* <li>if the stanza child element is has valid children itself.</li>
* </ul>
*
* @param iq
* The IQ object that should include a jabber:iq:search request.
* @return ''true'' if the supplied IQ stanza is a spec compliant search
* request, ''false'' otherwise.
* @return ''true'' if the supplied IQ stanza is a spec compliant search request, ''false'' otherwise.
*/
@SuppressWarnings("unchecked")
public static boolean isValidSearchRequest(IQ iq) {
......@@ -598,7 +605,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
Collection<User> foundUsers = new ArrayList<User>();
if (userManager != null && query.length() > 0 && !query.equals(NAMESPACE_JABBER_IQ_SEARCH)) {
if(max >= 0) {
if (max >= 0) {
foundUsers.addAll(userManager.findUsers(new HashSet<String>(Arrays.asList(field)), query, startIndex, max));
} else {
foundUsers.addAll(userManager.findUsers(new HashSet<String>(Arrays.asList(field)), query));
......@@ -627,17 +634,12 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
/**
* This utilty method extracts the search query from the request. A query is
* 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
* for that field.
*
* 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
* other search method makes use of extended data forms. Search queries that
* are supplied to this {@link #extractSearchQuery(Element)} that make use
* of this last method get forwarded to
* {@link #extractExtendedSearchQuery(Element)}.
* This utilty method extracts the search query from the request. A query is 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 for that field.
*
* 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 other search method makes use of extended data forms. Search queries that are supplied to this
* {@link #extractSearchQuery(Element)} that make use of this last method get forwarded to {@link #extractExtendedSearchQuery(Element)}.
*
* @param incomingForm
* The form from which to extract the query
......@@ -672,9 +674,8 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
/**
* Extracts a search query from a data form that makes use of data forms to
* specify the search request. This 'extended' way of constructing a search
* request is documented in XEP-0055, chapter 3.
* Extracts a search query from a data form that makes use of data forms to specify the search request. This 'extended' way of
* constructing a search request is documented in XEP-0055, chapter 3.
*
* @param incomingForm
* The form from which to extract the query
......@@ -682,10 +683,8 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* @see #extractSearchQuery(Element)
*/
@SuppressWarnings("unchecked")
private Hashtable<String, String> extractExtendedSearchQuery(
Element incomingForm) {
final Element dataform = incomingForm.element(QName.get("x",
"jabber:x:data"));
private Hashtable<String, String> extractExtendedSearchQuery(Element incomingForm) {
final Element dataform = incomingForm.element(QName.get("x", "jabber:x:data"));
Hashtable<String, String> searchList = new Hashtable<String, String>();
List<String> searchFields = new ArrayList<String>();
......@@ -717,8 +716,10 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/**
* 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 packet the IQ packet sent by the client
* @param users
* set of users that will be used to construct the search results
* @param packet
* the IQ packet sent by the client
* @return the iq packet that contains the search results
*/
private IQ replyDataFormResult(Collection<User> users, IQ packet) {
......@@ -730,19 +731,16 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
for (final String fieldName : getFilteredSearchFields()) {
searchResults.addReportedField(fieldName,
LocaleUtils.getLocalizedString("advance.user.search." + fieldName.toLowerCase(), "search"),
FormField.Type.text_single);
LocaleUtils.getLocalizedString("advance.user.search." + fieldName.toLowerCase(), "search"), FormField.Type.text_single);
}
for (final User user : users) {
final String username = JID.unescapeNode(user.getUsername());
final Map<String, Object> item = new HashMap<String, Object>();
item.put("jid",
username + "@" + serverName);
item.put("jid", username + "@" + serverName);
item.put(LocaleUtils.getLocalizedString("advance.user.search.username", "search"),
username);
item.put(LocaleUtils.getLocalizedString("advance.user.search.username", "search"), username);
item.put(LocaleUtils.getLocalizedString("advance.user.search.name", "search"),
(user.isNameVisible() ? removeNull(user.getName()) : ""));
......@@ -754,8 +752,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
IQ replyPacket = IQ.createResultIQ(packet);
Element reply = replyPacket.setChildElement("query",
NAMESPACE_JABBER_IQ_SEARCH);
Element reply = replyPacket.setChildElement("query", NAMESPACE_JABBER_IQ_SEARCH);
reply.add(searchResults.getElement());
return replyPacket;
......@@ -764,14 +761,15 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/**
* 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 packet the IQ packet sent by the client
* @param users
* set of users that will be used to construct the search results
* @param packet
* the IQ packet sent by the client
* @return the iq packet that contains the search results
*/
private IQ replyNonDataFormResult(Collection<User> users, IQ packet) {
IQ replyPacket = IQ.createResultIQ(packet);
Element replyQuery = replyPacket.setChildElement("query",
NAMESPACE_JABBER_IQ_SEARCH);
Element replyQuery = replyPacket.setChildElement("query", NAMESPACE_JABBER_IQ_SEARCH);
for (User user : users) {
Element item = replyQuery.addElement("item");
......@@ -781,20 +779,17 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
// return to the client the same fields that were submitted
for (String field : reverseFieldLookup.keySet()) {
if ("Username".equals(field)) {
Element element = item.addElement(reverseFieldLookup
.get(field));
Element element = item.addElement(reverseFieldLookup.get(field));
element.addText(username);
}
if ("Name".equals(field)) {
Element element = item.addElement(reverseFieldLookup
.get(field));
Element element = item.addElement(reverseFieldLookup.get(field));
element.addText(user.isNameVisible() ? removeNull(user.getName()) : "");
}
if ("Email".equals(field)) {
Element element = item.addElement(reverseFieldLookup
.get(field));
Element element = item.addElement(reverseFieldLookup.get(field));
element.addText(user.isEmailVisible() ? removeNull(user.getEmail()) : "");
}
}
......@@ -813,11 +808,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
/**
* 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
* and then add itself back using the new 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 and then add itself back using the new name.
*
* @param name the service name of this component.
* @param name
* the service name of this component.
*/
public void setServiceName(String name) {
changeServiceName(name);
......@@ -834,11 +829,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
/**
* 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
* unavailable.
* 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 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) {
serviceEnabled = enabled;
......@@ -846,8 +841,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
/**
* Returns the collection of searchable field names that does not include the fields
* listed in the EXCLUDEDFIELDS property list.
* Returns the collection of searchable field names that does not include the fields listed in the EXCLUDEDFIELDS property list.
*
* @return collection of searchable field names.
*/
......@@ -858,70 +852,86 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
// by providing our own searching.
try {
searchFields = new ArrayList<String>(userManager.getSearchFields());
}
catch (UnsupportedOperationException uoe) {
} catch (UnsupportedOperationException uoe) {
// Use a SearchPluginUserManager instead.
searchFields = getSearchPluginUserManagerSearchFields();
}
searchFields.removeAll(exculudedFields);
searchFields.removeAll(excludedFields);
return searchFields;
}
/**
* 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
* the search results.
* 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 the search results.
*
* @param excludedFields
* fields that can not be searched on or shown to the client
*/
public void setExcludedFields(Collection<String> excludedFields) {
this.excludedFields = excludedFields;
JiveGlobals.setProperty(EXCLUDEDFIELDS, StringUtils.collectionToString(excludedFields));
}
/**
* Checks if the search service is restricted to groups.
*
* @return true if restricted to groups.
*/
public boolean isGroupOnly() {
return groupOnly;
}
/**
* Sets the search service scope.
*
* @param exculudedFields fields that can not be searched on or shown to the client
* @param groupOnly
* true if group only.
*/
public void setExcludedFields(Collection<String> exculudedFields) {
this.exculudedFields = exculudedFields;
JiveGlobals.setProperty(EXCLUDEDFIELDS, StringUtils.collectionToString(exculudedFields));
public void setGroupOnly(boolean groupOnly) {
this.groupOnly = groupOnly;
JiveGlobals.setProperty(GROUPONLY, groupOnly ? "true" : "false");
}
/*
* (non-Javadoc)
*
* @see org.jivesoftware.util.PropertyEventListener#propertySet(java.lang.String,
* java.util.Map)
* @see org.jivesoftware.util.PropertyEventListener#propertySet(java.lang.String, java.util.Map)
*/
public void propertySet(String property, Map<String, Object> params) {
if (property.equals(SERVICEENABLED)) {
this.serviceEnabled = Boolean.parseBoolean((String)params.get("value"));
}
else if (property.equals(SERVICENAME)) {
changeServiceName((String)params.get("value"));
}
else if (property.equals(EXCLUDEDFIELDS)) {
exculudedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, (String)params.get("value")));
this.serviceEnabled = Boolean.parseBoolean((String) params.get("value"));
} else if (property.equals(SERVICENAME)) {
changeServiceName((String) params.get("value"));
} else if (property.equals(EXCLUDEDFIELDS)) {
excludedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, (String) params.get("value")));
} else if (property.equals(GROUPONLY)) {
this.groupOnly = Boolean.parseBoolean((String) params.get("value"));
}
}
/*
* (non-Javadoc)
*
* @see org.jivesoftware.util.PropertyEventListener#propertyDeleted(java.lang.String,
* java.util.Map)
* @see org.jivesoftware.util.PropertyEventListener#propertyDeleted(java.lang.String, java.util.Map)
*/
public void propertyDeleted(String property, Map<String, Object> params) {
if (property.equals(SERVICEENABLED)) {
this.serviceEnabled = true;
}
else if (property.equals(SERVICENAME)) {
} else if (property.equals(SERVICENAME)) {
changeServiceName("search");
}
else if (property.equals(EXCLUDEDFIELDS)) {
exculudedFields = new ArrayList<String>();
} else if (property.equals(EXCLUDEDFIELDS)) {
excludedFields = new ArrayList<String>();
} else if (property.equals(GROUPONLY)) {
this.groupOnly = false;
}
}
/*
* (non-Javadoc)
*
* @see org.jivesoftware.util.PropertyEventListener#xmlPropertySet(java.lang.String,
* java.util.Map)
* @see org.jivesoftware.util.PropertyEventListener#xmlPropertySet(java.lang.String, java.util.Map)
*/
public void xmlPropertySet(String property, Map<String, Object> params) {
// not used
......@@ -930,8 +940,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/*
* (non-Javadoc)
*
* @see org.jivesoftware.util.PropertyEventListener#xmlPropertyDeleted(java.lang.String,
* java.util.Map)
* @see org.jivesoftware.util.PropertyEventListener#xmlPropertyDeleted(java.lang.String, java.util.Map)
*/
public void xmlPropertyDeleted(String property, Map<String, Object> params) {
// not used
......@@ -949,15 +958,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
// Re-register the service.
try {
componentManager.removeComponent(this.serviceName);
}
catch (Exception e) {
} catch (Exception e) {
Log.error(e.getMessage(), e);
}
try {
componentManager.addComponent(serviceName, this);
}
catch (Exception e) {
} catch (Exception e) {
Log.error(e.getMessage(), e);
}
......@@ -974,13 +981,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
/**
* Returns the trimmed argument, or an empty String object of null was
* supplied as an argument.
* Returns the trimmed argument, or an empty String object of null was supplied as an argument.
*
* @param s
* The String to be trimmed.
* @return String object that does not start or end with whitespace
* characters.
* @return String object that does not start or end with whitespace characters.
*/
private String removeNull(String s) {
if (s == null) {
......@@ -991,9 +996,8 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
}
/**
* Returns the collection of field names that can be used to search for a
* user. Typical fields are username, name, and email. These values can be
* used to contruct a data form.
* Returns the collection of field names that can be used to search for a user. Typical fields are username, name, and email. These
* values can be used to contruct a data form.
*
* @return the collection of field names that can be used to search.
*/
......
......@@ -13,6 +13,7 @@
boolean success = request.getParameter("success") != null;
String searchName = ParamUtils.getParameter(request, "searchname");
boolean searchEnabled = ParamUtils.getBooleanParameter(request, "searchEnabled");
boolean groupOnly = ParamUtils.getBooleanParameter(request, "groupOnly");
SearchPlugin plugin = (SearchPlugin) XMPPServer.getInstance().getPluginManager().getPlugin("search");
......@@ -34,7 +35,7 @@
}
}
plugin.setExcludedFields(excludedFields);
plugin.setGroupOnly(groupOnly);
response.sendRedirect("search-props-edit-form.jsp?success=true");
return;
}
......@@ -50,6 +51,7 @@
searchEnabled = plugin.getServiceEnabled();
Collection<String> searchableFields = plugin.getFilteredSearchFields();
groupOnly = plugin.isGroupOnly();
%>
<html>
......@@ -169,6 +171,38 @@
<br>
<div class="jive-contentBoxHeader"><fmt:message key="search.props.edit.form.search_scope" /></div>
<div class="jive-contentBox">
<p>
<fmt:message key="search.props.edit.form.search_scope_directions" />
</p>
<table cellpadding="3" cellspacing="0" border="0" width="100%">
<tbody>
<tr>
<td width="1%">
<input type="radio" name="groupOnly" value="false" id="rb-grouponly-01"
<%= ((!groupOnly) ? "checked" : "") %>>
</td>
<td width="99%">
<label for="rb-grouponly-01"><b><fmt:message key="search.props.edit.form.search_scope_anyone" /></b></label> - <fmt:message key="search.props.edit.form.search_scope_anyone_details" />
</td>
</tr>
<tr>
<td width="1%">
<input type="radio" name="groupOnly" value="true" id="rb-grouponly-02"
<%= ((groupOnly) ? "checked" : "") %>>
</td>
<td width="99%">
<label for="rb-grouponly-02"><b><fmt:message key="search.props.edit.form.search_scope_groups" /></b></label> - <fmt:message key="search.props.edit.form.search_scope_groups_details" />
</td>
</tr>
</tbody>
</table>
</div>
<br>
<input type="submit" value="<fmt:message key="search.props.edit.form.save_properties" />">
</form>
......
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