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 @@ ...@@ -44,6 +44,11 @@
Search Plugin Changelog Search Plugin Changelog
</h1> </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> <p><b>1.5.1</b> -- January 2, 2010</p>
<ul> <ul>
<li>[<a href='http://www.igniterealtime.org/issues/browse/OF-45'>OF-45</a>] - Added Lithuanian translation by Rytis Umbrasas.</li> <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 @@ ...@@ -5,8 +5,8 @@
<name>Search</name> <name>Search</name>
<description>Provides support for Jabber Search (XEP-0055)</description> <description>Provides support for Jabber Search (XEP-0055)</description>
<author>Ryan Graham</author> <author>Ryan Graham</author>
<version>1.5.1</version> <version>1.5.2</version>
<date>1/2/2010</date> <date>7/4/2013</date>
<minServerVersion>3.7.0</minServerVersion> <minServerVersion>3.7.0</minServerVersion>
<adminconsole> <adminconsole>
......
...@@ -68,4 +68,11 @@ search.props.edit.form.searchable_fields_details=Use the form below to enable wh ...@@ -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.fields=Fields
search.props.edit.form.save_properties=Save Properties 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. search.service_unavailable=This service is unavailable.
...@@ -69,5 +69,12 @@ search.props.edit.form.searchable_fields_details=\u017demiau esan\u010dia forma ...@@ -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.fields=Laukai
search.props.edit.form.save_properties=I\u0161saugoti nuostatas 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. search.service_unavailable=Paslauga negalima.
...@@ -48,4 +48,11 @@ search.props.edit.form.searchable_fields_details=Utilize o formul\u00e1rio abaix ...@@ -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.fields=Campos
search.props.edit.form.save_properties=Gravar Op\u00e7\u00f5es 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. search.service_unavailable=Este servi\u00e7o est\u00e1 indispon\u00edvel.
...@@ -27,9 +27,9 @@ import java.util.Hashtable; ...@@ -27,9 +27,9 @@ import java.util.Hashtable;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.Map.Entry;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.Element; import org.dom4j.Element;
...@@ -39,6 +39,8 @@ import org.jivesoftware.openfire.container.Plugin; ...@@ -39,6 +39,8 @@ import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager; import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.disco.IQDiscoInfoHandler; import org.jivesoftware.openfire.disco.IQDiscoInfoHandler;
import org.jivesoftware.openfire.disco.IQDiscoItemsHandler; 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.User;
import org.jivesoftware.openfire.user.UserManager; import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
...@@ -55,23 +57,21 @@ import org.xmpp.component.ComponentManagerFactory; ...@@ -55,23 +57,21 @@ import org.xmpp.component.ComponentManagerFactory;
import org.xmpp.forms.DataForm; import org.xmpp.forms.DataForm;
import org.xmpp.forms.FormField; import org.xmpp.forms.FormField;
import org.xmpp.packet.IQ; import org.xmpp.packet.IQ;
import org.xmpp.packet.IQ.Type;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.xmpp.packet.Packet; import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError; import org.xmpp.packet.PacketError;
import org.xmpp.packet.IQ.Type;
import org.xmpp.packet.PacketError.Condition; import org.xmpp.packet.PacketError.Condition;
import org.xmpp.resultsetmanagement.ResultSet; import org.xmpp.resultsetmanagement.ResultSet;
import org.xmpp.resultsetmanagement.ResultSetImpl; import org.xmpp.resultsetmanagement.ResultSetImpl;
/** /**
* Provides support for Jabber Search * Provides support for Jabber Search (<a href="http://www.xmpp.org/extensions/xep-0055.html">XEP-0055</a>).
* (<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, and to
* regarding the possible search fields, to send a search query, * receive search results. This implementation was primarily designed to use <a href="http://www.xmpp.org/extensions/xep-0004.html">Data
* and to receive search results. This implementation was primarily designed to use * Forms</a>, but also supports non-dataform searches.
* <a href="http://www.xmpp.org/extensions/xep-0004.html">Data Forms</a>, but
* 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>
...@@ -84,6 +84,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -84,6 +84,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
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";
public static final String GROUPONLY = "plugin.search.groupOnly";
private UserManager userManager; private UserManager userManager;
private ComponentManager componentManager; private ComponentManager componentManager;
...@@ -91,7 +92,9 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -91,7 +92,9 @@ 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> excludedFields;
private boolean groupOnly;
private static String serverName; private static String serverName;
...@@ -116,7 +119,8 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -116,7 +119,8 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
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, "")); excludedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, ""));
groupOnly = JiveGlobals.getBooleanProperty(GROUPONLY);
serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain(); serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
userManager = UserManager.getInstance(); userManager = UserManager.getInstance();
...@@ -153,8 +157,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -153,8 +157,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/* /*
* (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;
...@@ -162,8 +165,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -162,8 +165,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
componentManager = ComponentManagerFactory.getComponentManager(); componentManager = ComponentManagerFactory.getComponentManager();
try { try {
componentManager.addComponent(serviceName, this); componentManager.addComponent(serviceName, this);
} } catch (ComponentException e) {
catch (ComponentException e) {
Log.error(e.getMessage(), e); Log.error(e.getMessage(), e);
} }
PropertyEventDispatcher.addListener(this); PropertyEventDispatcher.addListener(this);
...@@ -172,8 +174,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -172,8 +174,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/* /*
* (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) {
} }
...@@ -197,15 +198,14 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -197,15 +198,14 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
try { try {
componentManager.removeComponent(serviceName); componentManager.removeComponent(serviceName);
componentManager = null; componentManager = null;
} } catch (Exception e) {
catch (Exception e) {
if (componentManager != null) { if (componentManager != null) {
Log.error(e.getMessage(), e); Log.error(e.getMessage(), e);
} }
} }
serviceName = null; serviceName = null;
userManager = null; userManager = null;
exculudedFields = null; excludedFields = null;
serverName = null; serverName = null;
fieldLookup = null; fieldLookup = null;
reverseFieldLookup = null; reverseFieldLookup = null;
...@@ -230,8 +230,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -230,8 +230,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} }
final IQ packet = (IQ) p; final IQ packet = (IQ) p;
if (packet.getType().equals(IQ.Type.error) if (packet.getType().equals(IQ.Type.error) || packet.getType().equals(IQ.Type.result)) {
|| packet.getType().equals(IQ.Type.result)) {
return; return;
} }
...@@ -248,12 +247,9 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -248,12 +247,9 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} }
/** /**
* Handles IQ requests. This method throws an IllegalArgumentException if an * Handles IQ requests. This method throws an IllegalArgumentException if an IQ stanza is supplied that is not a request (if the stanza
* IQ stanza is supplied that is not a request (if the stanza is not of type * 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
* 'get' or 'set'). This method will either throw an Exception, or return a * 'result', as XMPP Core specifies that <strong>all</strong> IQ request stanza's (type 'get' or 'set') MUST be replied to.
* 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 * @param iq
* The IQ stanza that forms the request. * The IQ stanza that forms the request.
...@@ -268,17 +264,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -268,17 +264,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
final IQ.Type type = iq.getType(); final IQ.Type type = iq.getType();
if (type != IQ.Type.get && type != IQ.Type.set) { if (type != IQ.Type.get && type != IQ.Type.set) {
throw new IllegalArgumentException( throw new IllegalArgumentException("Argument 'iq' must be of type 'get' or 'set'");
"Argument 'iq' must be of type 'get' or 'set'");
} }
final Element childElement = iq.getChildElement(); final Element childElement = iq.getChildElement();
if (childElement == null) { if (childElement == null) {
replyPacket = IQ.createResultIQ(iq); replyPacket = IQ.createResultIQ(iq);
replyPacket replyPacket.setError(new PacketError(Condition.bad_request, org.xmpp.packet.PacketError.Type.modify,
.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).")); "IQ stanzas of type 'get' and 'set' MUST contain one and only one child element (RFC 3920 section 9.2.3)."));
return replyPacket; return replyPacket;
} }
...@@ -296,8 +288,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -296,8 +288,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
replyPacket = handleDiscoInfo(iq); replyPacket = handleDiscoInfo(iq);
} else if (namespace.equals(IQDiscoItemsHandler.NAMESPACE_DISCO_ITEMS)) { } else if (namespace.equals(IQDiscoItemsHandler.NAMESPACE_DISCO_ITEMS)) {
replyPacket = IQ.createResultIQ(iq); replyPacket = IQ.createResultIQ(iq);
replyPacket.setChildElement("query", replyPacket.setChildElement("query", IQDiscoItemsHandler.NAMESPACE_DISCO_ITEMS);
IQDiscoItemsHandler.NAMESPACE_DISCO_ITEMS);
} else { } else {
// don't known what to do with this. // don't known what to do with this.
replyPacket = IQ.createResultIQ(iq); replyPacket = IQ.createResultIQ(iq);
...@@ -319,26 +310,18 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -319,26 +310,18 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
throw new IllegalArgumentException("Argument 'iq' cannot be null."); throw new IllegalArgumentException("Argument 'iq' cannot be null.");
} }
if (!iq.getChildElement().getNamespaceURI().equals( if (!iq.getChildElement().getNamespaceURI().equals(IQDiscoInfoHandler.NAMESPACE_DISCO_INFO) || iq.getType() != Type.get) {
IQDiscoInfoHandler.NAMESPACE_DISCO_INFO) throw new IllegalArgumentException("This is not a valid disco#info request.");
|| iq.getType() != Type.get) {
throw new IllegalArgumentException(
"This is not a valid disco#info request.");
} }
final IQ replyPacket = IQ.createResultIQ(iq); final IQ replyPacket = IQ.createResultIQ(iq);
final Element responseElement = replyPacket.setChildElement("query", final Element responseElement = replyPacket.setChildElement("query", IQDiscoInfoHandler.NAMESPACE_DISCO_INFO);
IQDiscoInfoHandler.NAMESPACE_DISCO_INFO); responseElement.addElement("identity").addAttribute("category", "directory").addAttribute("type", "user")
responseElement.addElement("identity").addAttribute("category", .addAttribute("name", "User Search");
"directory").addAttribute("type", "user").addAttribute("name", responseElement.addElement("feature").addAttribute("var", NAMESPACE_JABBER_IQ_SEARCH);
"User Search"); responseElement.addElement("feature").addAttribute("var", IQDiscoInfoHandler.NAMESPACE_DISCO_INFO);
responseElement.addElement("feature").addAttribute("var", responseElement.addElement("feature").addAttribute("var", ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT);
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; return replyPacket;
} }
...@@ -362,32 +345,26 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -362,32 +345,26 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} }
/** /**
* Constructs a IQ result stanza, based on the request stanza that is * Constructs a IQ result stanza, based on the request stanza that is provided as an argument. The stanza tells the recipient that this
* provided as an argument. The stanza tells the recipient that this service * 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.
*/ */
private static IQ replyDisabled(IQ packet) { private static IQ replyDisabled(IQ packet) {
IQ replyPacket = IQ.createResultIQ(packet); IQ replyPacket = IQ.createResultIQ(packet);
Element reply = replyPacket.setChildElement("query", Element reply = replyPacket.setChildElement("query", NAMESPACE_JABBER_IQ_SEARCH);
NAMESPACE_JABBER_IQ_SEARCH);
final DataForm unavailableForm = new DataForm(DataForm.Type.cancel); final DataForm unavailableForm = new DataForm(DataForm.Type.cancel);
unavailableForm.setTitle(LocaleUtils.getLocalizedString( unavailableForm.setTitle(LocaleUtils.getLocalizedString("advance.user.search.title", "search"));
"advance.user.search.title", "search")); unavailableForm.addInstruction(LocaleUtils.getLocalizedString("search.service_unavailable", "search"));
unavailableForm.addInstruction(LocaleUtils.getLocalizedString(
"search.service_unavailable", "search"));
reply.add(unavailableForm.getElement()); reply.add(unavailableForm.getElement());
return replyPacket; return replyPacket;
} }
/** /**
* 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'
...@@ -395,16 +372,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -395,16 +372,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
*/ */
private IQ processGetPacket(IQ packet) { private IQ processGetPacket(IQ packet) {
if (!packet.getType().equals(IQ.Type.get)) { if (!packet.getType().equals(IQ.Type.get)) {
throw new IllegalArgumentException( throw new IllegalArgumentException("This method only accepts 'get' typed IQ stanzas as an argument.");
"This method only accepts 'get' typed IQ stanzas as an argument.");
} }
IQ replyPacket = IQ.createResultIQ(packet); IQ replyPacket = IQ.createResultIQ(packet);
Element queryResult = DocumentHelper.createElement(QName.get("query", Element queryResult = DocumentHelper.createElement(QName.get("query", NAMESPACE_JABBER_IQ_SEARCH));
NAMESPACE_JABBER_IQ_SEARCH));
String instructions = LocaleUtils.getLocalizedString( String instructions = LocaleUtils.getLocalizedString("advance.user.search.details", "search");
"advance.user.search.details", "search");
// non-data form // non-data form
queryResult.addElement("instructions").addText(instructions); queryResult.addElement("instructions").addText(instructions);
...@@ -414,26 +388,20 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -414,26 +388,20 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
queryResult.addElement("email"); queryResult.addElement("email");
DataForm searchForm = new DataForm(DataForm.Type.form); DataForm searchForm = new DataForm(DataForm.Type.form);
searchForm.setTitle(LocaleUtils.getLocalizedString( searchForm.setTitle(LocaleUtils.getLocalizedString("advance.user.search.title", "search"));
"advance.user.search.title", "search"));
searchForm.addInstruction(instructions); searchForm.addInstruction(instructions);
searchForm.addField("FORM_TYPE", null, FormField.Type.hidden) searchForm.addField("FORM_TYPE", null, FormField.Type.hidden).addValue(NAMESPACE_JABBER_IQ_SEARCH);
.addValue(NAMESPACE_JABBER_IQ_SEARCH);
searchForm.addField("search", searchForm.addField("search", LocaleUtils.getLocalizedString("advance.user.search.search", "search"), FormField.Type.text_single)
LocaleUtils.getLocalizedString("advance.user.search.search", "search"),
FormField.Type.text_single)
.setRequired(true); .setRequired(true);
for (String searchField : getFilteredSearchFields()) { for (String searchField : getFilteredSearchFields()) {
final FormField field = searchForm.addField(); final FormField field = searchForm.addField();
field.setVariable(searchField); field.setVariable(searchField);
field.setType(FormField.Type.boolean_type); field.setType(FormField.Type.boolean_type);
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);
} }
...@@ -444,8 +412,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -444,8 +412,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'
...@@ -453,10 +420,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -453,10 +420,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
*/ */
private IQ processSetPacket(IQ packet) { private IQ processSetPacket(IQ packet) {
if (!packet.getType().equals(IQ.Type.set)) { if (!packet.getType().equals(IQ.Type.set)) {
throw new IllegalArgumentException( throw new IllegalArgumentException("This method only accepts 'set' typed IQ stanzas as an argument.");
"This method only accepts 'set' typed IQ stanzas as an argument.");
} }
JID fromJID = packet.getFrom();
final IQ resultIQ; final IQ resultIQ;
// check if the request complies to the XEP-0055 standards // check if the request complies to the XEP-0055 standards
...@@ -469,25 +437,37 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -469,25 +437,37 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
final Element incomingForm = packet.getChildElement(); final Element incomingForm = packet.getChildElement();
final boolean isDataFormQuery = (incomingForm.element(QName.get("x", "jabber:x:data")) != null); final boolean isDataFormQuery = (incomingForm.element(QName.get("x", "jabber:x:data")) != null);
final Element rsmElement = incomingForm.element(QName.get("set", final Element rsmElement = incomingForm.element(QName.get("set", ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT));
ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT));
if (rsmElement != null) { if (rsmElement != null) {
final Element maxElement = rsmElement.element("max"); final Element maxElement = rsmElement.element("max");
final Element startIndexElement = rsmElement.element("index"); final Element startIndexElement = rsmElement.element("index");
int startIndex = 0; int startIndex = 0;
if(startIndexElement != null) { if (startIndexElement != null) {
startIndex = Integer.parseInt(startIndexElement.getTextTrim()); startIndex = Integer.parseInt(startIndexElement.getTextTrim());
} }
int max = -1; int max = -1;
if(maxElement != null) { if (maxElement != null) {
max = Integer.parseInt(maxElement.getTextTrim()); max = Integer.parseInt(maxElement.getTextTrim());
} }
final Set<User> searchResults = performSearch(incomingForm, startIndex, max); 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 // apply RSM
final List<User> rsmResults; final List<User> rsmResults;
final ResultSet<User> rs = new ResultSetImpl<User>(searchResults); final ResultSet<User> rs = new ResultSetImpl<User>(searchResults);
...@@ -510,6 +490,20 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -510,6 +490,20 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} else { } else {
final Set<User> searchResults = performSearch(incomingForm); 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 // don't apply RSM
if (isDataFormQuery) { if (isDataFormQuery) {
resultIQ = replyDataFormResult(searchResults, packet); resultIQ = replyDataFormResult(searchResults, packet);
...@@ -521,21 +515,34 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -521,21 +515,34 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
return resultIQ; 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 * This method checks if the search request that was received is a valid JABBER:IQ:SEARCH request. In other words, it checks if the
* JABBER:IQ:SEARCH request. In other words, it checks if the search request * search request is spec compliant (XEP-0055). It does this by checking:
* is spec compliant (XEP-0055). It does this by checking:
* <ul> * <ul>
* <li>if the IQ stanza is of type 'set';</li> * <li>if the IQ stanza is of type 'set';</li>
* <li>if a child element identified by the jabber:iq:search namespace is * <li>if a child element identified by the jabber:iq:search namespace is supplied;</li>
* supplied;</li>
* <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.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static boolean isValidSearchRequest(IQ iq) { public static boolean isValidSearchRequest(IQ iq) {
...@@ -598,7 +605,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -598,7 +605,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
Collection<User> foundUsers = new ArrayList<User>(); Collection<User> foundUsers = new ArrayList<User>();
if (userManager != null && query.length() > 0 && !query.equals(NAMESPACE_JABBER_IQ_SEARCH)) { 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)); foundUsers.addAll(userManager.findUsers(new HashSet<String>(Arrays.asList(field)), query, startIndex, max));
} else { } else {
foundUsers.addAll(userManager.findUsers(new HashSet<String>(Arrays.asList(field)), query)); foundUsers.addAll(userManager.findUsers(new HashSet<String>(Arrays.asList(field)), query));
...@@ -627,17 +634,12 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -627,17 +634,12 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} }
/** /**
* This utilty method extracts the search query from the request. A query is * 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
* defined as a set of key->value pairs, where the key denotes a search * a search field, and the value contains the value that was filled out by the user for that field.
* 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
* The query can be specified in one of two ways. The first way is a query * {@link #extractSearchQuery(Element)} that make use of this last method get forwarded to {@link #extractExtendedSearchQuery(Element)}.
* 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 * @param incomingForm
* The form from which to extract the query * The form from which to extract the query
...@@ -672,9 +674,8 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -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 * Extracts a search query from a data form that makes use of data forms to specify the search request. This 'extended' way of
* specify the search request. This 'extended' way of constructing a search * 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
...@@ -682,10 +683,8 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -682,10 +683,8 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
* @see #extractSearchQuery(Element) * @see #extractSearchQuery(Element)
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Hashtable<String, String> extractExtendedSearchQuery( private Hashtable<String, String> extractExtendedSearchQuery(Element incomingForm) {
Element incomingForm) { final Element dataform = incomingForm.element(QName.get("x", "jabber:x:data"));
final Element dataform = incomingForm.element(QName.get("x",
"jabber:x:data"));
Hashtable<String, String> searchList = new Hashtable<String, String>(); Hashtable<String, String> searchList = new Hashtable<String, String>();
List<String> searchFields = new ArrayList<String>(); List<String> searchFields = new ArrayList<String>();
...@@ -717,8 +716,10 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -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. * 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
* @param packet the IQ packet sent by the client * 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 * @return the iq packet that contains the search results
*/ */
private IQ replyDataFormResult(Collection<User> users, IQ packet) { private IQ replyDataFormResult(Collection<User> users, IQ packet) {
...@@ -730,19 +731,16 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -730,19 +731,16 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
for (final String fieldName : getFilteredSearchFields()) { for (final String fieldName : getFilteredSearchFields()) {
searchResults.addReportedField(fieldName, searchResults.addReportedField(fieldName,
LocaleUtils.getLocalizedString("advance.user.search." + fieldName.toLowerCase(), "search"), LocaleUtils.getLocalizedString("advance.user.search." + fieldName.toLowerCase(), "search"), FormField.Type.text_single);
FormField.Type.text_single);
} }
for (final User user : users) { for (final User user : users) {
final String username = JID.unescapeNode(user.getUsername()); final String username = JID.unescapeNode(user.getUsername());
final Map<String, Object> item = new HashMap<String, Object>(); final Map<String, Object> item = new HashMap<String, Object>();
item.put("jid", item.put("jid", username + "@" + serverName);
username + "@" + serverName);
item.put(LocaleUtils.getLocalizedString("advance.user.search.username", "search"), item.put(LocaleUtils.getLocalizedString("advance.user.search.username", "search"), username);
username);
item.put(LocaleUtils.getLocalizedString("advance.user.search.name", "search"), item.put(LocaleUtils.getLocalizedString("advance.user.search.name", "search"),
(user.isNameVisible() ? removeNull(user.getName()) : "")); (user.isNameVisible() ? removeNull(user.getName()) : ""));
...@@ -754,8 +752,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -754,8 +752,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} }
IQ replyPacket = IQ.createResultIQ(packet); IQ replyPacket = IQ.createResultIQ(packet);
Element reply = replyPacket.setChildElement("query", Element reply = replyPacket.setChildElement("query", NAMESPACE_JABBER_IQ_SEARCH);
NAMESPACE_JABBER_IQ_SEARCH);
reply.add(searchResults.getElement()); reply.add(searchResults.getElement());
return replyPacket; return replyPacket;
...@@ -764,14 +761,15 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -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. * 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
* @param packet the IQ packet sent by the client * 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 * @return the iq packet that contains the search results
*/ */
private IQ replyNonDataFormResult(Collection<User> users, IQ packet) { private IQ replyNonDataFormResult(Collection<User> users, IQ packet) {
IQ replyPacket = IQ.createResultIQ(packet); IQ replyPacket = IQ.createResultIQ(packet);
Element replyQuery = replyPacket.setChildElement("query", Element replyQuery = replyPacket.setChildElement("query", NAMESPACE_JABBER_IQ_SEARCH);
NAMESPACE_JABBER_IQ_SEARCH);
for (User user : users) { for (User user : users) {
Element item = replyQuery.addElement("item"); Element item = replyQuery.addElement("item");
...@@ -781,20 +779,17 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -781,20 +779,17 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
// return to the client the same fields that were submitted // return to the client the same fields that were submitted
for (String field : reverseFieldLookup.keySet()) { for (String field : reverseFieldLookup.keySet()) {
if ("Username".equals(field)) { if ("Username".equals(field)) {
Element element = item.addElement(reverseFieldLookup Element element = item.addElement(reverseFieldLookup.get(field));
.get(field));
element.addText(username); element.addText(username);
} }
if ("Name".equals(field)) { if ("Name".equals(field)) {
Element element = item.addElement(reverseFieldLookup Element element = item.addElement(reverseFieldLookup.get(field));
.get(field));
element.addText(user.isNameVisible() ? 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(user.isEmailVisible() ? removeNull(user.getEmail()) : ""); element.addText(user.isEmailVisible() ? removeNull(user.getEmail()) : "");
} }
} }
...@@ -813,11 +808,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -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 * Sets the service name of this component, which is "search" by default. If the name is different than the existing name the plugin
* is different than the existing name the plugin will remove itself from the ComponentManager * will remove itself from the ComponentManager and then add itself back using the new name.
* 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) { public void setServiceName(String name) {
changeServiceName(name); changeServiceName(name);
...@@ -834,11 +829,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -834,11 +829,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} }
/** /**
* 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
* to do a search they will receive an XForm informing that the service is * 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;
...@@ -846,8 +841,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -846,8 +841,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} }
/** /**
* 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.
*/ */
...@@ -858,70 +852,86 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -858,70 +852,86 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
// 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(excludedFields);
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
* in the case of preventing users email addresses from being revealed as part of * being revealed as part of the search results.
* 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) { public void setGroupOnly(boolean groupOnly) {
this.exculudedFields = exculudedFields; this.groupOnly = groupOnly;
JiveGlobals.setProperty(EXCLUDEDFIELDS, StringUtils.collectionToString(exculudedFields)); JiveGlobals.setProperty(GROUPONLY, groupOnly ? "true" : "false");
} }
/* /*
* (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)) {
} excludedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, (String) params.get("value")));
else if (property.equals(EXCLUDEDFIELDS)) { } else if (property.equals(GROUPONLY)) {
exculudedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, (String)params.get("value"))); this.groupOnly = Boolean.parseBoolean((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)) { excludedFields = new ArrayList<String>();
exculudedFields = new ArrayList<String>(); } else if (property.equals(GROUPONLY)) {
this.groupOnly = false;
} }
} }
/* /*
* (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
...@@ -930,8 +940,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -930,8 +940,7 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
/* /*
* (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
...@@ -949,15 +958,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -949,15 +958,13 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
// Re-register the service. // Re-register the service.
try { try {
componentManager.removeComponent(this.serviceName); componentManager.removeComponent(this.serviceName);
} } catch (Exception e) {
catch (Exception e) {
Log.error(e.getMessage(), e); Log.error(e.getMessage(), e);
} }
try { try {
componentManager.addComponent(serviceName, this); componentManager.addComponent(serviceName, this);
} } catch (Exception e) {
catch (Exception e) {
Log.error(e.getMessage(), e); Log.error(e.getMessage(), e);
} }
...@@ -974,13 +981,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -974,13 +981,11 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener {
} }
/** /**
* 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) {
...@@ -991,9 +996,8 @@ public class SearchPlugin implements Component, Plugin, PropertyEventListener { ...@@ -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 * Returns the collection of field names that can be used to search for a user. Typical fields are username, name, and email. These
* user. Typical fields are username, name, and email. These values can be * values can be used to contruct a data form.
* used to contruct a data form.
* *
* @return the collection of field names that can be used to search. * @return the collection of field names that can be used to search.
*/ */
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
boolean success = request.getParameter("success") != null; boolean success = request.getParameter("success") != null;
String searchName = ParamUtils.getParameter(request, "searchname"); String searchName = ParamUtils.getParameter(request, "searchname");
boolean searchEnabled = ParamUtils.getBooleanParameter(request, "searchEnabled"); boolean searchEnabled = ParamUtils.getBooleanParameter(request, "searchEnabled");
boolean groupOnly = ParamUtils.getBooleanParameter(request, "groupOnly");
SearchPlugin plugin = (SearchPlugin) XMPPServer.getInstance().getPluginManager().getPlugin("search"); SearchPlugin plugin = (SearchPlugin) XMPPServer.getInstance().getPluginManager().getPlugin("search");
...@@ -34,7 +35,7 @@ ...@@ -34,7 +35,7 @@
} }
} }
plugin.setExcludedFields(excludedFields); plugin.setExcludedFields(excludedFields);
plugin.setGroupOnly(groupOnly);
response.sendRedirect("search-props-edit-form.jsp?success=true"); response.sendRedirect("search-props-edit-form.jsp?success=true");
return; return;
} }
...@@ -50,6 +51,7 @@ ...@@ -50,6 +51,7 @@
searchEnabled = plugin.getServiceEnabled(); searchEnabled = plugin.getServiceEnabled();
Collection<String> searchableFields = plugin.getFilteredSearchFields(); Collection<String> searchableFields = plugin.getFilteredSearchFields();
groupOnly = plugin.isGroupOnly();
%> %>
<html> <html>
...@@ -169,6 +171,38 @@ ...@@ -169,6 +171,38 @@
<br> <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" />"> <input type="submit" value="<fmt:message key="search.props.edit.form.save_properties" />">
</form> </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