Merged V3.1 to HEAD

13249: Fix for ETHREEOH-1064 and ETHREEOH-1286 and fix for issue where New Page action not activated in Share Wiki page list.
   13251: Final part of fix for ETHREEOH-1270 - Group pickers in JSF client now use new findAuthorities() API to find groups for user searches - over 10x quicker for installations with many groups.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@13580 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Kevin Roast
2009-03-11 19:51:09 +00:00
parent 1aa1ef1f13
commit a94f5f0bff
9 changed files with 130 additions and 60 deletions

View File

@@ -306,6 +306,7 @@ upload_new_version=Upload new version
checkin_this_file=Check in this file checkin_this_file=Check in this file
cannot_delete_node_has_working_copy=Cannot delete file \"{0}\" as it has an associated working copy. cannot_delete_node_has_working_copy=Cannot delete file \"{0}\" as it has an associated working copy.
unknown=Unknown unknown=Unknown
picker_search_min=Please enter at least {0} characters to perform a search.
# Properties # Properties
username=User Name username=User Name

View File

@@ -111,6 +111,9 @@
<!-- Allow Users to modify their personal settings in the User Console screen - you may wish to --> <!-- Allow Users to modify their personal settings in the User Console screen - you may wish to -->
<!-- disable this if your admin does not wish to allow users to edit their own details --> <!-- disable this if your admin does not wish to allow users to edit their own details -->
<allow-user-config>true</allow-user-config> <allow-user-config>true</allow-user-config>
<!-- the minimum number of characters required for a valid search in the pickers -->
<picker-search-minimum>2</picker-search-minimum>
</client> </client>
</config> </config>

View File

@@ -46,7 +46,6 @@ import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.web.app.Application; import org.alfresco.web.app.Application;
import org.alfresco.web.bean.dialog.BaseDialogBean; import org.alfresco.web.bean.dialog.BaseDialogBean;
import org.alfresco.web.bean.groups.GroupsDialog.UserAuthorityDetails; import org.alfresco.web.bean.groups.GroupsDialog.UserAuthorityDetails;
@@ -213,13 +212,7 @@ public class AddUsersDialog extends BaseDialogBean
// Use lucene search to retrieve user details // Use lucene search to retrieve user details
String term = QueryParser.escape(contains.trim()); String term = QueryParser.escape(contains.trim());
StringBuilder query = new StringBuilder(128); StringBuilder query = new StringBuilder(128);
query.append("@").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:firstName:\"*"); Utils.generatePersonSearch(query, term);
query.append(term);
query.append("*\" @").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:lastName:\"*");
query.append(term);
query.append("*\" @").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:userName:");
query.append(term);
query.append("*");
List<NodeRef> nodes; List<NodeRef> nodes;
ResultSet resultSet = Repository.getServiceRegistry(context).getSearchService().query( ResultSet resultSet = Repository.getServiceRegistry(context).getSearchService().query(
Repository.getStoreRef(), Repository.getStoreRef(),

View File

@@ -39,7 +39,6 @@ import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.web.app.Application; import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.SortableSelectItem; import org.alfresco.web.ui.common.SortableSelectItem;
@@ -55,7 +54,6 @@ import org.apache.commons.logging.LogFactory;
*/ */
public class SetPermissionsDialog extends UpdatePermissionsDialog public class SetPermissionsDialog extends UpdatePermissionsDialog
{ {
private static final long serialVersionUID = -8139619811033232880L; private static final long serialVersionUID = -8139619811033232880L;
/** logger */ /** logger */
@@ -251,13 +249,7 @@ public class SetPermissionsDialog extends UpdatePermissionsDialog
// Use lucene search to retrieve user details // Use lucene search to retrieve user details
String term = QueryParser.escape(contains.trim()); String term = QueryParser.escape(contains.trim());
StringBuilder query = new StringBuilder(128); StringBuilder query = new StringBuilder(128);
query.append("@").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:firstName:\"*"); Utils.generatePersonSearch(query, term);
query.append(term);
query.append("*\" @").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:lastName:\"*");
query.append(term);
query.append("*\" @").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:userName:");
query.append(term);
query.append("*");
ResultSet resultSet = Repository.getServiceRegistry(context).getSearchService().query(Repository.getStoreRef(), SearchService.LANGUAGE_LUCENE, query.toString()); ResultSet resultSet = Repository.getServiceRegistry(context).getSearchService().query(Repository.getStoreRef(), SearchService.LANGUAGE_LUCENE, query.toString());
List<NodeRef> nodes; List<NodeRef> nodes;
try try
@@ -284,18 +276,23 @@ public class SetPermissionsDialog extends UpdatePermissionsDialog
} }
else else
{ {
// groups - simple text based match on name // groups - text search match on supplied name
Set<String> groups = getAuthorityService().getAllAuthorities(AuthorityType.GROUP); String term = PermissionService.GROUP_PREFIX + "*" + contains.trim() + "*";
Set<String> groups;
groups = getAuthorityService().findAuthorities(AuthorityType.GROUP, term);
groups.addAll(getAuthorityService().getAllAuthorities(AuthorityType.EVERYONE)); groups.addAll(getAuthorityService().getAllAuthorities(AuthorityType.EVERYONE));
String containsLower = contains.trim().toLowerCase(); String groupDisplayName;
int offset = PermissionService.GROUP_PREFIX.length();
for (String group : groups) for (String group : groups)
{ {
if (group.toLowerCase().indexOf(containsLower, offset) != -1) // get display name, if not present strip prefix from group id
groupDisplayName = getAuthorityService().getAuthorityDisplayName(group);
if (groupDisplayName == null || groupDisplayName.length() == 0)
{ {
results.add(new SortableSelectItem(group, group.substring(offset), group)); groupDisplayName = group.substring(PermissionService.GROUP_PREFIX.length());
} }
results.add(new SortableSelectItem(group, groupDisplayName, groupDisplayName));
} }
} }

View File

@@ -35,6 +35,7 @@ import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.Set; import java.util.Set;
import javax.faces.application.FacesMessage;
import javax.faces.component.UISelectOne; import javax.faces.component.UISelectOne;
import javax.faces.context.FacesContext; import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent; import javax.faces.event.ActionEvent;
@@ -86,6 +87,7 @@ public abstract class BaseInviteUsersWizard extends BaseWizardBean
private static final String MSG_INVITED_TO = "invited_to"; private static final String MSG_INVITED_TO = "invited_to";
private static final String MSG_INVITED_ROLE = "invite_role"; private static final String MSG_INVITED_ROLE = "invite_role";
private static final String MSG_MAX_USERS = "max_users_returned"; private static final String MSG_MAX_USERS = "max_users_returned";
private static final String MSG_SEARCH_MINIMUM = "picker_search_min";
protected static final String STEP_NOTIFY = "notify"; protected static final String STEP_NOTIFY = "notify";
@@ -365,6 +367,15 @@ public abstract class BaseInviteUsersWizard extends BaseWizardBean
{ {
FacesContext context = FacesContext.getCurrentInstance(); FacesContext context = FacesContext.getCurrentInstance();
// quick exit if not enough characters entered for a search
String search = contains.trim();
int searchMin = Application.getClientConfig(context).getPickerSearchMinimum();
if (search.length() < searchMin)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(context, MSG_SEARCH_MINIMUM), searchMin));
return new SelectItem[0];
}
SelectItem[] items; SelectItem[] items;
this.maxUsersReturned = false; this.maxUsersReturned = false;
@@ -374,12 +385,14 @@ public abstract class BaseInviteUsersWizard extends BaseWizardBean
tx = Repository.getUserTransaction(context, true); tx = Repository.getUserTransaction(context, true);
tx.begin(); tx.begin();
List<SelectItem> results = new ArrayList<SelectItem>(); int maxResults = Application.getClientConfig(context).getInviteUsersMaxResults();
List<SelectItem> results;
if (filterIndex == 0) if (filterIndex == 0)
{ {
// Use lucene search to retrieve user details // Use lucene search to retrieve user details
String term = QueryParser.escape(contains.trim()); String term = QueryParser.escape(search);
StringBuilder query = new StringBuilder(128); StringBuilder query = new StringBuilder(128);
if (contains == null || contains.length() == 0) if (contains == null || contains.length() == 0)
{ {
@@ -390,17 +403,9 @@ public abstract class BaseInviteUsersWizard extends BaseWizardBean
} }
else else
{ {
query.append("@").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:firstName:\"*"); Utils.generatePersonSearch(query, term);
query.append(term);
query.append("*\" @").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:lastName:\"*");
query.append(term);
query.append("*\" @").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:userName:");
query.append(term);
query.append("*");
} }
int maxResults = Application.getClientConfig(context).getInviteUsersMaxResults();
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Maximum invite users results size: " + maxResults); logger.debug("Maximum invite users results size: " + maxResults);
@@ -428,12 +433,7 @@ public abstract class BaseInviteUsersWizard extends BaseWizardBean
resultSet.close(); resultSet.close();
} }
// set the maximum users returned flag if appropriate results = new ArrayList<SelectItem>(nodes.size());
if (nodes.size() == maxResults)
{
this.maxUsersReturned = true;
}
for (int index=0; index<nodes.size(); index++) for (int index=0; index<nodes.size(); index++)
{ {
NodeRef personRef = nodes.get(index); NodeRef personRef = nodes.get(index);
@@ -449,25 +449,28 @@ public abstract class BaseInviteUsersWizard extends BaseWizardBean
} }
else else
{ {
// groups - simple text based match on name // groups - text search match on supplied name
Set<String> groups = getAuthorityService().getAllAuthorities(AuthorityType.GROUP); String term = PermissionService.GROUP_PREFIX + "*" + search + "*";
Set<String> groups;
groups = getAuthorityService().findAuthorities(AuthorityType.GROUP, term);
groups.addAll(getAuthorityService().getAllAuthorities(AuthorityType.EVERYONE)); groups.addAll(getAuthorityService().getAllAuthorities(AuthorityType.EVERYONE));
String containsLower = contains.trim().toLowerCase(); results = new ArrayList<SelectItem>(groups.size());
int count = 0;
String groupDisplayName; String groupDisplayName;
for (String group : groups) for (String group : groups)
{ {
// get display name, if not present strip prefix from group id // get display name, if not present strip prefix from group id
groupDisplayName = authorityService.getAuthorityDisplayName(group); groupDisplayName = getAuthorityService().getAuthorityDisplayName(group);
if (groupDisplayName == null || groupDisplayName.length() == 0) if (groupDisplayName == null || groupDisplayName.length() == 0)
{ {
groupDisplayName = group.substring(PermissionService.GROUP_PREFIX.length()); groupDisplayName = group.substring(PermissionService.GROUP_PREFIX.length());
} }
if (groupDisplayName.toLowerCase().indexOf(containsLower) != -1) results.add(new SortableSelectItem(group, groupDisplayName, groupDisplayName));
{
results.add(new SortableSelectItem(group, groupDisplayName, groupDisplayName)); if (++count == maxResults) break;
}
} }
} }
@@ -475,6 +478,12 @@ public abstract class BaseInviteUsersWizard extends BaseWizardBean
results.toArray(items); results.toArray(items);
Arrays.sort(items); Arrays.sort(items);
// set the maximum users returned flag if appropriate
if (results.size() == maxResults)
{
this.maxUsersReturned = true;
}
// commit the transaction // commit the transaction
tx.commit(); tx.commit();
} }

View File

@@ -37,12 +37,13 @@ import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.search.impl.lucene.QueryParser; import org.alfresco.repo.search.impl.lucene.QueryParser;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.search.LimitBy;
import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowService; import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.web.app.Application; import org.alfresco.web.app.Application;
import org.alfresco.web.bean.dialog.BaseDialogBean; import org.alfresco.web.bean.dialog.BaseDialogBean;
import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.bean.repository.Repository;
@@ -57,6 +58,8 @@ import org.alfresco.web.ui.common.Utils;
*/ */
public abstract class BaseReassignDialog extends BaseDialogBean public abstract class BaseReassignDialog extends BaseDialogBean
{ {
private static final String MSG_SEARCH_MINIMUM = "picker_search_min";
transient private WorkflowService workflowService; transient private WorkflowService workflowService;
transient private PersonService personService; transient private PersonService personService;
@@ -125,6 +128,15 @@ public abstract class BaseReassignDialog extends BaseDialogBean
{ {
FacesContext context = FacesContext.getCurrentInstance(); FacesContext context = FacesContext.getCurrentInstance();
// quick exit if not enough characters entered for a search
String search = contains.trim();
int searchMin = Application.getClientConfig(context).getPickerSearchMinimum();
if (search.length() < searchMin)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(context, MSG_SEARCH_MINIMUM), searchMin));
return new SelectItem[0];
}
SelectItem[] items; SelectItem[] items;
UserTransaction tx = null; UserTransaction tx = null;
@@ -133,19 +145,25 @@ public abstract class BaseReassignDialog extends BaseDialogBean
{ {
tx = Repository.getUserTransaction(context, true); tx = Repository.getUserTransaction(context, true);
tx.begin(); tx.begin();
int maxResults = Application.getClientConfig(context).getInviteUsersMaxResults();
// Use lucene search to retrieve user details // Use lucene search to retrieve user details
String term = QueryParser.escape(contains.trim()); String term = QueryParser.escape(contains.trim());
StringBuilder query = new StringBuilder(128); StringBuilder query = new StringBuilder(128);
query.append("@").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:firstName:\"*"); Utils.generatePersonSearch(query, term);
query.append(term);
query.append("*\" @").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:lastName:\"*"); SearchParameters searchParams = new SearchParameters();
query.append(term); searchParams.addStore(Repository.getStoreRef());
query.append("*\" @").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:userName:"); searchParams.setLanguage(SearchService.LANGUAGE_LUCENE);
query.append(term); searchParams.setQuery(query.toString());
query.append("*"); if (maxResults > 0)
resultSet = Repository.getServiceRegistry(context).getSearchService().query( {
Repository.getStoreRef(), SearchService.LANGUAGE_LUCENE, query.toString()); searchParams.setLimit(maxResults);
searchParams.setLimitBy(LimitBy.FINAL_SIZE);
}
resultSet = Repository.getServiceRegistry(context).getSearchService().query(searchParams);
List<NodeRef> nodes = resultSet.getNodeRefs(); List<NodeRef> nodes = resultSet.getNodeRefs();
ArrayList<SelectItem> itemList = new ArrayList<SelectItem>(nodes.size()); ArrayList<SelectItem> itemList = new ArrayList<SelectItem>(nodes.size());

View File

@@ -85,6 +85,7 @@ public class ClientConfigElement extends ConfigElementAdapter
private boolean zeroByteFileUploads = true; private boolean zeroByteFileUploads = true;
private boolean userGroupAdmin = true; private boolean userGroupAdmin = true;
private boolean allowUserConfig = true; private boolean allowUserConfig = true;
private int pickerSearchMinimum = 2;
/** /**
@@ -281,6 +282,11 @@ public class ClientConfigElement extends ConfigElementAdapter
combinedElement.setUserGroupAdmin(newElement.isUserGroupAdmin()); combinedElement.setUserGroupAdmin(newElement.isUserGroupAdmin());
} }
if (newElement.getPickerSearchMinimum() != combinedElement.getPickerSearchMinimum())
{
combinedElement.setPickerSearchMinimum(newElement.getPickerSearchMinimum());
}
return combinedElement; return combinedElement;
} }
@@ -810,4 +816,20 @@ public class ClientConfigElement extends ConfigElementAdapter
{ {
this.allowUserConfig = allowUserConfig; this.allowUserConfig = allowUserConfig;
} }
/**
* @return Returns the minimum number of characters for a picker search.
*/
public int getPickerSearchMinimum()
{
return this.pickerSearchMinimum;
}
/**
* @param searchMinimum The minimum number of characters for a picker search.
*/
/*package*/ void setPickerSearchMinimum(int searchMinimum)
{
this.pickerSearchMinimum = searchMinimum;
}
} }

View File

@@ -70,6 +70,7 @@ public class ClientElementReader implements ConfigElementReader
public static final String ELEMENT_ZEROBYTEFILEUPLOADS = "zero-byte-file-uploads"; public static final String ELEMENT_ZEROBYTEFILEUPLOADS = "zero-byte-file-uploads";
public static final String ELEMENT_USERGROUPADMIN = "user-group-admin"; public static final String ELEMENT_USERGROUPADMIN = "user-group-admin";
public static final String ELEMENT_ALLOWUSERCONFIG = "allow-user-config"; public static final String ELEMENT_ALLOWUSERCONFIG = "allow-user-config";
public static final String ELEMENT_PICKERSEARCHMINIMUM = "picker-search-minimum";
/** /**
@@ -307,6 +308,13 @@ public class ClientElementReader implements ConfigElementReader
{ {
configElement.setAllowUserConfig(Boolean.parseBoolean(userConfig.getTextTrim())); configElement.setAllowUserConfig(Boolean.parseBoolean(userConfig.getTextTrim()));
} }
// get the minimum number of characters for valid picker search string
Element pickerSearchMin = element.element(ELEMENT_PICKERSEARCHMINIMUM);
if (pickerSearchMin != null)
{
configElement.setPickerSearchMinimum(Integer.parseInt(pickerSearchMin.getTextTrim()));
}
} }
return configElement; return configElement;

View File

@@ -67,6 +67,7 @@ import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NoTransformerException; import org.alfresco.service.cmr.repository.NoTransformerException;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.util.URLEncoder; import org.alfresco.util.URLEncoder;
import org.alfresco.web.app.Application; import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.DownloadContentServlet; import org.alfresco.web.app.servlet.DownloadContentServlet;
@@ -1051,4 +1052,22 @@ public final class Utils extends StringUtils
} }
return userAgent; return userAgent;
} }
/**
* Generate the Lucene query for a standard Person search. The query used is standardised
* across multiple JSF components and beans.
*
* @param query Buffer for the query
* @param term Search term
*/
public static void generatePersonSearch(StringBuilder query, String term)
{
query.append("@").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:firstName:\"*");
query.append(term);
query.append("*\" @").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:lastName:\"*");
query.append(term);
query.append("*\" @").append(NamespaceService.CONTENT_MODEL_PREFIX).append("\\:userName:");
query.append(term);
query.append("*");
}
} }