Index: authz/authz-tool/tool/src/java/org/sakaiproject/authz/tool/RealmsAction.java =================================================================== --- authz/authz-tool/tool/src/java/org/sakaiproject/authz/tool/RealmsAction.java (revision 130931) +++ authz/authz-tool/tool/src/java/org/sakaiproject/authz/tool/RealmsAction.java (working copy) @@ -21,6 +21,7 @@ package org.sakaiproject.authz.tool; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -54,6 +55,8 @@ import org.sakaiproject.entity.api.Entity; import org.sakaiproject.event.api.SessionState; import org.sakaiproject.javax.PagingPosition; +import org.sakaiproject.site.api.RosterAudit; +import org.sakaiproject.site.api.RosterAuditService; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.cover.SiteService; import org.sakaiproject.tool.cover.SessionManager; @@ -77,6 +80,9 @@ private org.sakaiproject.authz.api.GroupProvider groupProvider = (org.sakaiproject.authz.api.GroupProvider) ComponentManager .get(org.sakaiproject.authz.api.GroupProvider.class); + private static RosterAudit rosterAudit = (RosterAudit) ComponentManager.get("org.sakaiproject.site.api.RosterAudit.sitemanage"); + private static RosterAuditService rosterAuditService = (RosterAuditService) ComponentManager.get(RosterAuditService.class); + private static Log M_log = LogFactory.getLog(RealmsAction.class); /** @@ -704,6 +710,13 @@ try { AuthzGroupService.save(realm); + // Grab the list from session state and save it, if appropriate + List rosterAuditList = (List) state.getAttribute("rosterAuditList"); + if (rosterAuditList!=null && !rosterAuditList.isEmpty()) + { + rosterAudit.addToRosterAuditing(rosterAuditList); + state.removeAttribute("rosterAuditList"); + } } catch (GroupNotDefinedException e) { @@ -1285,8 +1298,38 @@ if (realm != null && user != null) { + // Need to grab the role before removing the user from the realm + String roleId = realm.getUserRole(user.getId()).getId(); + // clear out this user's settings realm.removeMember(user.getId()); + + // Grab the rosterAuditList from the SessionState if it exists, then make the appropriate String[] to log the audit + List rosterAuditList = (List) state.getAttribute("rosterAuditList"); + if (rosterAuditList!=null) + { + state.removeAttribute("rosterAuditList"); + } + else + { + rosterAuditList = new ArrayList(); + } + String realmId = realm.getId(); + String siteId = ""; + String fullReferenceRoot = SiteService.REFERENCE_ROOT + Entity.SEPARATOR; + if (realmId.startsWith(fullReferenceRoot)) + { + siteId = realmId.substring(fullReferenceRoot.length()); + } + else + { + // this will likely never happen + siteId = realmId; + } + String[] rosterAuditString = {siteId,user.getEid(),roleId,rosterAuditService.ROSTER_AUDIT_ACTION_REMOVE,rosterAudit.getDatabaseSourceKey(),UserDirectoryService.getCurrentUser().getEid(),null}; + rosterAuditList.add(rosterAuditString); + + state.setAttribute("rosterAuditList", rosterAuditList); // done with the user state.removeAttribute("user"); @@ -1391,6 +1434,41 @@ { // TODO: active, provided realm.addMember(user.getId(), roles, status, false); + + // roster auditing + List rosterAuditList = (List) state.getAttribute("rosterAuditList"); + if (rosterAuditList!=null && !rosterAuditList.isEmpty()) + { + state.removeAttribute("rosterAuditList"); + } + else + { + rosterAuditList = new ArrayList(); + } + String realmId = realm.getId(); + String siteId = ""; + String fullReferenceRoot = SiteService.REFERENCE_ROOT + Entity.SEPARATOR; + if (realmId.startsWith(fullReferenceRoot)) + { + siteId = realmId.substring(fullReferenceRoot.length()); + } + else + { + // this will likely never happen, but adding it in as a backup + siteId = realmId; + } + String newOrExistingUser = (String) state.getAttribute("newUser"); + String rosterAuditAction = rosterAuditService.ROSTER_AUDIT_ACTION_UPDATE; + + // if this using the Grant As functionality, it will be a new user being added + if (newOrExistingUser!=null && "true".equals(newOrExistingUser)) + { + rosterAuditAction = rosterAuditService.ROSTER_AUDIT_ACTION_ADD; + } + String[] rosterAuditString = {siteId,user.getEid(),roles,rosterAuditAction,rosterAudit.getDatabaseSourceKey(),UserDirectoryService.getCurrentUser().getEid(),null}; + rosterAuditList.add(rosterAuditString); + + state.setAttribute("rosterAuditList", rosterAuditList); } } @@ -1413,6 +1491,7 @@ state.removeAttribute("allLocks"); state.removeAttribute("roles"); state.removeAttribute("locks"); + state.removeAttribute("rosterAuditList"); } // cleanState Index: config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties =================================================================== --- config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties (revision 130931) +++ config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties (working copy) @@ -2181,6 +2181,11 @@ # DEFAULT: true # roster_view_email=false +# This determines if you want roster event logging in your instance of Sakai +# This setting controls both if the page renders in the Roster tool and writing the audits to the database +# DEFAULT: true +# roster_event_log=true + ## SEARCH # Enable the elastic search # DEFAULT: false Index: roster/components/src/webapp/WEB-INF/components.xml =================================================================== --- roster/components/src/webapp/WEB-INF/components.xml (revision 130931) +++ roster/components/src/webapp/WEB-INF/components.xml (working copy) @@ -26,7 +26,8 @@ - + + Index: roster/roster-api/src/java/org/sakaiproject/api/app/roster/RosterManager.java =================================================================== --- roster/roster-api/src/java/org/sakaiproject/api/app/roster/RosterManager.java (revision 130931) +++ roster/roster-api/src/java/org/sakaiproject/api/app/roster/RosterManager.java (working copy) @@ -99,4 +99,11 @@ * @return */ public boolean isGroupMembershipViewable(); + + /** + * Can the user view the event log page? + * @param siteId Site id where we want to get the users from + * @return + */ + public boolean isEventLogViewable(String siteId); } Index: roster/roster-app/src/bundle/org/sakaiproject/tool/roster/bundle/Messages.properties =================================================================== --- roster/roster-app/src/bundle/org/sakaiproject/tool/roster/bundle/Messages.properties (revision 130931) +++ roster/roster-app/src/bundle/org/sakaiproject/tool/roster/bundle/Messages.properties (working copy) @@ -6,11 +6,13 @@ navbar_pics=Pictures navbar_status= Enrollment Status navbar_group_membership=Group Membership +navbar_eventLog=Event Log title_overview=Overview title_pictures=Pictures title_status=Enrollment Status title_group_membership=Group Membership +title_event_log=Event Log roster_search_text=Name or ID roster_search_button=Find @@ -159,3 +161,19 @@ # SSP Add Alert facet_ssp=Add Alert ssp_add_alert = Add + +# Event Log Text - headers +event_log_name = Name +event_log_user_id = User ID +event_log_role = Role +event_log_date = Date +event_log_event = Event +event_log_source = Source + +# Event Log Text - actions +event_log_add = Add +event_log_remove = Remove +event_log_update = Update + +# Event Log Text - source texts +event_log_not_available = n/a Index: roster/roster-app/src/java/org/sakaiproject/tool/roster/BaseRosterPageBean.java =================================================================== --- roster/roster-app/src/java/org/sakaiproject/tool/roster/BaseRosterPageBean.java (revision 130931) +++ roster/roster-app/src/java/org/sakaiproject/tool/roster/BaseRosterPageBean.java (working copy) @@ -188,6 +188,7 @@ protected Boolean renderPicturesLink; protected Boolean renderProfileLinks; protected Boolean renderGroupMembershipLink; + protected Boolean renderEventLogLink; public boolean isRenderStatusLink() { if(renderStatusLink == null) { @@ -226,6 +227,13 @@ return renderOfficialPhotos.booleanValue(); } + public boolean isRenderEventLogLink() { + if(renderEventLogLink == null) { + renderEventLogLink = filter.services.rosterManager.isEventLogViewable(getSiteContext()); + } + return renderEventLogLink.booleanValue(); + } + public String getPrintFriendlyUrl() { return ServerConfigurationService.getToolUrl() + Entity.SEPARATOR Index: roster/roster-app/src/java/org/sakaiproject/tool/roster/RosterEventLog.java =================================================================== --- roster/roster-app/src/java/org/sakaiproject/tool/roster/RosterEventLog.java (revision 0) +++ roster/roster-app/src/java/org/sakaiproject/tool/roster/RosterEventLog.java (working copy) @@ -0,0 +1,463 @@ +package org.sakaiproject.tool.roster; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.text.Collator; +import java.text.DateFormat; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.faces.context.FacesContext; +import javax.faces.event.ActionEvent; + +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.jsf.util.LocaleUtil; +import org.sakaiproject.site.api.RosterAudit; +import org.sakaiproject.site.api.RosterAuditService; +import org.sakaiproject.user.api.User; +import org.sakaiproject.user.api.UserNotDefinedException; + +public class RosterEventLog extends BaseRosterPageBean { + private static final Log log = LogFactory.getLog(RosterEventLog.class); + protected List eventLog = new ArrayList(); + // Static comparators + public static final Comparator displayNameComparatorEL; + public static final Comparator userIdComparatorEL; + public static final Comparator roleNameComparatorEL; + public static final Comparator auditStampComparatorEL; + public static final Comparator actionTextComparatorEL; + public static final Comparator sourceTextComparatorEL; + protected String sortColumn; + protected boolean sortAscending; + private int totalItems = -1; + private int firstItem = 0; + private int pageSize = 0; + private Map userMap = new HashMap(); + private transient SqlService sqlService = (SqlService) ComponentManager.get(SqlService.class.getName()); + private transient RosterAuditService rosterAuditService = (RosterAuditService) ComponentManager.get(RosterAuditService.class.getName()); + + static { + displayNameComparatorEL = new Comparator() { + public int compare(EventLog one, EventLog another) { + int comparison = one.getUserDisplayName().compareToIgnoreCase(another.getUserDisplayName()); + return comparison == 0 ? userIdComparatorEL.compare(one,another) : comparison; + } + }; + + userIdComparatorEL = new Comparator() { + public int compare(EventLog one, EventLog another) { + return Collator.getInstance().compare(one.getUser().getEid(),another.getUser().getEid()); + } + }; + + roleNameComparatorEL = new Comparator() { + public int compare(EventLog one, EventLog another) { + int comparison = Collator.getInstance().compare(one.getRoleName(),another.getRoleName()); + return comparison == 0 ? userIdComparatorEL.compare(one,another) : comparison; + } + }; + + auditStampComparatorEL = new Comparator() { + public int compare(EventLog one, EventLog another) { + // calling auditStamp directly so it does a comparison to the actual date versus a string style comparison, which isn't quite right. + int comparison = (one.auditStamp.compareTo(another.auditStamp)); + return comparison == 0 ? userIdComparatorEL.compare(one,another) : comparison; + } + }; + + actionTextComparatorEL = new Comparator() { + public int compare(EventLog one, EventLog another) { + int comparison = Collator.getInstance().compare(one.getActionText(),another.getActionText()); + return comparison == 0 ? userIdComparatorEL.compare(one,another) : comparison; + } + }; + + sourceTextComparatorEL = new Comparator() { + public int compare(EventLog one, EventLog another) { + int comparison = Collator.getInstance().compare(one.getSourceText(),another.getSourceText()); + return comparison == 0 ? userIdComparatorEL.compare(one,another) : comparison; + } + }; + } + + public void export(ActionEvent event) { + // not really an option in the UI, but have to create an empty method because of BaseRosterPageBean + } + + protected Comparator getComparatorEL() + { + String sortColumn = getSortColumn(); + Comparator comparator; + if ("userDisplayName".equals(sortColumn)) + { + comparator = displayNameComparatorEL; + } + else if ("userId".equals(sortColumn)) + { + comparator = userIdComparatorEL; + } + else if("roleName".equals(sortColumn)) + { + comparator = roleNameComparatorEL; + } + else if("auditStamp".equals(sortColumn)) + { + comparator = auditStampComparatorEL; + } + else if("actionText".equals(sortColumn)) + { + comparator = actionTextComparatorEL; + } + else if("sourceText".equals(sortColumn)) + { + comparator = sourceTextComparatorEL; + } + else + { + // Default to the sort name + comparator = auditStampComparatorEL; + } + return comparator; + } + + public class EventLog { + protected String actionTaken; + protected String actionText; + protected User actionUser; + protected Date auditStamp; + protected String roleName; + protected String source; + protected String sourceText; + protected User user; + protected String userDisplayName; + protected String childSiteId; + + /** + * Constructs a EventLogImpl. + * + * @param user - this is a User object for who was add/dropped from a site + * @param roleName - the user's role in the site + * @param actionTaken - this interprets the A, D, and U and return the appropriate text from the bundle + * @param auditStamp - will return a String, although a Date object is passed in. This is the date and time the user was added or dropped from the site + * @param source - interprets E, M, R, S, or null and returns the appropriate text from the bundle + * @param actionUser - User object for who performed the add/drop action + * @param childSiteId - Used for logging something specific with parent/child sites + */ + public EventLog(User user, String roleName, String actionTaken, Date auditStamp, String source, User actionUser, String childSiteId) { + this.user = user; + this.roleName = roleName; + this.actionTaken = actionTaken; + this.auditStamp = auditStamp; + this.source = source; + this.actionUser = actionUser; + this.childSiteId = childSiteId; + } + + public String getActionTaken() { + return actionTaken; + } + + public String getActionText() { + if (rosterAuditService.ROSTER_AUDIT_ACTION_ADD.equals(actionTaken)) + { + actionText = LocaleUtil.getLocalizedString(FacesContext.getCurrentInstance(),ServicesBean.MESSAGE_BUNDLE, "event_log_add"); + } + else if (rosterAuditService.ROSTER_AUDIT_ACTION_REMOVE.equals(actionTaken)) + { + actionText = LocaleUtil.getLocalizedString(FacesContext.getCurrentInstance(),ServicesBean.MESSAGE_BUNDLE, "event_log_remove"); + } + else if (rosterAuditService.ROSTER_AUDIT_ACTION_UPDATE.equals(actionTaken)) + { + actionText = LocaleUtil.getLocalizedString(FacesContext.getCurrentInstance(),ServicesBean.MESSAGE_BUNDLE, "event_log_update"); + } + return actionText; + } + + public User getActionUser() { + return actionUser; + } + + public String getAuditStamp() { + DateFormat df = DateFormat.getDateTimeInstance(); + return df.format(auditStamp); + } + + public String getChildSiteId() { + return childSiteId; + } + + public String getRoleName() { + return roleName; + } + + public String getSource() { + return source; + } + + public String getSourceText() { + for(RosterAudit ra : rosterAuditService.getRegisteredItems()) + { + if (ra.getDatabaseSourceKey().equals(source)) + { + String[] params = new String[] {actionUser.getSortName(), actionUser.getEid()}; + sourceText = ra.getSourceText(params); + break; + } + else + { + // if we didn't find an appropriate source, use the not available option + sourceText = LocaleUtil.getLocalizedString(FacesContext.getCurrentInstance(),ServicesBean.MESSAGE_BUNDLE, "event_log_not_available"); + } + } + + return sourceText; + } + + public User getUser() { + return user; + } + + public String getUserDisplayName() { + userDisplayName = user.getSortName(); + return userDisplayName; + } + + public void setActionTaken(String actionTaken) { + this.actionTaken = actionTaken; + } + + public void setActionText(String actionText) { + this.actionText = actionText; + } + + public void setActionUser(User actionUser) { + this.actionUser = actionUser; + } + + public void setAuditStamp(Date auditStamp) { + this.auditStamp = auditStamp; + } + + public void setChildSiteId(String childSiteId) { + this.childSiteId = childSiteId; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public void setSource(String source) { + this.source = source; + } + + public void setSourceText(String sourceText) { + this.sourceText = sourceText; + } + + public void setUser(User user) { + this.user = user; + } + + public void setUserDisplayName(String userDisplayName) { + this.userDisplayName = userDisplayName; + } + } + + public List getEventLog() { + return eventLog; + } + + private void getEvents() + { + if (this.eventLog == null || this.eventLog.isEmpty()) + { + eventLog = new ArrayList(); + Connection conn = null; + Statement statement = null; + ResultSet result = null; + String sql = ""; + String siteId = getSiteContext(); + try + { + conn = sqlService.borrowConnection(); + statement = conn.createStatement(); + sql = "select * from ROSTER_AUDITS where SITE_ID = '" + siteId + "' order by AUDIT_STAMP desc"; + result = statement.executeQuery(sql); + while (result.next()) + { + String userId = result.getString("USER_ID"); + String roleName = result.getString("ROLE_NAME"); + String actionTaken = result.getString("ACTION_TAKEN"); + Timestamp auditStamp = result.getTimestamp("AUDIT_STAMP"); + String source = result.getString("SOURCE"); + String actionUserId = result.getString("ACTION_USER_ID"); + String childSiteId = result.getString("CHILD_SITE_ID"); + + User cachedUser; + if (userMap.containsKey(userId)) + { + cachedUser = userMap.get(userId); + } + else + { + cachedUser = filter.services.userDirectoryService.getUserByEid(userId); + userMap.put(userId, cachedUser); + } + + if (actionUserId!=null && !"".equals(actionUserId)) + { + User cachedActionUser; + if (userMap.containsKey(actionUserId)) + { + cachedActionUser = userMap.get(actionUserId); + } + else + { + cachedActionUser = filter.services.userDirectoryService.getUserByEid(actionUserId); + userMap.put(actionUserId, cachedActionUser); + } + eventLog.add(new EventLog(cachedUser,roleName,actionTaken,auditStamp,source,cachedActionUser,childSiteId)); + } + else + { + eventLog.add(new EventLog(cachedUser,roleName,actionTaken,auditStamp,source,null,childSiteId)); + } + } + } + catch (UserNotDefinedException e) + { + log.warn("ERROR getting the roster audit logs!", e); + } + catch (SQLException e) + { + log.warn("ERROR getting the roster audit logs!", e); + } + finally + { + try + { + if (result!=null) + { + result.close(); + } + } + catch (SQLException e) + { + log.warn("Error trying to close the result set in the Roster Event Log!", e); + } + try + { + if (statement!=null) + { + statement.close(); + } + } + catch (SQLException e) + { + log.warn("Error trying to close the statement in the Roster Event Log!", e); + } + try + { + if (conn!=null) + { + conn.close(); + } + } + catch (SQLException e) + { + log.warn("Error trying to close the database connection in the Roster Event Log!", e); + } + } + } + this.totalItems = eventLog.size(); + } + + public String getInitValues() { + getEvents(); + + if (eventLog != null && eventLog.size() >= 1) { + Collections.sort(eventLog, getComparatorEL()); + if(!isSortAscending()) { + Collections.reverse(eventLog); + } + } + + return ""; + } + + public String getPageTitle() { + filter.services.eventTrackingService.post(filter.services.eventTrackingService.newEvent("roster.view",getSiteReference(),false)); + return LocaleUtil.getLocalizedString(FacesContext.getCurrentInstance(), + ServicesBean.MESSAGE_BUNDLE, "title_event_log"); + } + + public String getSortColumn() { + if (this.sortColumn == null) { + this.sortColumn = "auditStamp"; + } + return this.sortColumn; + } + + public boolean isExportablePage() { + return false; + } + + public boolean isSortAscending() { + return sortAscending; + } + + public void setEventLog(List eventLog) { + this.eventLog = eventLog; + } + + public void setSortAscending(boolean sortAscending) { + this.sortAscending = sortAscending; + } + + public void setSortColumn(String sortColumn) { + this.sortColumn = sortColumn; + } + + 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; + } +} Index: roster/roster-app/src/webapp/WEB-INF/faces-config.xml =================================================================== --- roster/roster-app/src/webapp/WEB-INF/faces-config.xml (revision 130931) +++ roster/roster-app/src/webapp/WEB-INF/faces-config.xml (working copy) @@ -60,6 +60,11 @@ /roster/groupMembership.jsp + + eventLog + /roster/eventLog.jsp + + @@ -251,6 +256,16 @@ #{prefs} + + + eventLog + org.sakaiproject.tool.roster.RosterEventLog + session + + filter + #{filter} + + Index: roster/roster-app/src/webapp/roster/eventLog.jsp =================================================================== --- roster/roster-app/src/webapp/roster/eventLog.jsp (revision 0) +++ roster/roster-app/src/webapp/roster/eventLog.jsp (working copy) @@ -0,0 +1,89 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> +<%@ taglib uri="http://sakaiproject.org/jsf/sakai" prefix="sakai"%> +<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%> +<% +response.setContentType("text/html; charset=UTF-8"); +%> + + + + + + <%=""%> + + + <%@include file="inc/nav.jspf" %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: roster/roster-app/src/webapp/roster/inc/nav.jspf =================================================================== --- roster/roster-app/src/webapp/roster/inc/nav.jspf (revision 130931) +++ roster/roster-app/src/webapp/roster/inc/nav.jspf (working copy) @@ -3,6 +3,7 @@ + Index: roster/roster-impl/src/java/org/sakaiproject/component/app/roster/RosterManagerImpl.java =================================================================== --- roster/roster-impl/src/java/org/sakaiproject/component/app/roster/RosterManagerImpl.java (revision 130931) +++ roster/roster-impl/src/java/org/sakaiproject/component/app/roster/RosterManagerImpl.java (working copy) @@ -48,6 +48,7 @@ import org.sakaiproject.authz.api.GroupNotDefinedException; import org.sakaiproject.authz.api.Member; import org.sakaiproject.authz.api.SecurityService; +import org.sakaiproject.component.api.ServerConfigurationService; import org.sakaiproject.coursemanagement.api.CourseManagementService; import org.sakaiproject.coursemanagement.api.Section; import org.sakaiproject.event.api.EventTrackingService; @@ -76,6 +77,7 @@ public abstract SecurityService securityService(); public abstract CourseManagementService cmService(); public abstract EventTrackingService eventTrackingService(); + public abstract ServerConfigurationService serverConfigurationService(); public void init() { log.info("init()"); @@ -564,4 +566,9 @@ return false; return true; } + + public boolean isEventLogViewable(String siteId) { + boolean eventLog = "true".equals(serverConfigurationService().getConfig("roster_event_log", "true")); + return (eventLog && userHasSitePermission(userDirectoryService().getCurrentUser(),"site.upd")); + } } Index: roster/roster-impl/src/java/org/sakaiproject/component/app/roster/RosterManagerTestImpl.java =================================================================== --- roster/roster-impl/src/java/org/sakaiproject/component/app/roster/RosterManagerTestImpl.java (revision 130931) +++ roster/roster-impl/src/java/org/sakaiproject/component/app/roster/RosterManagerTestImpl.java (working copy) @@ -25,6 +25,7 @@ import org.sakaiproject.authz.api.AuthzGroupService; import org.sakaiproject.authz.api.FunctionManager; import org.sakaiproject.authz.api.SecurityService; +import org.sakaiproject.component.api.ServerConfigurationService; import org.sakaiproject.coursemanagement.api.CourseManagementService; import org.sakaiproject.section.api.SectionAwareness; import org.sakaiproject.site.api.SiteService; @@ -100,6 +101,12 @@ // TODO Auto-generated method stub return null; } + + @Override + public ServerConfigurationService serverConfigurationService() { + // TODO Auto-generated method stub + return null; + } } Index: site-manage/site-manage-impl/impl/src/bundle/RosterAudit.properties =================================================================== --- site-manage/site-manage-impl/impl/src/bundle/RosterAudit.properties (revision 0) +++ site-manage/site-manage-impl/impl/src/bundle/RosterAudit.properties (working copy) @@ -0,0 +1,2 @@ +M = {0} ({1}) +S = Self \ No newline at end of file Index: site-manage/site-manage-impl/impl/src/java/org/sakaiproject/sitemanage/impl/RosterAuditSiteManageImpl.java =================================================================== --- site-manage/site-manage-impl/impl/src/java/org/sakaiproject/sitemanage/impl/RosterAuditSiteManageImpl.java (revision 0) +++ site-manage/site-manage-impl/impl/src/java/org/sakaiproject/sitemanage/impl/RosterAuditSiteManageImpl.java (working copy) @@ -0,0 +1,33 @@ +/********************************************************************************** + * $URL$ + * $Id$ + *********************************************************************************** + * + * Copyright (c) 2010 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.sitemanage.impl; + +import org.sakaiproject.util.ResourceLoader; +import org.sakaiproject.util.RosterAuditImpl; + +public class RosterAuditSiteManageImpl extends RosterAuditImpl { + + public ResourceLoader getResourceLoader(String location) { + return new ResourceLoader(location); + } + +} Index: site-manage/site-manage-impl/pack/src/webapp/WEB-INF/components.xml =================================================================== --- site-manage/site-manage-impl/pack/src/webapp/WEB-INF/components.xml (revision 130931) +++ site-manage/site-manage-impl/pack/src/webapp/WEB-INF/components.xml (working copy) @@ -83,5 +83,22 @@ bean="org.sakaiproject.springframework.orm.hibernate.GlobalSessionFactory" /> + + + RosterAudit + M + true + + + + RosterAudit + S + Index: site-manage/site-manage-participant-helper/src/java/org/sakaiproject/site/tool/helper/participant/impl/SiteAddParticipantHandler.java =================================================================== --- site-manage/site-manage-participant-helper/src/java/org/sakaiproject/site/tool/helper/participant/impl/SiteAddParticipantHandler.java (revision 130931) +++ site-manage/site-manage-participant-helper/src/java/org/sakaiproject/site/tool/helper/participant/impl/SiteAddParticipantHandler.java (working copy) @@ -23,8 +23,11 @@ import org.sakaiproject.authz.api.Role; import org.sakaiproject.authz.api.SecurityService; import org.sakaiproject.component.api.ServerConfigurationService; +import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.event.cover.EventTrackingService; import org.sakaiproject.exception.IdUnusedException; +import org.sakaiproject.site.api.RosterAudit; +import org.sakaiproject.site.api.RosterAuditService; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SiteService; import org.sakaiproject.site.util.Participant; @@ -84,6 +87,8 @@ public SessionManager sessionManager = null; public ServerConfigurationService serverConfigurationService; private final String HELPER_ID = "sakai.tool.helper.id"; + private static RosterAudit rosterAudit = (RosterAudit) ComponentManager.get("org.sakaiproject.site.api.RosterAudit.sitemanage"); + private static RosterAuditService rosterAuditService = (RosterAuditService) ComponentManager.get(RosterAuditService.class); public MessageLocator messageLocator; @@ -539,6 +544,10 @@ AuthzGroup realmEdit = authzGroupService.getAuthzGroup(realmId); boolean allowUpdate = authzGroupService.allowUpdate(realmId); SetokRoles = new HashSet(); + + // List used for roster auditing + List rosterAuditList = new ArrayList(); + for (UserRoleEntry entry: userRoleEntries) { String eId = entry.userEId; String role =entry.role; @@ -573,6 +582,11 @@ false); addedUserEIds.add(eId); addedUserInfos.add("uid=" + user.getId() + ";role=" + role + ";active=" + statusChoice.equals("active") + ";provided=false"); + + // Add the user to the list for the Roster Event Log + String currentUserId = userDirectoryService.getUserEid(sessionManager.getCurrentSessionUserId()); + String[] rosterAuditString = {site.getId(),eId,role,rosterAuditService.ROSTER_AUDIT_ACTION_ADD,rosterAudit.getDatabaseSourceKey(),currentUserId,null}; + rosterAuditList.add(rosterAuditString); // send notification if (notify) { @@ -591,6 +605,13 @@ try { authzGroupService.save(realmEdit); + + // do the audit logging - here won't be 100% accurate, but is nice just doing one call and will be close enough. + if (!rosterAuditList.isEmpty()) + { + rosterAudit.addToRosterAuditing(rosterAuditList); + } + // post event about adding participant EventTrackingService.post(EventTrackingService.newEvent(SiteService.SECURE_UPDATE_SITE_MEMBERSHIP, realmEdit.getId(),false)); Index: site-manage/site-manage-tool/tool/src/java/org/sakaiproject/site/tool/MembershipAction.java =================================================================== --- site-manage/site-manage-tool/tool/src/java/org/sakaiproject/site/tool/MembershipAction.java (revision 130931) +++ site-manage/site-manage-tool/tool/src/java/org/sakaiproject/site/tool/MembershipAction.java (working copy) @@ -31,14 +31,19 @@ import org.sakaiproject.cheftool.PagedResourceActionII; import org.sakaiproject.cheftool.RunData; import org.sakaiproject.cheftool.VelocityPortlet; +import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.component.cover.ServerConfigurationService; import org.sakaiproject.event.api.SessionState; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.InUseException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.javax.PagingPosition; +import org.sakaiproject.site.api.RosterAudit; +import org.sakaiproject.site.api.RosterAuditService; import org.sakaiproject.site.cover.SiteService; import org.sakaiproject.site.util.SiteTextEditUtil; +import org.sakaiproject.user.api.User; +import org.sakaiproject.user.api.UserDirectoryService; import org.sakaiproject.util.ResourceLoader; /** @@ -63,6 +68,12 @@ private static String SEARCH_TERM = "search"; private static final String STATE_TOP_PAGE_MESSAGE = "msg-top"; + + private static final String STATE_CM_CURRENT_USERID = "site_cm_current_userId"; + + private static RosterAudit rosterAudit = (RosterAudit) ComponentManager.get("org.sakaiproject.site.api.RosterAudit.membership"); + private static RosterAuditService rosterAuditService = (RosterAuditService) ComponentManager.get(RosterAuditService.class); + private static UserDirectoryService userDirectoryService = (UserDirectoryService) ComponentManager.get(UserDirectoryService.class); /* * (non-Javadoc) @@ -396,6 +407,17 @@ SiteService.join(id); String msg = rb.getString("mb.youhave2") + " " + SiteService.getSite(id).getTitle(); addAlert(state, msg); + + // add to roster auditing + List rosterAuditList = new ArrayList(); + String currentUserEid = userDirectoryService.getCurrentUser().getEid(); + String roleId = SiteService.getSite(id).getJoinerRole(); + String[] rosterAuditString = {id,currentUserEid,roleId,rosterAuditService.ROSTER_AUDIT_ACTION_ADD,rosterAudit.getDatabaseSourceKey(),currentUserEid,null}; + rosterAuditList.add(rosterAuditString); + if (!rosterAuditList.isEmpty()) + { + rosterAudit.addToRosterAuditing(rosterAuditList); + } } catch (IdUnusedException e) { @@ -431,13 +453,27 @@ if (id != null) { String msg = rb.getString("mb.youhave") + " "; + + // add to roster auditing + List rosterAuditList = new ArrayList(); + // get the User object since we need a couple of lookups + User tempUser = userDirectoryService.getCurrentUser(); + String currentUserId = tempUser.getId(); + String currentUserEid = tempUser.getEid(); + for(int i=0; i< id.length; i++){ try { + // Get the user's role before unjoining the site + String roleId = SiteService.getSite(id[i]).getUserRole(currentUserId).getId(); + SiteService.unjoin(id[i]); if (i>0) msg=msg+" ,"; msg = msg+SiteService.getSite(id[i]).getTitle(); + + String[] rosterAuditString = {id[i],currentUserEid,roleId,rosterAuditService.ROSTER_AUDIT_ACTION_REMOVE,rosterAudit.getDatabaseSourceKey(),currentUserEid,null}; + rosterAuditList.add(rosterAuditString); } catch (IdUnusedException ignore) { @@ -455,6 +491,10 @@ } } addAlert(state, msg); + if (!rosterAuditList.isEmpty()) + { + rosterAudit.addToRosterAuditing(rosterAuditList); + } } // TODO: hard coding this frame id is fragile, portal dependent, and needs to be fixed -ggolden Index: site-manage/site-manage-tool/tool/src/java/org/sakaiproject/site/tool/SiteAction.java =================================================================== --- site-manage/site-manage-tool/tool/src/java/org/sakaiproject/site/tool/SiteAction.java (revision 130931) +++ site-manage/site-manage-tool/tool/src/java/org/sakaiproject/site/tool/SiteAction.java (working copy) @@ -112,6 +112,8 @@ import org.sakaiproject.javax.PagingPosition; import org.sakaiproject.lti.api.LTIService; import org.sakaiproject.site.api.Group; +import org.sakaiproject.site.api.RosterAudit; +import org.sakaiproject.site.api.RosterAuditService; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SitePage; import org.sakaiproject.site.api.SiteService.SortType; @@ -760,6 +762,10 @@ private String m_filePath; private String moreInfoPath; private String libraryPath; + + private static RosterAudit rosterAudit = (RosterAudit) ComponentManager.get("org.sakaiproject.site.api.RosterAudit.sitemanage"); + private static RosterAuditService rosterAuditService = (RosterAuditService) ComponentManager.get(RosterAuditService.class); + /** * what are the tool ids within Home page? * If this is for a newly added Home tool, get the tool ids from template site or system set default @@ -7746,6 +7752,9 @@ // list of roles being added or removed HashSetroles = new HashSet(); + + // List used for roster auditing + List rosterAuditList = new ArrayList(); // remove all roles and then add back those that were checked for (int i = 0; i < participants.size(); i++) { @@ -7800,6 +7809,9 @@ } realmEdit.addMember(id, roleId, activeGrant, fromProvider); + String currentUserId = (String) state.getAttribute(STATE_CM_CURRENT_USERID); + String[] rosterAuditString = {s.getId(),participant.getEid(),roleId,rosterAuditService.ROSTER_AUDIT_ACTION_UPDATE,rosterAudit.getDatabaseSourceKey(),currentUserId,null}; + rosterAuditList.add(rosterAuditString); // construct the event string String userUpdatedString = "uid=" + id; @@ -7858,6 +7870,9 @@ } realmEdit.removeMember(userId); usersDeleted.add("uid=" + userId); + String currentUserId = (String) state.getAttribute(STATE_CM_CURRENT_USERID); + String[] rosterAuditString = {s.getId(),user.getEid(),role.getId(),rosterAuditService.ROSTER_AUDIT_ACTION_REMOVE,rosterAudit.getDatabaseSourceKey(),currentUserId,null}; + rosterAuditList.add(rosterAuditString); } } } @@ -7890,6 +7905,12 @@ } } AuthzGroupService.save(realmEdit); + + // do the audit logging - here won't be 100% accurate, but is nice just doing one call and will be close enough. + if (!rosterAuditList.isEmpty()) + { + rosterAudit.addToRosterAuditing(rosterAuditList); + } // then update all related group realms for the role doUpdate_related_group_participants(s, realmId);