/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/edu-services/trunk/sections-service/sections-impl/sakai/impl/src/java/org/sakaiproject/component/section/sakai/SectionAwarenessImpl.java $
* $Id: SectionAwarenessImpl.java 105077 2012-02-24 22:54:29Z ottenhoff@longsight.com $
***********************************************************************************
*
* Copyright (c) 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.component.section.sakai;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.section.api.SectionAwareness;
import org.sakaiproject.section.api.coursemanagement.Course;
import org.sakaiproject.section.api.coursemanagement.CourseSection;
import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord;
import org.sakaiproject.section.api.coursemanagement.ParticipationRecord;
import org.sakaiproject.section.api.coursemanagement.User;
import org.sakaiproject.section.api.facade.Role;
import org.sakaiproject.authz.api.AuthzGroup;
import org.sakaiproject.authz.api.FunctionManager;
import org.sakaiproject.authz.api.Member;
import org.sakaiproject.authz.api.SecurityService;
import org.sakaiproject.component.section.sakai.facade.SakaiUtil;
import org.sakaiproject.coursemanagement.api.CourseManagementService;
import org.sakaiproject.coursemanagement.api.SectionCategory;
import org.sakaiproject.entity.api.EntityManager;
import org.sakaiproject.entity.api.Reference;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.site.api.Group;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.user.api.UserDirectoryService;
import org.sakaiproject.user.api.UserNotDefinedException;
/**
* A sakai based implementation of the Section Awareness API, using the
* grouping capability of the framework as the persistence mechanism.
*
* @author <a href="mailto:jholtzman@berkeley.edu">Josh Holtzman</a>
*
*/
public class SectionAwarenessImpl implements SectionAwareness {
private static final Log log = LogFactory.getLog(SectionAwarenessImpl.class);
// Sakai services
protected SiteService siteService;
protected SecurityService securityService;
protected EntityManager entityManager;
protected FunctionManager functionManager;
protected UserDirectoryService userDirectoryService;
protected CourseManagementService courseManagementService;
/**
* Bean initialization (called by spring) registers authorization functions
* with the AuthzGroup system.
*/
public void init() {
if(log.isInfoEnabled()) log.info("init()");
functionManager.registerFunction("section.role.student");
functionManager.registerFunction("section.role.ta");
functionManager.registerFunction("section.role.instructor");
}
public void destroy() {
if(log.isInfoEnabled()) log.info("destroy()");
}
/**
* @inheritDoc
*/
public List getSections(final String siteContext) {
if(log.isDebugEnabled()) log.debug("Getting sections for context " + siteContext);
List<CourseSectionImpl> sectionList = new ArrayList<CourseSectionImpl>();
Collection<Group> sections;
try {
sections = siteService.getSite(siteContext).getGroups();
} catch (IdUnusedException e) {
log.error("No site with id = " + siteContext);
return sectionList;
}
for(Iterator<Group> iter = sections.iterator(); iter.hasNext();) {
Group group = (Group)iter.next();
sectionList.add(new CourseSectionImpl(group));
}
Collections.sort(sectionList);
return sectionList;
}
/**
* @inheritDoc
*/
public List<String> getSectionCategories(String siteContext) {
List<String> catCodes = new ArrayList<String>();
for(Iterator<String> iter = courseManagementService.getSectionCategories().iterator(); iter.hasNext();) {
catCodes.add(iter.next());
}
return catCodes;
}
/**
* @inheritDoc
*/
public CourseSection getSection(final String sectionUuid) {
Group group;
group = siteService.findGroup(sectionUuid);
if(group == null) {
log.error("Unable to find section " + sectionUuid);
return null;
}
return new CourseSectionImpl(group);
}
/**
* @inheritDoc
*/
public List getSiteMembersInRole(final String siteContext, final Role role) {
if(role.isInstructor()) {
return getSiteInstructors(siteContext);
} else if(role.isTeachingAssistant()) {
return getSiteTeachingAssistants(siteContext);
} else if(role.isStudent()) {
return getSiteEnrollments(siteContext);
} else {
log.error("Can not get site members in role " + role);
return new ArrayList();
}
}
private List getSiteInstructors(String siteContext) {
Course course = getCourse(siteContext);
if(course == null) {
return new ArrayList();
}
List sakaiMembers = securityService.unlockUsers(SectionAwareness.INSTRUCTOR_MARKER,
course.getUuid());
List<InstructorRecordImpl> membersList = new ArrayList<InstructorRecordImpl>();
for(Iterator iter = sakaiMembers.iterator(); iter.hasNext();) {
org.sakaiproject.user.api.User sakaiUser = (org.sakaiproject.user.api.User)iter.next();
User user = SakaiUtil.convertUser(sakaiUser);
if (user != null) {
InstructorRecordImpl record = new InstructorRecordImpl(course, user);
membersList.add(record);
}
}
return membersList;
}
private List getSiteEnrollments(String siteContext) {
Course course = getCourse(siteContext);
if(course == null) {
log.error("Could not find course site " + siteContext);
return new ArrayList();
}
List sakaiMembers = securityService.unlockUsers(SectionAwareness.STUDENT_MARKER, course.getUuid());
if(log.isDebugEnabled()) log.debug("Site students size = " + sakaiMembers.size());
List<EnrollmentRecordImpl> membersList = new ArrayList<EnrollmentRecordImpl>();
for(Iterator iter = sakaiMembers.iterator(); iter.hasNext();) {
org.sakaiproject.user.api.User sakaiUser = (org.sakaiproject.user.api.User)iter.next();
User user = SakaiUtil.convertUser(sakaiUser);
if (user != null) {
EnrollmentRecordImpl record = new EnrollmentRecordImpl(course, null, user);
membersList.add(record);
}
}
return membersList;
}
private List getSiteTeachingAssistants(String siteContext) {
Course course = getCourse(siteContext);
if(course == null) {
return new ArrayList();
}
List sakaiMembers = securityService.unlockUsers(SectionAwareness.TA_MARKER, course.getUuid());
if(log.isDebugEnabled()) log.debug("Site TAs size = " + sakaiMembers.size());
List<TeachingAssistantRecordImpl> membersList = new ArrayList<TeachingAssistantRecordImpl>();
for(Iterator iter = sakaiMembers.iterator(); iter.hasNext();) {
org.sakaiproject.user.api.User sakaiUser = (org.sakaiproject.user.api.User)iter.next();
User user = SakaiUtil.convertUser(sakaiUser);
if (user != null) {
TeachingAssistantRecordImpl record = new TeachingAssistantRecordImpl(course, user);
membersList.add(record);
}
}
return membersList;
}
private Course getCourse(final String siteContext) {
if(log.isDebugEnabled()) log.debug("Getting course for context " + siteContext);
Site site;
try {
site = siteService.getSite(siteContext);
} catch (IdUnusedException e) {
log.error("Could not find site with id = " + siteContext);
return null;
}
return new CourseImpl(site);
}
/**
* @inheritDoc
*/
public List findSiteMembersInRole(final String siteContext, final Role role, final String pattern) {
List fullList = getSiteMembersInRole(siteContext, role);
List<ParticipationRecord> filteredList = new ArrayList<ParticipationRecord>();
for(Iterator iter = fullList.iterator(); iter.hasNext();) {
ParticipationRecord record = (ParticipationRecord)iter.next();
User user = record.getUser();
if(user.getDisplayName().toLowerCase().startsWith(pattern.toLowerCase()) ||
user.getSortName().toLowerCase().startsWith(pattern.toLowerCase()) ||
user.getDisplayId().toLowerCase().startsWith(pattern.toLowerCase())) {
filteredList.add(record);
}
}
return filteredList;
}
/**
* @inheritDoc
*/
public boolean isSiteMemberInRole(String siteContext, String userUid, Role role) {
String authzRef = getSiteReference(siteContext);
return isUserInRole(userUid, role, authzRef);
}
/**
* Checks whether a user is in a role in any learningContext (site or section)
*
* @param userUid
* @param role
* @param authzRef
* @return
*/
private boolean isUserInRole(String userUid, Role role, String authzRef) {
org.sakaiproject.user.api.User user;
try {
user = userDirectoryService.getUser(userUid);
} catch (UserNotDefinedException ide) {
log.error("Could not find user with id " + userUid);
return false;
}
if(role.isNone()) {
// Make sure that the user is in fact NOT in any role with a role marker
if(securityService.unlock(user, SectionAwareness.INSTRUCTOR_MARKER, authzRef) ||
securityService.unlock(user, SectionAwareness.STUDENT_MARKER, authzRef) ||
securityService.unlock(user, SectionAwareness.TA_MARKER, authzRef)) {
return false;
} else {
return true;
}
}
return securityService.unlock(user, getLock(role), authzRef);
}
private String getSiteReference(String siteContext) {
final Reference ref = entityManager.newReference(siteService.siteReference(siteContext));
return ref.getReference();
}
/**
* @inheritDoc
*/
public List getSectionMembers(final String sectionUuid) {
Group group = siteService.findGroup(sectionUuid);
CourseSection section = new CourseSectionImpl(group);
Set taRoles = getSectionTaRoles(group);
Set studentRoles = getSectionStudentRoles(group);
Set members = group.getMembers();
List<ParticipationRecord> sectionMembershipRecords = new ArrayList<ParticipationRecord>();
for(Iterator iter = members.iterator(); iter.hasNext();) {
Member member = (Member)iter.next();
String roleString = member.getRole().getId();
User user = SakaiUtil.getUserFromSakai(member.getUserId());
if (user != null) {
ParticipationRecord record = null;
if(taRoles.contains(roleString)) {
record = new TeachingAssistantRecordImpl(section, user);
} else if(studentRoles.contains(roleString)) {
record = new EnrollmentRecordImpl(section, null, user);
}
if(record != null) {
sectionMembershipRecords.add(record);
}
}
}
return sectionMembershipRecords;
}
/**
* Gets the group-scoped role to use when adding a student to a group.
*
* @param group The authzGroup
* @return The role id, or null if there is not role with the student marker.
*/
private Set getSectionStudentRoles(AuthzGroup group) {
return group.getRolesIsAllowed(SectionAwareness.STUDENT_MARKER);
}
/**
* Gets the group-scoped role to use when adding a TA to a group.
*
* @param group The authzGroup
* @return The role id, or null if there is not role with the TA marker.
*/
private Set getSectionTaRoles(Group group) {
return group.getRolesIsAllowed(SectionAwareness.TA_MARKER);
}
/**
* @inheritDoc
*/
public List getSectionMembersInRole(final String sectionUuid, final Role role) {
if(role.isTeachingAssistant()) {
return getSectionTeachingAssistants(sectionUuid);
} else if(role.isStudent()) {
return getSectionEnrollments(sectionUuid);
} else {
log.error("Can't get section members in role " + role);
return new ArrayList();
}
}
private List getSectionEnrollments(String sectionUuid) {
Group group = siteService.findGroup(sectionUuid);
CourseSection section = getSection(sectionUuid);
if(section == null) {
return new ArrayList();
}
if(log.isDebugEnabled()) log.debug("Getting section enrollments in " + sectionUuid);
Set studentRoles = getSectionStudentRoles(group);
if(studentRoles == null || studentRoles.isEmpty()) {
return new ArrayList();
}
Set<String> sakaiUserUids = new HashSet<String>();
for(Iterator iter = studentRoles.iterator(); iter.hasNext();) {
String role = (String)iter.next();
sakaiUserUids.addAll(group.getUsersHasRole(role));
}
List sakaiUsers = userDirectoryService.getUsers(sakaiUserUids);
List<EnrollmentRecord> membersList = new ArrayList<EnrollmentRecord>();
for(Iterator iter = sakaiUsers.iterator(); iter.hasNext();) {
User user = SakaiUtil.convertUser((org.sakaiproject.user.api.User) iter.next());
if (user != null) {
EnrollmentRecordImpl record = new EnrollmentRecordImpl(section, null, user);
membersList.add(record);
}
}
return membersList;
}
private List getSectionTeachingAssistants(String sectionUuid) {
Group group = siteService.findGroup(sectionUuid);
CourseSection section = getSection(sectionUuid);
if(section == null) {
return new ArrayList();
}
if(log.isDebugEnabled()) log.debug("Getting section enrollments in " + sectionUuid);
Set taRoles = getSectionTaRoles(group);
if(taRoles == null || taRoles.isEmpty()) {
if(log.isDebugEnabled()) log.debug("There is no role for TAs in this site... returning an empty list");
return new ArrayList();
}
Set sakaiUserUids = new HashSet();
for(Iterator iter = taRoles.iterator(); iter.hasNext();) {
String role = (String)iter.next();
sakaiUserUids.addAll(group.getUsersHasRole(role));
}
List sakaiUsers = userDirectoryService.getUsers(sakaiUserUids);
List<TeachingAssistantRecordImpl> membersList = new ArrayList<TeachingAssistantRecordImpl>();
for(Iterator iter = sakaiUsers.iterator(); iter.hasNext();) {
User user = SakaiUtil.convertUser((org.sakaiproject.user.api.User) iter.next());
if (user != null) {
TeachingAssistantRecordImpl record = new TeachingAssistantRecordImpl(section, user);
membersList.add(record);
}
}
return membersList;
}
//////////////////////////////////////////////////
/**
* @inheritDoc
*/
public boolean isSectionMemberInRole(final String sectionUuid, final String userUid, final Role role) {
return isUserInRole(userUid, role, sectionUuid);
}
/**
* @inheritDoc
*/
public String getSectionName(final String sectionUuid) {
CourseSection section = getSection(sectionUuid);
return section.getTitle();
}
/**
* @inheritDoc
*/
public String getSectionCategory(final String sectionUuid) {
CourseSection section = getSection(sectionUuid);
return section.getCategory();
}
/**
* @inheritDoc
*/
public List getSectionsInCategory(final String siteContext, final String categoryId) {
if(log.isDebugEnabled()) log.debug("Getting " + categoryId +
" sections for context " + siteContext);
List<CourseSection> sectionList = new ArrayList<CourseSection>();
Collection groups;
try {
groups = siteService.getSite(siteContext).getGroups();
} catch (IdUnusedException e) {
log.error("No site with id = " + siteContext);
return new ArrayList();
}
for(Iterator iter = groups.iterator(); iter.hasNext();) {
Group group = (Group)iter.next();
if(categoryId.equals(group.getProperties().getProperty(CourseSectionImpl.CATEGORY))) {
sectionList.add(new CourseSectionImpl(group));
}
}
return sectionList;
}
/**
* @inheritDoc
*/
public String getCategoryName(String categoryId, Locale locale) {
String description = courseManagementService.getSectionCategoryDescription(categoryId);
if(description == null) {
return categoryId;
}
return description;
}
/**
* @inheritDoc
*/
public List getUnassignedMembersInRole(final String siteContext, final Role role) {
List siteMembers = getSiteMembersInRole(siteContext, role);
// Get all userUids of all users in sections
List<String> sectionedUserUids = new ArrayList<String>();
List sections = getSections(siteContext);
for(Iterator sectionIter = sections.iterator(); sectionIter.hasNext();) {
CourseSection section = (CourseSection)sectionIter.next();
List sectionUsers = securityService.unlockUsers(getLock(role), section.getUuid());
for(Iterator userIter = sectionUsers.iterator(); userIter.hasNext();) {
org.sakaiproject.user.api.User user = (org.sakaiproject.user.api.User)userIter.next();
sectionedUserUids.add(user.getId());
}
}
// Now generate the list of unsectioned enrollments by subtracting the two collections
// Since the APIs return different kinds of objects, we need to iterate
List<ParticipationRecord> unsectionedMembers = new ArrayList<ParticipationRecord>();
for(Iterator iter = siteMembers.iterator(); iter.hasNext();) {
ParticipationRecord record = (ParticipationRecord)iter.next();
if(! sectionedUserUids.contains(record.getUser().getUserUid())) {
unsectionedMembers.add(record);
}
}
return unsectionedMembers;
}
private String getLock(Role role) {
if(role.isInstructor()) {
return SectionAwareness.INSTRUCTOR_MARKER;
} else if(role.isTeachingAssistant()) {
return SectionAwareness.TA_MARKER;
} else if(role.isStudent()) {
return SectionAwareness.STUDENT_MARKER;
} else {
return null;
}
}
// Dependency injection
public void setSiteService(SiteService siteService) {
this.siteService = siteService;
}
public void setFunctionManager(FunctionManager functionManager) {
this.functionManager = functionManager;
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
public void setUserDirectoryService(UserDirectoryService userDirectoryService) {
this.userDirectoryService = userDirectoryService;
}
public void setCourseManagementService(CourseManagementService courseManagementService) {
this.courseManagementService = courseManagementService;
}
}