/********************************************************************************** * $URL: https://source.sakaiproject.org/contrib/ufp/usermembership/trunk/tool/src/java/org/sakaiproject/umem/tool/ui/UserListBean.java $ * $Id: UserListBean.java 4381 2007-03-21 11:25:54Z nuno@ufp.pt $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.sakaiproject.umem.tool.ui; import java.io.Serializable; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.text.Collator; import java.text.ParseException; import java.text.RuleBasedCollator; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Properties; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import javax.faces.event.ValueChangeEvent; import javax.faces.model.SelectItem; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.db.api.SqlService; import org.sakaiproject.tool.api.ToolManager; import org.sakaiproject.umem.api.Authz; import org.sakaiproject.user.api.User; import org.sakaiproject.user.api.UserDirectoryService; import org.sakaiproject.user.api.UserNotDefinedException; import org.sakaiproject.util.ResourceLoader; /** * @author <a href="mailto:nuno@ufp.pt">Nuno Fernandes</a> */ public class UserListBean { private static final long serialVersionUID = 1L; private static final String USER_TYPE_ALL = "All"; private static final String USER_TYPE_NONE = "(no type)"; private static final String USER_AUTH_ALL = "All"; private static final String USER_AUTH_INTERNAL = "Internal"; private static final String USER_AUTH_EXTERNAL = "External"; private static final String SORT_USER_NAME = "name"; private static final String SORT_USER_ID = "id"; private static final String SORT_USER_EMAIL = "email"; private static final String SORT_USER_TYPE = "type"; private static final String SORT_USER_AUTHORITY = "authority"; private static final String SORT_USER_CREATED_ON = "createdOn"; private static final String SORT_USER_MODIFIED_ON = "modifiedOn"; private static final String SORT_INTERNAL_USER_ID = "internalUserId"; private static final String NO_NAME_USER = ""; private static final String CFG_USER_TYPE_LIMIT_TO_SELF = "userType.limitToSelf"; private static final String CFG_USER_TYPE_LIMIT_TO_LIST = "userType.limitToList"; /** Our log (commons). */ private static Log LOG = LogFactory.getLog(UserListBean.class); /** Resource bundle */ private transient ResourceLoader msgs = new ResourceLoader("org.sakaiproject.umem.tool.bundle.Messages"); /** Controller fields */ private List<UserRow> userRows; /** Getter vars */ private boolean allowed = false; private boolean refreshQuery = false; private boolean renderTable = false; private boolean renderPager = true; private boolean renderClearSearch = false; private int totalItems = -1; private int firstItem = 0; private int pageSize = 20; private String searchKeyword = null; private boolean userSortAscending = true; private String userSortColumn = SORT_USER_NAME; private List userTypes = null; private List userAuthorities = null; private String selectedUserType = null; private String selectedAuthority = USER_AUTH_ALL; private String newUserType = null; private String newAuthority = USER_AUTH_ALL; /** Private vars */ private static RuleBasedCollator collator; // use private String message = ""; // system /** Sakai services vars */ private transient UserDirectoryService M_uds = (UserDirectoryService) ComponentManager.get(UserDirectoryService.class.getName()); private transient ToolManager M_tm = (ToolManager) ComponentManager.get(ToolManager.class.getName()); private transient SqlService M_sql = (SqlService) ComponentManager.get(SqlService.class.getName()); private transient Authz authz = (Authz) ComponentManager.get(Authz.class.getName()); // ###################################################################################### // UserRow, UserSitesRow CLASS // ###################################################################################### public static class UserRow implements Serializable { private static final long serialVersionUID = 1L; private String userID; private String userEID; private String userDisplayId; private String userName; private String userEmail; private String userType; private String authority; private String createdOn; private String modifiedOn; static { try{ collator= new RuleBasedCollator(((RuleBasedCollator)Collator.getInstance()).getRules().replaceAll("<'\u005f'", "<' '<'\u005f'")); }catch(ParseException e){ collator = (RuleBasedCollator)Collator.getInstance(); } } public UserRow() { } public UserRow(String userID, String userEID, String userDisplayId, String userName, String userEmail, String userType, String authority, String createdOn, String modifiedOn) { this.userID = userID; this.userEID = userEID; this.userDisplayId = userDisplayId; this.userName = userName; this.userEmail = userEmail; this.userType = userType; this.authority = authority; this.createdOn = createdOn; this.modifiedOn = modifiedOn; } public String getUserEmail() { return this.userEmail; } public String getUserID() { return this.userID; } public String getUserEID() { return this.userEID; } public String getUserDisplayId() { return this.userDisplayId; } public String getUserName() { return this.userName; } public String getUserType() { return (this.userType == null)? "" : this.userType; } public String getAuthority(){ return this.authority; } public String getCreatedOn() { return createdOn; } public String getModifiedOn() { return modifiedOn; } public void setUserRow(UserRow row, String authority) { this.userID = row.getUserID(); this.userEID = row.getUserEID(); this.userDisplayId = row.getUserDisplayId(); this.userName = row.getUserName(); this.userEmail = row.getUserEmail(); this.userType = row.getUserType(); this.authority = authority; this.createdOn = row.createdOn; this.modifiedOn = row.modifiedOn; } public UserRow getUserRow() { return this; } } public static final Comparator<UserRow> getUserRowComparator(final String fieldName, final boolean sortAscending, final Collator collator) { return new Comparator<UserRow>() { public int compare(UserRow o1, UserRow o2) { UserRow r1 = (UserRow) o1; UserRow r2 = (UserRow) o2; try{ if(fieldName.equals(SORT_USER_NAME)){ String s1 = r1.getUserName(); String s2 = r2.getUserName(); int res = collator.compare(s1!=null? s1.toLowerCase():"", s2!=null? s2.toLowerCase():""); if(sortAscending) return res; else return -res; }else if(fieldName.equals(SORT_USER_ID)){ String s1 = r1.getUserEID(); String s2 = r2.getUserEID(); int res = collator.compare(s1!=null? s1.toLowerCase():"", s2!=null? s2.toLowerCase():""); if(sortAscending) return res; else return -res; }else if(fieldName.equals(SORT_USER_EMAIL)){ String s1 = r1.getUserEmail(); String s2 = r2.getUserEmail(); int res = collator.compare(s1!=null? s1.toLowerCase():"", s2!=null? s2.toLowerCase():""); if(sortAscending) return res; else return -res; }else if(fieldName.equals(SORT_USER_TYPE)){ String s1 = r1.getUserType(); String s2 = r2.getUserType(); int res = collator.compare(s1!=null? s1.toLowerCase():"", s2!=null? s2.toLowerCase():""); if(sortAscending) return res; else return -res; }else if(fieldName.equals(SORT_USER_AUTHORITY)){ String s1 = r1.getAuthority(); String s2 = r2.getAuthority(); int res = collator.compare(s1!=null? s1.toLowerCase():"", s2!=null? s2.toLowerCase():""); if(sortAscending) return res; else return -res; }else if(fieldName.equals(SORT_USER_CREATED_ON)){ String s1 = r1.getCreatedOn(); String s2 = r2.getCreatedOn(); int res = collator.compare(s1!=null? s1.toLowerCase():"", s2!=null? s2.toLowerCase():""); if(sortAscending) return res; else return -res; }else if(fieldName.equals(SORT_USER_MODIFIED_ON)){ String s1 = r1.getModifiedOn(); String s2 = r2.getModifiedOn(); int res = collator.compare(s1!=null? s1.toLowerCase():"", s2!=null? s2.toLowerCase():""); if(sortAscending) return res; else return -res; }else if(fieldName.equals(SORT_INTERNAL_USER_ID)){ String s1 = r1.getUserID(); String s2 = r2.getUserID(); int res = collator.compare(s1!=null? s1.toLowerCase():"", s2!=null? s2.toLowerCase():""); if(sortAscending) return res; else return -res; } }catch(Exception e){ LOG.warn("Error occurred while sorting by: "+fieldName, e); } return 0; } }; } // ###################################################################################### // Main methods // ###################################################################################### public String getInitValues() { if(searchKeyword == null){ renderClearSearch = false; searchKeyword = msgs.getString("bar_input_search_inst"); }else renderClearSearch = true; if(isAllowed() && renderTable && refreshQuery){ doSearch(); refreshQuery = false; } if(userRows != null){ // Sort resulting list Collections.sort(userRows, getUserRowComparator(userSortColumn, userSortAscending, collator)); } return ""; } private void doSearch() { LOG.debug("Refreshing query..."); selectedUserType = newUserType; selectedAuthority = newAuthority; searchKeyword = searchKeyword.trim(); userRows = new ArrayList<UserRow>(); if(LOG.isDebugEnabled()){ LOG.debug("selectedUserType: " + selectedUserType); LOG.debug("selectedAuthority: " + selectedAuthority); LOG.debug("searchKeyword: " + searchKeyword); } // 1. Search internal users (Sakai DB) if(selectedAuthority.equalsIgnoreCase(USER_AUTH_ALL) || selectedAuthority.equalsIgnoreCase(USER_AUTH_INTERNAL)){ if(LOG.isDebugEnabled()){ LOG.debug("Searching internal users..."); } try{ //SAK-20857 if empty search, return all users, otherwise only those that match. List<User> users; if(StringUtils.isBlank(searchKeyword)) { users = M_uds.getUsers(); } else { users = M_uds.searchUsers(searchKeyword, 1, Integer.MAX_VALUE); } for(User u : users) { // filter user type if(userTypeMatches(u.getType())) { userRows.add(new UserRow( u.getId(), u.getEid(), u.getDisplayId(), u.getDisplayName(), u.getEmail(), u.getType(), msgs.getString("user_auth_internal"), (u.getCreatedTime() == null) ? "" : u.getCreatedTime().toStringLocalDate(), (u.getModifiedTime() == null) ? "" : u.getModifiedTime().toStringLocalDate() ) ); } } if(LOG.isDebugEnabled()){ LOG.debug("Internal search results: " + users.size()); } }catch(Exception e){ LOG.warn("Exception occurred while searching internal users: " + e.getMessage()); e.printStackTrace(); } } // 2. Search users on external user providers if(selectedAuthority.equalsIgnoreCase(USER_AUTH_ALL) || selectedAuthority.equalsIgnoreCase(USER_AUTH_EXTERNAL)){ if(LOG.isDebugEnabled()){ LOG.debug("Searching external users..."); } try{ List<User> users = M_uds.searchExternalUsers(searchKeyword, -1, -1); for(User u : users) { // filter user type if(userTypeMatches(u.getType())) { userRows.add(new UserRow( u.getId(), u.getEid(), u.getDisplayId(), u.getDisplayName(), u.getEmail(), u.getType(), msgs.getString("user_auth_external"), (u.getCreatedTime() == null) ? "" : u.getCreatedTime().toStringLocalDate(), (u.getModifiedTime() == null) ? "" : u.getModifiedTime().toStringLocalDate() ) ); } } if(LOG.isDebugEnabled()){ LOG.debug("External search results: " + users.size()); } }catch(RuntimeException e){ LOG.warn("Exception occurred while searching external users: " + e.getMessage(), e); } } if(LOG.isDebugEnabled()){ LOG.debug("Total results: " + userRows.size()); } // 4. Update pager this.totalItems = userRows.size(); renderPager = false; firstItem = 0; if(totalItems > 0) { renderPager = true; } } private boolean userTypeMatches(String userType) { return USER_TYPE_ALL.equals(selectedUserType) || (!USER_TYPE_NONE.equals(selectedUserType) && StringUtils.equals(userType, selectedUserType)) || (USER_TYPE_NONE.equals(selectedUserType) && StringUtils.isEmpty(userType)); } // ###################################################################################### // ActionListener methods // ###################################################################################### public String processActionSearch() { renderTable = true; refreshQuery = true; return "userlist"; } public String processActionClearSearch() throws SQLException { this.selectedUserType = ((SelectItem) getUserTypes().get(0)).getLabel(); this.selectedAuthority = ((SelectItem) getUserAuthorities().get(0)).getLabel(); searchKeyword = null; renderTable = false; refreshQuery = false; return "userlist"; } public void processActionSearchChangeListener(ValueChangeEvent event) { searchKeyword = (searchKeyword == null) ? "" : searchKeyword.trim(); } // ###################################################################################### // Generic get/set methods // ###################################################################################### public boolean isAllowed() { allowed = authz.isUserAbleToViewUmem(M_tm.getCurrentPlacement().getContext()); if(!allowed){ FacesContext fc = FacesContext.getCurrentInstance(); message = msgs.getString("unauthorized"); fc.addMessage("allowed", new FacesMessage(FacesMessage.SEVERITY_FATAL, message, null)); allowed = false; } return allowed; } public boolean isEmptyUserList() { return renderTable && ((userRows == null) || (userRows.size() <= 0)); } public List getUserRows() { return userRows; } public int getRowsNumber() { if(totalItems <= pageSize){ return totalItems; } return pageSize; } public int getFirstItem() { return firstItem; } public void setFirstItem(int firstItem) { this.firstItem = firstItem; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getTotalItems() { return this.totalItems; } public void setTotalItems(int totalItems) { this.totalItems = totalItems; } public boolean isRenderPager() { return renderTable && renderPager; } public boolean isRenderTable() { return renderTable && userRows != null && userRows.size() > 0; } public boolean isRenderClearSearch() { return renderClearSearch; } public String getSearchKeyword() { return searchKeyword; } public void setSearchKeyword(String searchKeyword) { this.searchKeyword = searchKeyword; } public void setUserSortAscending(boolean sortAscending) { this.userSortAscending = sortAscending; } public boolean getUserSortAscending() { return this.userSortAscending; } public String getUserSortColumn() { return this.userSortColumn; } public void setUserSortColumn(String sortColumn) { this.userSortColumn = sortColumn; } private void addExtraUserType(String newType) throws SQLException{ boolean found = false; if(userTypes == null) userTypes = getUserTypes(); Iterator it = userTypes.iterator(); while(it.hasNext()){ if(((SelectItem) it.next()).getLabel().equals(newType)) found = true; } if(!found){ userTypes.add(userTypes.size()-1,new SelectItem(newType)); } } public List getUserTypes() throws SQLException { if(userTypes == null){ Properties config = M_tm.getCurrentPlacement().getConfig(); Boolean userTypeLimitToSelf = Boolean.parseBoolean(config.getProperty(CFG_USER_TYPE_LIMIT_TO_SELF, "false")); String userTypeLimitToListStr = config.getProperty(CFG_USER_TYPE_LIMIT_TO_LIST, ""); String[] userTypeLimitToList = null; userTypes = new ArrayList(); if(userTypeLimitToSelf){ userTypes.add(new SelectItem(M_uds.getCurrentUser().getType())); }else if(!"".equals(userTypeLimitToListStr)){ userTypeLimitToList = userTypeLimitToListStr.split(","); StringBuilder all = new StringBuilder(); for(int i=0; i<userTypeLimitToList.length; i++){ userTypes.add(new SelectItem(userTypeLimitToList[i])); all.append("'"); all.append(userTypeLimitToList[i]); all.append("'"); if(i<userTypeLimitToList.length-1) all.append(","); } userTypes.add(0,new SelectItem(all.toString(), msgs.getString("user_type_all"))); }else { userTypes.add(new SelectItem(USER_TYPE_ALL, msgs.getString("user_type_all"))); Connection c = null; Statement st = null; ResultSet rs = null; try{ c = M_sql.borrowConnection(); String vendor = M_sql.getVendor(); String sql = null; if(vendor.equalsIgnoreCase("oracle")){ sql = "select distinct TYPE from SAKAI_USER where TYPE is not null order by TYPE"; }else{ sql = "select distinct TYPE from SAKAI_USER where TYPE!='' and TYPE is not null order by TYPE;"; } st = c.createStatement(); rs = st.executeQuery(sql); while (rs.next()){ String type = rs.getString(1); userTypes.add(new SelectItem(type)); } }catch(SQLException e){ LOG.error("SQL error occurred while retrieving user types: " + e.getMessage(), e); }finally{ try{ if(rs != null) rs.close(); }finally{ try{ if(st != null) st.close(); }finally{ if(c != null) M_sql.returnConnection(c); } } } userTypes.add(new SelectItem(USER_TYPE_NONE, msgs.getString("user_type_none"))); } } return userTypes; } public String getSelectedUserType() throws SQLException { if(this.selectedUserType != null) return this.selectedUserType; else{ this.selectedUserType = ((SelectItem) getUserTypes().get(0)).getLabel(); return this.selectedUserType; } } public void setSelectedUserType(String type) { this.newUserType = type; } public List getUserAuthorities() { userAuthorities = new ArrayList(); userAuthorities.add(new SelectItem(USER_AUTH_ALL, msgs.getString("user_auth_all"))); userAuthorities.add(new SelectItem(USER_AUTH_INTERNAL, msgs.getString("user_auth_internal"))); userAuthorities.add(new SelectItem(USER_AUTH_EXTERNAL, msgs.getString("user_auth_external"))); return userAuthorities; } public String getSelectedAuthority() { if(this.selectedAuthority != null) return this.selectedAuthority; else{ this.selectedAuthority = ((SelectItem) getUserAuthorities().get(0)).getLabel(); return this.selectedAuthority; } } public void setSelectedAuthority(String type) { this.newAuthority = type; } public String getNoName(){ return NO_NAME_USER; } // ###################################################################################### // CSV export // ###################################################################################### public void exportAsCsv(ActionEvent event) { Export.writeAsCsv(buildDataTable(userRows), "UserListing"); } /** * Export the data in this user list to the response stream as an Excel workbook * @param event */ public void exportAsXls(ActionEvent event) { Export.writeAsXls(buildDataTable(userRows), "UserListing"); } /** * Build a generic tabular representation of the user membership data export. * * @param userRows The content of the table * @return * A table of data suitable to be exported */ private List<List<Object>> buildDataTable(List<UserRow> userRows) { List<List<Object>> table = new LinkedList<List<Object>>(); //add header row List<Object> header = new ArrayList<Object>(); header.add(msgs.getString("user_id")); header.add(msgs.getString("internal_user_id")); header.add(msgs.getString("user_name")); header.add(msgs.getString("user_email")); header.add(msgs.getString("user_type")); header.add(msgs.getString("user_authority")); header.add(msgs.getString("user_created_on")); header.add(msgs.getString("user_modified_on")); table.add(header); //add data rows for (UserRow userRow : userRows) { List<Object> currentRow = new ArrayList<Object>(); currentRow.add(userRow.getUserEID()); currentRow.add(userRow.getUserID()); currentRow.add(userRow.getUserName()); currentRow.add(userRow.getUserEmail()); currentRow.add(userRow.getUserType()); currentRow.add(userRow.getAuthority()); currentRow.add(userRow.getCreatedOn()); currentRow.add(userRow.getModifiedOn()); table.add(currentRow); } return table; } }