0001: /**********************************************************************************
0002: * $URL: https://source.sakaiproject.org/svn/sections/tags/sakai_2-4-1/sections-impl/sakai/impl/src/java/org/sakaiproject/component/section/sakai/SectionManagerImpl.java $
0003: * $Id: SectionManagerImpl.java 22202 2007-03-05 23:42:44Z jholtzman@berkeley.edu $
0004: ***********************************************************************************
0005: *
0006: * Copyright (c) 2005, 2006 The Regents of the University of California and The Regents of the University of Michigan
0007: *
0008: * Licensed under the Educational Community License, Version 1.0 (the "License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.opensource.org/licenses/ecl1.php
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: **********************************************************************************/package org.sakaiproject.component.section.sakai;
0021:
0022: import java.sql.Time;
0023: import java.util.ArrayList;
0024: import java.util.Arrays;
0025: import java.util.Collection;
0026: import java.util.HashSet;
0027: import java.util.Iterator;
0028: import java.util.List;
0029: import java.util.Locale;
0030: import java.util.Set;
0031:
0032: import org.apache.commons.lang.StringUtils;
0033: import org.apache.commons.logging.Log;
0034: import org.apache.commons.logging.LogFactory;
0035: import org.sakaiproject.tool.api.SessionManager;
0036: import org.sakaiproject.section.api.SectionAwareness;
0037: import org.sakaiproject.section.api.SectionManager;
0038: import org.sakaiproject.section.api.coursemanagement.Course;
0039: import org.sakaiproject.section.api.coursemanagement.CourseSection;
0040: import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord;
0041: import org.sakaiproject.section.api.coursemanagement.Meeting;
0042: import org.sakaiproject.section.api.coursemanagement.ParticipationRecord;
0043: import org.sakaiproject.section.api.coursemanagement.SectionEnrollments;
0044: import org.sakaiproject.section.api.coursemanagement.User;
0045: import org.sakaiproject.section.api.exception.MembershipException;
0046: import org.sakaiproject.section.api.exception.RoleConfigurationException;
0047: import org.sakaiproject.section.api.facade.Role;
0048: import org.sakaiproject.component.cover.ComponentManager;
0049: import org.sakaiproject.component.section.sakai.facade.SakaiUtil;
0050: import org.sakaiproject.coursemanagement.api.CourseManagementService;
0051: import org.sakaiproject.coursemanagement.api.Section;
0052: import org.sakaiproject.coursemanagement.api.exception.IdNotFoundException;
0053: import org.sakaiproject.exception.IdUnusedException;
0054: import org.sakaiproject.exception.PermissionException;
0055: import org.sakaiproject.authz.api.AuthzGroup;
0056: import org.sakaiproject.authz.api.AuthzGroupService;
0057: import org.sakaiproject.authz.api.AuthzPermissionException;
0058: import org.sakaiproject.authz.api.GroupNotDefinedException;
0059: import org.sakaiproject.authz.api.GroupProvider;
0060: import org.sakaiproject.authz.api.Member;
0061: import org.sakaiproject.entity.api.EntityManager;
0062: import org.sakaiproject.entity.api.Reference;
0063: import org.sakaiproject.entity.api.ResourceProperties;
0064: import org.sakaiproject.event.api.Event;
0065: import org.sakaiproject.event.api.EventTrackingService;
0066: import org.sakaiproject.authz.api.SecurityService;
0067: import org.sakaiproject.site.api.Group;
0068: import org.sakaiproject.site.api.Site;
0069: import org.sakaiproject.site.api.SiteAdvisor;
0070: import org.sakaiproject.site.api.SiteService;
0071: import org.sakaiproject.user.api.UserDirectoryService;
0072: import org.sakaiproject.user.api.UserNotDefinedException;
0073:
0074: /**
0075: * A sakai-based implementation of the Section Management API, using the
0076: * new grouping capability of the framework.
0077: *
0078: * @author <a href="mailto:jholtzman@berkeley.edu">Josh Holtzman</a>
0079: *
0080: */
0081: public abstract class SectionManagerImpl implements SectionManager,
0082: SiteAdvisor {
0083:
0084: private static final Log log = LogFactory
0085: .getLog(SectionManagerImpl.class);
0086:
0087: // Sakai services set by method injection
0088: protected abstract SiteService siteService();
0089:
0090: // Sakai services set by dependency injection
0091: protected AuthzGroupService authzGroupService;
0092: protected GroupProvider groupProvider;
0093: protected SecurityService securityService;
0094: protected UserDirectoryService userDirectoryService;
0095: protected SessionManager sessionManager;
0096: protected EntityManager entityManager;
0097: protected EventTrackingService eventTrackingService;
0098: protected CourseManagementService courseManagementService;
0099:
0100: // Configuration setting
0101: protected ExternalIntegrationConfig config;
0102:
0103: /**
0104: * Initialization called once all dependencies are set.
0105: */
0106: public void init() {
0107: if (log.isInfoEnabled())
0108: log.info("init()");
0109: siteService().addSiteAdvisor(this );
0110:
0111: // A group provider may not exist, so we can't use spring to inject it
0112: groupProvider = (GroupProvider) ComponentManager
0113: .get(GroupProvider.class);
0114: }
0115:
0116: /**
0117: * Cleans up any resources in use before destroying this service.
0118: */
0119: public void destroy() {
0120: if (log.isInfoEnabled())
0121: log.info("destroy()");
0122: siteService().removeSiteAdvisor(this );
0123: }
0124:
0125: // SiteAdvisor methods
0126:
0127: /**
0128: * {@inheritDoc}
0129: */
0130: public void update(Site site) {
0131: // NOTE: This code will be called any time a site is saved (including site creation).
0132: // Be very careful...
0133:
0134: // If we're on a non-course site, do nothing
0135: if (!"course".equalsIgnoreCase(site.getType())) {
0136: if (log.isDebugEnabled())
0137: log.debug("SiteAdvisor "
0138: + this .getClass().getCanonicalName()
0139: + " ignoring site " + site.getTitle()
0140: + ", which is not a course site");
0141: return;
0142: }
0143:
0144: // Get our app config and the site properties
0145: ExternalIntegrationConfig appConfig = getConfiguration(null);
0146: ResourceProperties siteProps = site.getProperties();
0147:
0148: // If we're configured to be mandatory auto or mandatory manual, handle those conditions and return
0149: if (handlingMandatoryConfigs(appConfig, site)) {
0150: if (log.isDebugEnabled())
0151: log.debug(this .getClass().getCanonicalName()
0152: + " finished decorating site "
0153: + site.getTitle() + " for " + appConfig);
0154: return;
0155: }
0156:
0157: // Set the defaults for non-mandatory sites
0158: setSiteDefaults(site, appConfig, siteProps);
0159:
0160: // If this site is manually managed, it could have been just changed to be manual.
0161: // In that case, we flip the formerly "provided" users to be non-provided so they stay in the section.
0162: if ("false".equals(siteProps
0163: .getProperty(CourseImpl.EXTERNALLY_MAINTAINED))) {
0164: if (log.isDebugEnabled())
0165: log
0166: .debug("SiteAdvisor "
0167: + this .getClass().getCanonicalName()
0168: + " stripping provider IDs from all sections in site "
0169: + site.getTitle()
0170: + ". The site is internally managed.");
0171: for (Iterator iter = site.getGroups().iterator(); iter
0172: .hasNext();) {
0173: Group group = (Group) iter.next();
0174: if (group.getProviderGroupId() == null) {
0175: // This wasn't provided, so skip it
0176: continue;
0177: }
0178: group.setProviderGroupId(null);
0179:
0180: // Add members to the groups based on the current (provided) memberships
0181: Set members = group.getMembers();
0182: for (Iterator memberIter = members.iterator(); memberIter
0183: .hasNext();) {
0184: Member member = (Member) memberIter.next();
0185: if (member.isProvided()) {
0186: group.addMember(member.getUserId(), member
0187: .getRole().getId(), member.isActive(),
0188: false);
0189: }
0190: }
0191: }
0192: } else {
0193: // This is an externally managed site, so remove any groups without a category and without a providerId
0194: for (Iterator<Group> iter = site.getGroups().iterator(); iter
0195: .hasNext();) {
0196: Group group = iter.next();
0197: ResourceProperties props = group.getProperties();
0198: if (group.getProviderGroupId() == null
0199: && (props != null && props
0200: .getProperty(CourseSectionImpl.CATEGORY) != null)) {
0201: // This is a section, and it doesn't have a provider id
0202: iter.remove();
0203: }
0204: }
0205: // Update the sections from CM.
0206: syncSections(site);
0207: }
0208: }
0209:
0210: private boolean handlingMandatoryConfigs(
0211: ExternalIntegrationConfig appConfig, Site site) {
0212: ResourceProperties siteProps = site.getProperties();
0213:
0214: switch (appConfig) {
0215: case MANUAL_MANDATORY:
0216: // If we're configured to treat all sites as manual, set the site to manual control
0217: siteProps.addProperty(CourseImpl.EXTERNALLY_MAINTAINED,
0218: Boolean.FALSE.toString());
0219: return true;
0220:
0221: case AUTOMATIC_MANDATORY:
0222: // If we're configured to treat all sites as automatic, set the site to external control and update the sections
0223: siteProps.addProperty(CourseImpl.EXTERNALLY_MAINTAINED,
0224: Boolean.TRUE.toString());
0225: siteProps.addProperty(
0226: CourseImpl.STUDENT_REGISTRATION_ALLOWED,
0227: Boolean.FALSE.toString());
0228: siteProps.addProperty(CourseImpl.STUDENT_SWITCHING_ALLOWED,
0229: Boolean.FALSE.toString());
0230: syncSections(site);
0231: return true;
0232:
0233: default:
0234: return false;
0235: }
0236: }
0237:
0238: private void setSiteDefaults(Site site,
0239: ExternalIntegrationConfig appConfig,
0240: ResourceProperties siteProps) {
0241: // If the site doesn't have a property set for "Externally Maintained", it's either a new site or one
0242: // that was created before this SiteAdvisor was registered.
0243: if (siteProps.getProperty(CourseImpl.EXTERNALLY_MAINTAINED) == null) {
0244: // Set this property to external if the app config is AUTOMATIC_DEFAULT, else make it internally managed
0245: // FIXME -- This might have unforseen consequences...
0246: if (log.isDebugEnabled())
0247: log.debug("Site '" + site.getTitle()
0248: + "' has no EXTERNALLY_MAINTAINED flag.");
0249: if (appConfig == ExternalIntegrationConfig.AUTOMATIC_DEFAULT) {
0250: siteProps.addProperty(CourseImpl.EXTERNALLY_MAINTAINED,
0251: Boolean.TRUE.toString());
0252: siteProps.addProperty(
0253: CourseImpl.STUDENT_REGISTRATION_ALLOWED,
0254: Boolean.FALSE.toString());
0255: siteProps.addProperty(
0256: CourseImpl.STUDENT_SWITCHING_ALLOWED,
0257: Boolean.FALSE.toString());
0258: } else if (appConfig == ExternalIntegrationConfig.MANUAL_DEFAULT) {
0259: siteProps.addProperty(CourseImpl.EXTERNALLY_MAINTAINED,
0260: Boolean.FALSE.toString());
0261: return;
0262: }
0263: }
0264: }
0265:
0266: /**
0267: * Replaces all existing sections (whether internally or externally defined) with
0268: * sections that are externally defined.
0269: *
0270: * @param site
0271: * @param appConfig
0272: */
0273: private void syncSections(Site site) {
0274: if (log.isInfoEnabled())
0275: log
0276: .info("Synchronizing internal sections with externally defined sections in site "
0277: + site.getId());
0278:
0279: // Use the group provider to split the complex string
0280: if (groupProvider == null) {
0281: log
0282: .warn("SectionManager can not automatically generate sections without"
0283: + "a properly configured GroupProvider");
0284: return;
0285: }
0286:
0287: // Get the provider Ids associated with this site. We can't use
0288: // authzGroupService.getProviderIds(), since we're inspecting the provider IDs
0289: // on the in-memory object, not what's in persistence
0290: String siteProviderId = site.getProviderGroupId();
0291: List<String> providerIdList;
0292: if (StringUtils.trimToNull(siteProviderId) == null) {
0293: providerIdList = new ArrayList<String>();
0294: } else {
0295: String[] providerIdArray = groupProvider
0296: .unpackId(siteProviderId);
0297: providerIdList = Arrays.asList(providerIdArray);
0298: }
0299:
0300: Set<Group> sectionsToSync = new HashSet<Group>();
0301:
0302: // Remove any formerly provided sections that are no longer in the list of provider ids,
0303: // and sync existing sections if they are still listed in the provider ids.
0304: if (site.getGroups() != null) {
0305: for (Iterator<Group> iter = site.getGroups().iterator(); iter
0306: .hasNext();) {
0307: Group group = iter.next();
0308: String providerId = group.getProviderGroupId();
0309: if (providerId == null) {
0310: // This wasn't provided, so skip it
0311: continue;
0312: }
0313: if (group.getProperties() == null
0314: || StringUtils
0315: .trimToNull(group
0316: .getProperties()
0317: .getProperty(
0318: CourseSectionImpl.CATEGORY)) == null) {
0319: // This isn't a section, so skip it
0320: continue;
0321: }
0322: if (providerIdList.contains(providerId)) {
0323: if (log.isDebugEnabled())
0324: log.debug("Synchronizing section "
0325: + group.getReference());
0326: sectionsToSync.add(group);
0327: } else {
0328: if (log.isDebugEnabled())
0329: log.debug("Removing section "
0330: + group.getReference());
0331: iter.remove();
0332: }
0333: }
0334: }
0335:
0336: // Sync existing sections
0337: Set<String> sectionProviderIdsToSync = new HashSet<String>();
0338: for (Iterator<Group> iter = sectionsToSync.iterator(); iter
0339: .hasNext();) {
0340: Group group = iter.next();
0341: sectionProviderIdsToSync.add(group.getProviderGroupId());
0342: syncExternalCourseSectionWithSite(group);
0343: }
0344:
0345: // Add provided sections that we're not synchronizing
0346: for (Iterator<String> iter = providerIdList.iterator(); iter
0347: .hasNext();) {
0348: String providerId = iter.next();
0349: if (!sectionProviderIdsToSync.contains(providerId)) {
0350: addExternalCourseSectionToSite(site, providerId);
0351: }
0352: }
0353: }
0354:
0355: /**
0356: * Synchronizes the state of an internal CourseSection with the state of an externally
0357: * defined (CM) section. This is done so meeting times, locations, etc are kept
0358: * in sync with changes made to these data outside Sakai.
0359: *
0360: * @param group
0361: */
0362: private void syncExternalCourseSectionWithSite(Group group) {
0363: String providerId = group.getProviderGroupId();
0364: Section officialSection = null;
0365: try {
0366: officialSection = courseManagementService
0367: .getSection(providerId);
0368: } catch (IdNotFoundException ide) {
0369: log.error("Site " + group.getContainingSite().getId()
0370: + " has a provider id, " + providerId
0371: + ", that has no matching section in CM.");
0372: return;
0373: }
0374: decorateGroupWithCmSection(group, officialSection);
0375: }
0376:
0377: private CourseSection decorateGroupWithCmSection(Group group,
0378: Section officialSection) {
0379: CourseSectionImpl section = new CourseSectionImpl(group);
0380:
0381: section.setTitle(officialSection.getTitle());
0382: section.setCategory(officialSection.getCategory());
0383: section.setMaxEnrollments(officialSection.getMaxSize());
0384: Set officialMeetings = officialSection.getMeetings();
0385: if (officialMeetings != null) {
0386: List<Meeting> meetings = new ArrayList<Meeting>();
0387: for (Iterator meetingIter = officialMeetings.iterator(); meetingIter
0388: .hasNext();) {
0389: org.sakaiproject.coursemanagement.api.Meeting officialMeeting = (org.sakaiproject.coursemanagement.api.Meeting) meetingIter
0390: .next();
0391: MeetingImpl meeting = new MeetingImpl(officialMeeting
0392: .getLocation(), officialMeeting.getStartTime(),
0393: officialMeeting.getFinishTime(),
0394: officialMeeting.isMonday(), officialMeeting
0395: .isTuesday(), officialMeeting
0396: .isWednesday(), officialMeeting
0397: .isThursday(), officialMeeting
0398: .isFriday(), officialMeeting
0399: .isSaturday(), officialMeeting
0400: .isSunday());
0401: meetings.add(meeting);
0402: }
0403: section.setMeetings(meetings);
0404: }
0405: // Ensure that the group is decorated properly, so the group properties are
0406: // persisted with the correct section metadata
0407: section.decorateGroup(group);
0408:
0409: return section;
0410: }
0411:
0412: /**
0413: * Adds an externally managed CourseSection (a decorated group) to a site. The CourseSection is
0414: * constructed by finding the official section from CM and converting it to a CourseSection.
0415: *
0416: * @param site The site in which we are adding a CourseSection
0417: * @param sectionId The Enterprise ID of the section to add.
0418: *
0419: * @return The CourseSection that was added to the site
0420: */
0421: private CourseSection addExternalCourseSectionToSite(Site site,
0422: String sectionEid) {
0423: if (log.isDebugEnabled())
0424: log.debug("Adding section " + sectionEid + " to site "
0425: + site.getId());
0426:
0427: // Create a new sakai section (group) for this providerId
0428: Section officialSection = null;
0429: try {
0430: officialSection = courseManagementService
0431: .getSection(sectionEid);
0432: } catch (IdNotFoundException ide) {
0433: log.error("Site " + site.getId() + " has a provider id, "
0434: + sectionEid
0435: + ", that has no matching section in CM.");
0436: return null;
0437: }
0438: Group group = site.addGroup();
0439: group.setProviderGroupId(sectionEid);
0440: return decorateGroupWithCmSection(group, officialSection);
0441: }
0442:
0443: // SectionManager Methods
0444:
0445: /**
0446: * Filters out framework groups that do not have a category. A section's
0447: * category is determined by
0448: *
0449: */
0450: public List<CourseSection> getSections(String siteContext) {
0451: if (log.isDebugEnabled())
0452: log.debug("Getting sections for context " + siteContext);
0453: List<CourseSection> sectionList = new ArrayList<CourseSection>();
0454: Collection sections;
0455: try {
0456: sections = siteService().getSite(siteContext).getGroups();
0457: } catch (IdUnusedException e) {
0458: log.error("No site with id = " + siteContext);
0459: return new ArrayList<CourseSection>();
0460: }
0461: for (Iterator iter = sections.iterator(); iter.hasNext();) {
0462: Group group = (Group) iter.next();
0463: // Only use groups with a category defined. If there is no category,
0464: // it is not a section.
0465: if (StringUtils.trimToNull(group.getProperties()
0466: .getProperty(CourseSectionImpl.CATEGORY)) != null) {
0467: sectionList.add(new CourseSectionImpl(group));
0468: }
0469: }
0470: return sectionList;
0471: }
0472:
0473: /**
0474: * {@inheritDoc}
0475: */
0476: public List<CourseSection> getSectionsInCategory(
0477: String siteContext, String categoryId) {
0478: if (log.isDebugEnabled())
0479: log.debug("Getting " + categoryId
0480: + " sections for context " + siteContext);
0481: List<CourseSection> sectionList = new ArrayList<CourseSection>();
0482: Collection sections;
0483: try {
0484: sections = siteService().getSite(siteContext).getGroups();
0485: } catch (IdUnusedException e) {
0486: log.error("No site with id = " + siteContext);
0487: return new ArrayList<CourseSection>();
0488: }
0489: for (Iterator iter = sections.iterator(); iter.hasNext();) {
0490: Group group = (Group) iter.next();
0491: if (categoryId.equals(group.getProperties().getProperty(
0492: CourseSectionImpl.CATEGORY))) {
0493: sectionList.add(new CourseSectionImpl(group));
0494: }
0495: }
0496: return sectionList;
0497: }
0498:
0499: /**
0500: * {@inheritDoc}
0501: */
0502: public CourseSection getSection(String sectionUuid) {
0503: Group group;
0504: group = siteService().findGroup(sectionUuid);
0505: if (group == null) {
0506: log.error("Unable to find section " + sectionUuid);
0507: return null;
0508: }
0509: return new CourseSectionImpl(group);
0510: }
0511:
0512: /**
0513: * {@inheritDoc}
0514: */
0515: public List<ParticipationRecord> getSiteInstructors(
0516: String siteContext) {
0517: CourseImpl course = (CourseImpl) getCourse(siteContext);
0518: if (course == null) {
0519: return new ArrayList<ParticipationRecord>();
0520: }
0521: Site site = course.getSite();
0522: Set sakaiUserIds = site
0523: .getUsersIsAllowed(SectionAwareness.INSTRUCTOR_MARKER);
0524: List sakaiMembers = userDirectoryService.getUsers(sakaiUserIds);
0525: List<ParticipationRecord> membersList = new ArrayList<ParticipationRecord>();
0526: for (Iterator iter = sakaiMembers.iterator(); iter.hasNext();) {
0527: org.sakaiproject.user.api.User sakaiUser = (org.sakaiproject.user.api.User) iter
0528: .next();
0529: User user = SakaiUtil.convertUser(sakaiUser);
0530: InstructorRecordImpl record = new InstructorRecordImpl(
0531: course, user);
0532: membersList.add(record);
0533: }
0534: return membersList;
0535: }
0536:
0537: /**
0538: * {@inheritDoc}
0539: */
0540: public List<ParticipationRecord> getSiteTeachingAssistants(
0541: String siteContext) {
0542: CourseImpl course = (CourseImpl) getCourse(siteContext);
0543: if (course == null) {
0544: return new ArrayList<ParticipationRecord>();
0545: }
0546: Site site = course.getSite();
0547: Set sakaiUserIds = site
0548: .getUsersIsAllowed(SectionAwareness.TA_MARKER);
0549: List sakaiMembers = userDirectoryService.getUsers(sakaiUserIds);
0550: List<ParticipationRecord> membersList = new ArrayList<ParticipationRecord>();
0551: for (Iterator iter = sakaiMembers.iterator(); iter.hasNext();) {
0552: org.sakaiproject.user.api.User sakaiUser = (org.sakaiproject.user.api.User) iter
0553: .next();
0554: User user = SakaiUtil.convertUser(sakaiUser);
0555: TeachingAssistantRecordImpl record = new TeachingAssistantRecordImpl(
0556: course, user);
0557: membersList.add(record);
0558: }
0559: return membersList;
0560: }
0561:
0562: /**
0563: * {@inheritDoc}
0564: */
0565: public List<EnrollmentRecord> getSiteEnrollments(String siteContext) {
0566: CourseImpl course = (CourseImpl) getCourse(siteContext);
0567: if (course == null) {
0568: return new ArrayList<EnrollmentRecord>();
0569: }
0570: Site site = course.getSite();
0571: Set sakaiUserIds = site
0572: .getUsersIsAllowed(SectionAwareness.STUDENT_MARKER);
0573: List sakaiMembers = userDirectoryService.getUsers(sakaiUserIds);
0574: List<EnrollmentRecord> membersList = new ArrayList<EnrollmentRecord>();
0575: for (Iterator iter = sakaiMembers.iterator(); iter.hasNext();) {
0576: org.sakaiproject.user.api.User sakaiUser = (org.sakaiproject.user.api.User) iter
0577: .next();
0578: User user = SakaiUtil.convertUser(sakaiUser);
0579: EnrollmentRecordImpl record = new EnrollmentRecordImpl(
0580: course, null, user);
0581: membersList.add(record);
0582: }
0583: return membersList;
0584: }
0585:
0586: /**
0587: * {@inheritDoc}
0588: */
0589: public List<ParticipationRecord> getSectionTeachingAssistants(
0590: String sectionUuid) {
0591: Group group = siteService().findGroup(sectionUuid);
0592: CourseSection section = getSection(sectionUuid);
0593: if (section == null) {
0594: return new ArrayList<ParticipationRecord>();
0595: }
0596: if (log.isDebugEnabled())
0597: log.debug("Getting section enrollments in " + sectionUuid);
0598: String taRole;
0599: try {
0600: taRole = getSectionTaRole(group);
0601: } catch (RoleConfigurationException rce) {
0602: return new ArrayList<ParticipationRecord>();
0603: }
0604: Set sakaiUserUids = group.getUsersHasRole(taRole);
0605: List sakaiUsers = userDirectoryService.getUsers(sakaiUserUids);
0606:
0607: List<ParticipationRecord> membersList = new ArrayList<ParticipationRecord>();
0608: for (Iterator iter = sakaiUsers.iterator(); iter.hasNext();) {
0609: User user = SakaiUtil
0610: .convertUser((org.sakaiproject.user.api.User) iter
0611: .next());
0612: TeachingAssistantRecordImpl record = new TeachingAssistantRecordImpl(
0613: section, user);
0614: membersList.add(record);
0615: }
0616: return membersList;
0617: }
0618:
0619: /**
0620: * {@inheritDoc}
0621: */
0622: public List<EnrollmentRecord> getSectionEnrollments(
0623: String sectionUuid) {
0624: Group group = siteService().findGroup(sectionUuid);
0625: CourseSection section = getSection(sectionUuid);
0626: if (section == null) {
0627: return new ArrayList<EnrollmentRecord>();
0628: }
0629: if (log.isDebugEnabled())
0630: log.debug("Getting section enrollments in " + sectionUuid);
0631: String studentRole;
0632: try {
0633: studentRole = getSectionStudentRole(group);
0634: } catch (RoleConfigurationException rce) {
0635: log.error(rce);
0636: return new ArrayList<EnrollmentRecord>();
0637: }
0638:
0639: Set sakaiUserUids = group.getUsersHasRole(studentRole);
0640: List sakaiUsers = userDirectoryService.getUsers(sakaiUserUids);
0641:
0642: List<EnrollmentRecord> membersList = new ArrayList<EnrollmentRecord>();
0643: for (Iterator iter = sakaiUsers.iterator(); iter.hasNext();) {
0644: User user = SakaiUtil
0645: .convertUser((org.sakaiproject.user.api.User) iter
0646: .next());
0647: EnrollmentRecordImpl record = new EnrollmentRecordImpl(
0648: section, null, user);
0649: membersList.add(record);
0650: }
0651: return membersList;
0652: }
0653:
0654: /**
0655: * {@inheritDoc}
0656: */
0657: public List<EnrollmentRecord> findSiteEnrollments(
0658: String siteContext, String pattern) {
0659: List<EnrollmentRecord> fullList = getSiteEnrollments(siteContext);
0660: List<EnrollmentRecord> filteredList = new ArrayList<EnrollmentRecord>();
0661: for (Iterator iter = fullList.iterator(); iter.hasNext();) {
0662: EnrollmentRecord record = (EnrollmentRecord) iter.next();
0663: User user = record.getUser();
0664: if (user.getDisplayName().toLowerCase().startsWith(
0665: pattern.toLowerCase())
0666: || user.getSortName().toLowerCase().startsWith(
0667: pattern.toLowerCase())
0668: || user.getDisplayId().toLowerCase().startsWith(
0669: pattern.toLowerCase())) {
0670: filteredList.add(record);
0671: }
0672: }
0673: return filteredList;
0674: }
0675:
0676: /**
0677: * {@inheritDoc}
0678: */
0679: public String getCategoryName(String categoryId, Locale locale) {
0680: return courseManagementService
0681: .getSectionCategoryDescription(categoryId);
0682: }
0683:
0684: /**
0685: * {@inheritDoc}
0686: */
0687: public List<String> getSectionCategories(String siteContext) {
0688: return courseManagementService.getSectionCategories();
0689: }
0690:
0691: /**
0692: * {@inheritDoc}
0693: */
0694: public Course getCourse(String siteContext) {
0695: if (log.isDebugEnabled())
0696: log.debug("Getting course for context " + siteContext);
0697: Site site;
0698: try {
0699: site = siteService().getSite(siteContext);
0700: } catch (IdUnusedException e) {
0701: log.error("Could not find site with id = " + siteContext);
0702: return null;
0703: }
0704: return new CourseImpl(site);
0705: }
0706:
0707: /**
0708: * {@inheritDoc}
0709: */
0710: public SectionEnrollments getSectionEnrollmentsForStudents(
0711: String siteContext, Set studentUids) {
0712: if (studentUids == null || studentUids.isEmpty()) {
0713: if (log.isDebugEnabled())
0714: log
0715: .debug("Null or empty set of student Uids passed to getSectionEnrollmentsForStudents");
0716: return new SectionEnrollmentsImpl(new ArrayList());
0717: }
0718: // Get all sections
0719: List allSections = getSections(siteContext);
0720:
0721: // Get all student enrollments in each section, and combine them into a single collection
0722: List<ParticipationRecord> allSectionEnrollments = new ArrayList<ParticipationRecord>();
0723: for (Iterator sectionIter = allSections.iterator(); sectionIter
0724: .hasNext();) {
0725: CourseSection section = (CourseSection) sectionIter.next();
0726: List sectionEnrollments = getSectionEnrollments(section
0727: .getUuid());
0728: for (Iterator enrollmentIter = sectionEnrollments
0729: .iterator(); enrollmentIter.hasNext();) {
0730: EnrollmentRecord enr = (EnrollmentRecord) enrollmentIter
0731: .next();
0732: if (studentUids.contains(enr.getUser().getUserUid())) {
0733: allSectionEnrollments.add(enr);
0734: }
0735: }
0736: }
0737:
0738: return new SectionEnrollmentsImpl(allSectionEnrollments);
0739: }
0740:
0741: /**
0742: * Posts an event to Sakai's event tracking service. only posts events
0743: * that modify some object. Read-only events are not tracked.
0744: *
0745: * @param message The message to post
0746: * @param objectReference The object that was modified in the event
0747: */
0748: private void postEvent(String message, String objectReference) {
0749: Event event = eventTrackingService.newEvent(message,
0750: objectReference, true);
0751: eventTrackingService.post(event);
0752: }
0753:
0754: /**
0755: * {@inheritDoc}
0756: */
0757: public EnrollmentRecord joinSection(String sectionUuid)
0758: throws RoleConfigurationException {
0759: // Disallow if we're in an externally managed site
0760: ensureInternallyManaged(getSection(sectionUuid).getCourse()
0761: .getUuid());
0762:
0763: Group group = siteService().findGroup(sectionUuid);
0764:
0765: // It's possible that this section has been deleted
0766: if (group == null) {
0767: log.error("Section " + sectionUuid
0768: + " has been deleted, so it can't be joined.");
0769: return null;
0770: }
0771:
0772: String role = getSectionStudentRole(group);
0773: try {
0774: authzGroupService.joinGroup(sectionUuid, role);
0775: postEvent("section.student.join", sectionUuid);
0776: } catch (AuthzPermissionException e) {
0777: log
0778: .error(
0779: "access denied while attempting to join authz group: ",
0780: e);
0781: return null;
0782: } catch (GroupNotDefinedException e) {
0783: log
0784: .error(
0785: "can not find group while attempting to join authz group: ",
0786: e);
0787: return null;
0788: }
0789:
0790: // Return the membership record that the app understands
0791: String userUid = sessionManager.getCurrentSessionUserId();
0792: User user = SakaiUtil.getUserFromSakai(userUid);
0793: CourseSection section = getSection(sectionUuid);
0794:
0795: return new EnrollmentRecordImpl(section, null, user);
0796: }
0797:
0798: private String getSectionStudentRole(AuthzGroup group)
0799: throws RoleConfigurationException {
0800: Set roleStrings = group
0801: .getRolesIsAllowed(SectionAwareness.STUDENT_MARKER);
0802: if (roleStrings.size() != 1) {
0803: if (log.isDebugEnabled())
0804: log
0805: .debug("Group "
0806: + group
0807: + " must have one and only one role with permission "
0808: + SectionAwareness.STUDENT_MARKER);
0809: throw new RoleConfigurationException(
0810: "Can't add a user to a section as a student, since there is no student-flagged role");
0811: }
0812: return (String) roleStrings.iterator().next();
0813: }
0814:
0815: private String getSectionTaRole(Group group)
0816: throws RoleConfigurationException {
0817: Set roleStrings = group
0818: .getRolesIsAllowed(SectionAwareness.TA_MARKER);
0819: if (roleStrings.size() != 1) {
0820: if (log.isDebugEnabled())
0821: log
0822: .debug("Group "
0823: + group
0824: + " must have one and only one role with permission "
0825: + SectionAwareness.TA_MARKER);
0826: throw new RoleConfigurationException(
0827: "Can't add a user to a section as a TA, since there is no TA-flagged role");
0828: }
0829: return (String) roleStrings.iterator().next();
0830: }
0831:
0832: /**
0833: * {@inheritDoc}
0834: */
0835: public void switchSection(String newSectionUuid)
0836: throws RoleConfigurationException {
0837: // Disallow if we're in an externally managed site
0838: ensureInternallyManaged(getSection(newSectionUuid).getCourse()
0839: .getUuid());
0840:
0841: CourseSection newSection = getSection(newSectionUuid);
0842:
0843: // It's possible that this section has been deleted
0844: if (newSection == null) {
0845: return;
0846: }
0847:
0848: // Disallow if we're in an externally managed site
0849: if (isExternallyManaged(newSection.getCourse().getUuid())) {
0850: log
0851: .warn("Can not switch sections in an externally managed site");
0852: return;
0853: }
0854:
0855: String userUid = sessionManager.getCurrentSessionUserId();
0856:
0857: // Remove any section membership for a section of the same category.
0858: // We can not use dropEnrollmentFromCategory because security checks won't
0859: // allow a student to update the authZ groups directly.
0860: List categorySections = getSectionsInCategory(newSection
0861: .getCourse().getSiteContext(), newSection.getCategory());
0862:
0863: boolean errorDroppingSection = false;
0864:
0865: String oldSectionUuid = null;
0866: for (Iterator iter = categorySections.iterator(); iter
0867: .hasNext();) {
0868: CourseSection section = (CourseSection) iter.next();
0869: // Skip the current section
0870: if (section.getUuid().equals(newSectionUuid)) {
0871: continue;
0872: }
0873: if (this .isMember(userUid, section)) {
0874: oldSectionUuid = section.getUuid();
0875: try {
0876: authzGroupService.unjoinGroup(section.getUuid());
0877: oldSectionUuid = section.getUuid();
0878: } catch (GroupNotDefinedException e) {
0879: errorDroppingSection = true;
0880: log.error("There is not authzGroup with id "
0881: + section.getUuid());
0882: } catch (AuthzPermissionException e) {
0883: errorDroppingSection = true;
0884: log.error("Permission denied while " + userUid
0885: + " attempted to unjoin authzGroup "
0886: + section.getUuid());
0887: }
0888: }
0889: }
0890:
0891: // Only allow the user to join the new section if there were no errors dropping section(s)
0892: if (!errorDroppingSection) {
0893: // Join the new section
0894: joinSection(newSectionUuid);
0895:
0896: // Post the events
0897: postEvent("section.student.unjoin", oldSectionUuid);
0898: postEvent("section.student.switch", newSectionUuid);
0899: }
0900:
0901: }
0902:
0903: private boolean isMember(String userUid, CourseSection section) {
0904: return authzGroupService
0905: .getUserRole(userUid, section.getUuid()) != null;
0906: }
0907:
0908: /**
0909: * {@inheritDoc}
0910: */
0911: public ParticipationRecord addSectionMembership(String userUid,
0912: Role role, String sectionUuid) throws MembershipException,
0913: RoleConfigurationException {
0914: if (role.isStudent()) {
0915: // Disallow if we're in an externally managed site
0916: ensureInternallyManaged(getSection(sectionUuid).getCourse()
0917: .getUuid());
0918: return addStudentToSection(userUid, sectionUuid);
0919: } else if (role.isTeachingAssistant()) {
0920: return addTaToSection(userUid, sectionUuid);
0921: } else {
0922: throw new RuntimeException(
0923: "Adding a user to a section with role instructor or none is not supported");
0924: }
0925: }
0926:
0927: private ParticipationRecord addTaToSection(String userUid,
0928: String sectionUuid) throws RoleConfigurationException {
0929: CourseSectionImpl section = (CourseSectionImpl) getSection(sectionUuid);
0930:
0931: // It's possible that this section has been deleted
0932: if (section == null) {
0933: return null;
0934: }
0935:
0936: Group group = section.getGroup();
0937: User user = SakaiUtil.getUserFromSakai(userUid);
0938:
0939: // Add the membership to the framework
0940: String role = getSectionTaRole(group);
0941:
0942: group.addMember(userUid, role, true, false);
0943:
0944: try {
0945: siteService()
0946: .saveGroupMembership(group.getContainingSite());
0947: postEvent("section.add.ta", sectionUuid);
0948: } catch (IdUnusedException e) {
0949: log.error("unable to find site: ", e);
0950: return null;
0951: } catch (PermissionException e) {
0952: log.error("access denied while attempting to save site: ",
0953: e);
0954: return null;
0955: }
0956:
0957: // Return the enrollment record
0958: return new TeachingAssistantRecordImpl(section, user);
0959: }
0960:
0961: private EnrollmentRecord addStudentToSection(String userUid,
0962: String sectionUuid) throws RoleConfigurationException {
0963: User user = SakaiUtil.getUserFromSakai(userUid);
0964:
0965: CourseSectionImpl newSection = (CourseSectionImpl) getSection(sectionUuid);
0966:
0967: // It's possible that this section has been deleted
0968: if (newSection == null) {
0969: return null;
0970: }
0971:
0972: Group group = newSection.getGroup();
0973:
0974: String studentRole = getSectionStudentRole(group);
0975:
0976: // Remove any section membership for a section of the same category.
0977: dropEnrollmentFromCategory(userUid, newSection.getCourse()
0978: .getSiteContext(), newSection.getCategory());
0979:
0980: // Add the membership to the framework
0981: if (studentRole == null) {
0982: throw new RoleConfigurationException(
0983: "Can't add a student to a section, since there is no student-flgagged role");
0984: }
0985: group.addMember(userUid, studentRole, true, false);
0986:
0987: try {
0988: siteService()
0989: .saveGroupMembership(group.getContainingSite());
0990: postEvent("section.add.student", sectionUuid);
0991: } catch (IdUnusedException e) {
0992: log.error("unable to find site: ", e);
0993: return null;
0994: } catch (PermissionException e) {
0995: log.error("access denied while attempting to save site: ",
0996: e);
0997: return null;
0998: }
0999:
1000: // Return the enrollment record
1001: return new EnrollmentRecordImpl(newSection, null, user);
1002: }
1003:
1004: /**
1005: * {@inheritDoc}
1006: */
1007: public void setSectionMemberships(Set userUids, Role role,
1008: String sectionUuid) throws RoleConfigurationException {
1009: if (role.isStudent()) {
1010: // Disallow if we're in an externally managed site
1011: ensureInternallyManaged(getSection(sectionUuid).getCourse()
1012: .getUuid());
1013: }
1014:
1015: CourseSectionImpl section = (CourseSectionImpl) getSection(sectionUuid);
1016:
1017: // It's possible that this section has been deleted
1018: if (section == null) {
1019: return;
1020: }
1021:
1022: Group group = section.getGroup();
1023: String sakaiRoleString;
1024: if (role.isTeachingAssistant()) {
1025: sakaiRoleString = getSectionTaRole(group);
1026: } else if (role.isStudent()) {
1027: sakaiRoleString = getSectionStudentRole(group);
1028: } else {
1029: String str = "Only students and TAs can be added to sections";
1030: log.error(str);
1031: throw new RuntimeException(str);
1032: }
1033:
1034: if (sakaiRoleString == null) {
1035: throw new RoleConfigurationException(
1036: "Can't set memberships for role "
1037: + role
1038: + ". No sakai role string can be found for this role.");
1039: }
1040:
1041: // Remove the current members in this role
1042: Set currentUserIds = group.getUsersHasRole(sakaiRoleString);
1043: for (Iterator iter = currentUserIds.iterator(); iter.hasNext();) {
1044: String userUid = (String) iter.next();
1045: group.removeMember(userUid);
1046: }
1047:
1048: // Add the new members (sure would be nice to have transactions here!)
1049: for (Iterator iter = userUids.iterator(); iter.hasNext();) {
1050: String userUid = (String) iter.next();
1051: group.addMember(userUid, sakaiRoleString, true, false);
1052: }
1053:
1054: try {
1055: siteService()
1056: .saveGroupMembership(group.getContainingSite());
1057: postEvent("section.members.reset", sectionUuid);
1058: } catch (IdUnusedException e) {
1059: log.error("unable to find site: ", e);
1060: } catch (PermissionException e) {
1061: log
1062: .error(
1063: "access denied while attempting to save authz group: ",
1064: e);
1065: }
1066: }
1067:
1068: /**
1069: * {@inheritDoc}
1070: */
1071: public void dropSectionMembership(String userUid, String sectionUuid) {
1072:
1073: CourseSectionImpl section = (CourseSectionImpl) getSection(sectionUuid);
1074: Group group = section.getGroup();
1075:
1076: // If we're trying to drop a student, ensure that we're not automatically managed
1077: Member member = group.getMember(userUid);
1078: String studentRole = null;
1079: try {
1080: studentRole = getSectionStudentRole(group);
1081: } catch (RoleConfigurationException e1) {
1082: log.error("Can't find the student role for section"
1083: + sectionUuid);
1084: }
1085: if (studentRole != null && studentRole.equals(member.getRole())) {
1086: // We can not drop students from a section in externally managed sites
1087: ensureInternallyManaged(section.getCourse().getUuid());
1088: }
1089:
1090: group.removeMember(userUid);
1091: try {
1092: siteService()
1093: .saveGroupMembership(group.getContainingSite());
1094: postEvent("section.student.drop", sectionUuid);
1095: } catch (IdUnusedException e) {
1096: log.error("unable to find site: ", e);
1097: } catch (PermissionException e) {
1098: log.error("access denied while attempting to save site: ",
1099: e);
1100: }
1101: }
1102:
1103: /**
1104: * {@inheritDoc}
1105: */
1106: public void dropEnrollmentFromCategory(String studentUid,
1107: String siteContext, String category) {
1108: // Disallow if we're in an externally managed site
1109: ensureInternallyManaged(getCourse(siteContext).getUuid());
1110:
1111: if (log.isDebugEnabled())
1112: log.debug("Dropping " + studentUid
1113: + " from all sections in category " + category
1114: + " in site " + siteContext);
1115: // Get the sections in this category
1116: Site site;
1117: try {
1118: site = siteService().getSite(siteContext);
1119: } catch (IdUnusedException ide) {
1120: log.error("Unable to find site " + siteContext);
1121: return;
1122: }
1123: Collection groups = site.getGroups();
1124: for (Iterator iter = groups.iterator(); iter.hasNext();) {
1125: // Drop the user from this section if they are enrolled
1126: Group group = (Group) iter.next();
1127: CourseSectionImpl section = new CourseSectionImpl(group);
1128: // Don't drop someone from a non-section groups
1129: if (section.getCategory() == null) {
1130: continue;
1131: }
1132: if (section.getCategory().equals(category)) {
1133: group.removeMember(studentUid);
1134: }
1135: }
1136: try {
1137: siteService().saveGroupMembership(site);
1138: postEvent("section.student.drop.category", site
1139: .getReference());
1140: } catch (IdUnusedException e) {
1141: log.error("unable to find site: ", e);
1142: return;
1143: } catch (PermissionException e) {
1144: log.error("access denied while attempting to save site: ",
1145: e);
1146: return;
1147: }
1148: }
1149:
1150: /**
1151: * {@inheritDoc}
1152: */
1153: public int getTotalEnrollments(String learningContextUuid) {
1154: AuthzGroup authzGroup;
1155: try {
1156: authzGroup = authzGroupService
1157: .getAuthzGroup(learningContextUuid);
1158: } catch (GroupNotDefinedException e) {
1159: log.error("learning context " + learningContextUuid
1160: + " is neither a site nor a section");
1161: return 0;
1162: }
1163: String studentRole;
1164: try {
1165: studentRole = getSectionStudentRole(authzGroup);
1166: } catch (RoleConfigurationException rce) {
1167: log
1168: .warn("Can't get total enrollments, since there is no single student-flagged role in "
1169: + learningContextUuid);
1170: return 0;
1171: }
1172: Set users = authzGroup.getUsersHasRole(studentRole);
1173: return users.size();
1174: }
1175:
1176: /**
1177: * {@inheritDoc}
1178: */
1179: public CourseSection addSection(String courseUuid, String title,
1180: String category, Integer maxEnrollments, String location,
1181: Time startTime, Time endTime, boolean monday,
1182: boolean tuesday, boolean wednesday, boolean thursday,
1183: boolean friday, boolean saturday, boolean sunday) {
1184:
1185: // Get the site
1186: Reference ref = entityManager.newReference(courseUuid);
1187: Site site;
1188: try {
1189: site = siteService().getSite(ref.getId());
1190: } catch (IdUnusedException e) {
1191: log.error("Unable to find site " + courseUuid);
1192: return null;
1193: }
1194:
1195: CourseSection section = new CourseSectionImpl(getCourse(site
1196: .getId()), title, null, category, maxEnrollments,
1197: location, startTime, endTime, monday, tuesday,
1198: wednesday, thursday, friday, saturday, sunday);
1199:
1200: List<CourseSection> sections = new ArrayList<CourseSection>();
1201: sections.add(section);
1202:
1203: addSections(courseUuid, sections);
1204:
1205: return section;
1206: }
1207:
1208: /**
1209: * Throws a SecurityException if an attempt is made to modify a section or its
1210: * student memberships when the site is configured for external management.
1211: */
1212: private void ensureInternallyManaged(String courseUuid) {
1213: if (isExternallyManaged(courseUuid)) {
1214: throw new SecurityException(
1215: "Can not make changes to sections or student memberships in site "
1216: + courseUuid
1217: + ". It is externally managed.");
1218: }
1219: }
1220:
1221: private List<Meeting> filterMeetings(List<Meeting> meetings) {
1222: // Remove any empty meetings
1223: List<Meeting> filteredMeetings = new ArrayList<Meeting>();
1224: for (Iterator<Meeting> iter = meetings.iterator(); iter
1225: .hasNext();) {
1226: Meeting meeting = iter.next();
1227: if (!meeting.isEmpty()) {
1228: filteredMeetings.add(meeting);
1229: }
1230: }
1231: return filteredMeetings;
1232: }
1233:
1234: private CourseSection addSectionToSite(Site site, String title,
1235: String category, Integer maxEnrollments,
1236: List<Meeting> meetings) {
1237: Group group = site.addGroup();
1238:
1239: // Construct a CourseSection for this group
1240: CourseSectionImpl courseSection = new CourseSectionImpl(group);
1241:
1242: // Set the fields of the course section
1243: courseSection.setTitle(title);
1244: courseSection.setCategory(category);
1245: courseSection.setMaxEnrollments(maxEnrollments);
1246: courseSection.setMeetings(filterMeetings(meetings));
1247:
1248: // Decorate the framework group
1249: courseSection.decorateGroup(group);
1250:
1251: return courseSection;
1252: }
1253:
1254: public Collection<CourseSection> addSections(String courseUuid,
1255: Collection<CourseSection> sections) {
1256: // Disallow if we're in an externally managed site
1257: ensureInternallyManaged(courseUuid);
1258:
1259: // Get the site
1260: Reference ref = entityManager.newReference(courseUuid);
1261: Site site;
1262: try {
1263: site = siteService().getSite(ref.getId());
1264: } catch (IdUnusedException e) {
1265: log.error("Unable to find site " + courseUuid);
1266: return null;
1267: }
1268:
1269: List<CourseSection> addedSections = new ArrayList<CourseSection>();
1270:
1271: // Add the decorated groups to the site
1272: for (Iterator<CourseSection> iter = sections.iterator(); iter
1273: .hasNext();) {
1274: CourseSection section = iter.next();
1275: addedSections
1276: .add(addSectionToSite(site, section.getTitle(),
1277: section.getCategory(), section
1278: .getMaxEnrollments(), section
1279: .getMeetings()));
1280: }
1281:
1282: // Save the site, along with the new section
1283: try {
1284: siteService().save(site);
1285: for (Iterator<CourseSection> iter = sections.iterator(); iter
1286: .hasNext();) {
1287: postEvent("section.add", iter.next().getUuid());
1288: }
1289: return addedSections;
1290: } catch (IdUnusedException ide) {
1291: log.error("Error saving site... could not find site "
1292: + site.getId(), ide);
1293: } catch (PermissionException pe) {
1294: log.error(
1295: "Error saving site... permission denied for site "
1296: + site.getId(), pe);
1297: }
1298: return null;
1299: }
1300:
1301: /**
1302: * {@inheritDoc}
1303: */
1304: public void updateSection(String sectionUuid, String title,
1305: Integer maxEnrollments, String location, Time startTime,
1306: Time endTime, boolean monday, boolean tuesday,
1307: boolean wednesday, boolean thursday, boolean friday,
1308: boolean saturday, boolean sunday) {
1309: // Create a list of meetings with a single meeting
1310: List<Meeting> meetings = new ArrayList<Meeting>();
1311: MeetingImpl meeting = new MeetingImpl(location, startTime,
1312: endTime, monday, tuesday, wednesday, thursday, friday,
1313: saturday, sunday);
1314: meetings.add(meeting);
1315:
1316: // Update the section with a single meeting
1317: updateSection(sectionUuid, title, maxEnrollments, meetings);
1318: }
1319:
1320: public void updateSection(String sectionUuid, String title,
1321: Integer maxEnrollments, List<Meeting> meetings) {
1322: // Disallow if we're in an externally managed site
1323: ensureInternallyManaged(getSection(sectionUuid).getCourse()
1324: .getUuid());
1325:
1326: CourseSectionImpl section = (CourseSectionImpl) getSection(sectionUuid);
1327:
1328: if (section == null) {
1329: throw new RuntimeException("Unable to find section "
1330: + sectionUuid);
1331: }
1332:
1333: // Set the decorator's fields
1334: section.setTitle(title);
1335: section.setMaxEnrollments(maxEnrollments);
1336: section.setMeetings(filterMeetings(meetings));
1337:
1338: // Decorate the framework section
1339: Group group = siteService().findGroup(sectionUuid);
1340: section.decorateGroup(group);
1341:
1342: // Save the site with its new section
1343: try {
1344: siteService().save(group.getContainingSite());
1345: postEvent("section.update", sectionUuid);
1346: } catch (IdUnusedException ide) {
1347: log.error(
1348: "Error saving site... could not find site for section "
1349: + group, ide);
1350: } catch (PermissionException pe) {
1351: log.error(
1352: "Error saving site... permission denied for section "
1353: + group, pe);
1354: }
1355: }
1356:
1357: /**
1358: * {@inheritDoc}
1359: */
1360: public void disbandSection(String sectionUuid) {
1361: Set<String> set = new HashSet<String>();
1362: set.add(sectionUuid);
1363: disbandSections(set);
1364: }
1365:
1366: /**
1367: * {@inheritDoc}
1368: */
1369: public void disbandSections(Set<String> sectionUuids) {
1370: if (sectionUuids == null || sectionUuids.isEmpty()) {
1371: return;
1372: }
1373:
1374: // Determine the course (site) we're in
1375: String firstSectionUuid = sectionUuids.iterator().next();
1376: CourseSection firstSection = getSection(firstSectionUuid);
1377: if (firstSection == null) {
1378: if (log.isDebugEnabled())
1379: log.debug("Unable to remove section "
1380: + firstSectionUuid);
1381: return;
1382: }
1383: Course course = firstSection.getCourse();
1384:
1385: // Disallow if we're in an externally managed site
1386: ensureInternallyManaged(course.getUuid());
1387:
1388: Site site = null;
1389: for (Iterator<String> iter = sectionUuids.iterator(); iter
1390: .hasNext();) {
1391: String sectionUuid = iter.next();
1392: if (log.isDebugEnabled())
1393: log.debug("Disbanding section " + sectionUuid);
1394: Group group = siteService().findGroup(sectionUuid);
1395:
1396: // TODO Add token in UI to intercept double clicks in action buttons
1397: // SAK-3553 (Clicking remove button twice during section remove operation results in blank iframe.)
1398: if (group == null) {
1399: log.warn("Unable to find group with uuid "
1400: + sectionUuid);
1401: return;
1402: }
1403: if (site == null) {
1404: site = group.getContainingSite();
1405: }
1406: site.removeGroup(group);
1407: }
1408:
1409: try {
1410: siteService().save(site);
1411: for (Iterator<String> iter = sectionUuids.iterator(); iter
1412: .hasNext();) {
1413: String sectionUuid = iter.next();
1414: postEvent("section.disband", sectionUuid);
1415: }
1416: } catch (IdUnusedException e) {
1417: log.error(
1418: "Cound not disband section (can't find section): ",
1419: e);
1420: } catch (PermissionException e) {
1421: log.error("Cound not disband section (access denied): ", e);
1422: }
1423: }
1424:
1425: public boolean isExternallyManaged(String courseUuid) {
1426: Reference ref = entityManager.newReference(courseUuid);
1427: String siteId = ref.getId();
1428: Site site;
1429: try {
1430: site = siteService().getSite(siteId);
1431: } catch (IdUnusedException e) {
1432: throw new RuntimeException("Can not find site "
1433: + courseUuid, e);
1434: }
1435: ResourceProperties props = site.getProperties();
1436: return Boolean.toString(true).equals(
1437: props.getProperty(CourseImpl.EXTERNALLY_MAINTAINED));
1438: }
1439:
1440: public void setExternallyManaged(String courseUuid,
1441: boolean externallyManaged) {
1442: // Disallow if the service is configured to be mandatory
1443: ExternalIntegrationConfig appConfig = getConfiguration(null);
1444: if (appConfig == ExternalIntegrationConfig.AUTOMATIC_MANDATORY
1445: || appConfig == ExternalIntegrationConfig.MANUAL_MANDATORY) {
1446: throw new SecurityException(
1447: "Can not change the external management of a site, since this service is configured to be "
1448: + appConfig);
1449: }
1450:
1451: Reference ref = entityManager.newReference(courseUuid);
1452: String siteId = ref.getId();
1453: Site site;
1454: try {
1455: site = siteService().getSite(siteId);
1456: } catch (IdUnusedException e) {
1457: throw new RuntimeException("Can not find site "
1458: + courseUuid, e);
1459: }
1460: ResourceProperties props = site.getProperties();
1461:
1462: // Update the site
1463: props.addProperty(CourseImpl.EXTERNALLY_MAINTAINED, Boolean
1464: .toString(externallyManaged));
1465: if (externallyManaged) {
1466: // Also set the self join/switch to false
1467: props.addProperty(CourseImpl.STUDENT_REGISTRATION_ALLOWED,
1468: Boolean.toString(false));
1469: props.addProperty(CourseImpl.STUDENT_SWITCHING_ALLOWED,
1470: Boolean.toString(false));
1471: }
1472:
1473: try {
1474: siteService().save(site);
1475: if (log.isDebugEnabled())
1476: log.debug("Saved site " + site.getTitle());
1477: postEvent("section.external=" + externallyManaged, site
1478: .getReference());
1479: } catch (IdUnusedException ide) {
1480: log.error("Error saving site... could not find site "
1481: + site, ide);
1482: } catch (PermissionException pe) {
1483: log.error("Error saving site... permission denied for "
1484: + site, pe);
1485: }
1486: }
1487:
1488: /**
1489: * {@inheritDoc}
1490: */
1491: public boolean isSelfRegistrationAllowed(String courseUuid) {
1492: Reference ref = entityManager.newReference(courseUuid);
1493: String siteId = ref.getId();
1494: Site site;
1495: try {
1496: site = siteService().getSite(siteId);
1497: } catch (IdUnusedException e) {
1498: throw new RuntimeException("Can not find site "
1499: + courseUuid, e);
1500: }
1501: ResourceProperties props = site.getProperties();
1502: return Boolean
1503: .toString(true)
1504: .equals(
1505: props
1506: .getProperty(CourseImpl.STUDENT_REGISTRATION_ALLOWED));
1507: }
1508:
1509: public void setJoinOptions(String courseUuid, boolean joinAllowed,
1510: boolean switchAllowed) {
1511: // Disallow if we're in an externally managed site
1512: ensureInternallyManaged(courseUuid);
1513:
1514: Reference ref = entityManager.newReference(courseUuid);
1515: String siteId = ref.getId();
1516: Site site;
1517: try {
1518: site = siteService().getSite(siteId);
1519: } catch (IdUnusedException e) {
1520: throw new RuntimeException("Can not find site "
1521: + courseUuid, e);
1522: }
1523: ResourceProperties props = site.getProperties();
1524:
1525: // Get the existing join and switch settings, so we know what's changed
1526: boolean oldJoin = new Boolean(props
1527: .getProperty(CourseImpl.STUDENT_REGISTRATION_ALLOWED))
1528: .booleanValue();
1529: boolean oldSwitch = new Boolean(props
1530: .getProperty(CourseImpl.STUDENT_SWITCHING_ALLOWED))
1531: .booleanValue();
1532:
1533: props.addProperty(CourseImpl.STUDENT_REGISTRATION_ALLOWED,
1534: Boolean.toString(joinAllowed));
1535: props.addProperty(CourseImpl.STUDENT_SWITCHING_ALLOWED, Boolean
1536: .toString(switchAllowed));
1537: try {
1538: siteService().save(site);
1539: if (joinAllowed != oldJoin) {
1540: postEvent("section.student.reg=" + joinAllowed, site
1541: .getReference());
1542: }
1543: if (switchAllowed != oldSwitch) {
1544: postEvent("section.student.switch=" + switchAllowed,
1545: site.getReference());
1546: }
1547:
1548: } catch (IdUnusedException ide) {
1549: log.error("Error saving site... could not find site "
1550: + site, ide);
1551: } catch (PermissionException pe) {
1552: log.error("Error saving site... permission denied for "
1553: + site, pe);
1554: }
1555: }
1556:
1557: /**
1558: * {@inheritDoc}
1559: */
1560: public boolean isSelfSwitchingAllowed(String courseUuid) {
1561: Reference ref = entityManager.newReference(courseUuid);
1562: String siteId = ref.getId();
1563: Site site;
1564: try {
1565: site = siteService().getSite(siteId);
1566: } catch (IdUnusedException e) {
1567: throw new RuntimeException("Can not find site "
1568: + courseUuid, e);
1569: }
1570: ResourceProperties props = site.getProperties();
1571: return Boolean
1572: .toString(true)
1573: .equals(
1574: props
1575: .getProperty(CourseImpl.STUDENT_SWITCHING_ALLOWED));
1576: }
1577:
1578: /**
1579: * {@inheritDoc}
1580: */
1581: public List<EnrollmentRecord> getUnsectionedEnrollments(
1582: String courseUuid, String category) {
1583: Reference siteRef = entityManager.newReference(courseUuid);
1584: String siteId = siteRef.getId();
1585:
1586: // Get all of the sections and userUids of enrolled students
1587: List siteEnrollments = getSiteEnrollments(siteId);
1588:
1589: // Get all userUids of students enrolled in sections of this category
1590: List<String> sectionedStudentUids = new ArrayList<String>();
1591: List categorySections = getSectionsInCategory(siteId, category);
1592: for (Iterator sectionIter = categorySections.iterator(); sectionIter
1593: .hasNext();) {
1594: CourseSection section = (CourseSection) sectionIter.next();
1595: List sectionUsers = getSectionEnrollments(section.getUuid());
1596:
1597: if (log.isDebugEnabled())
1598: log.debug("There are " + sectionUsers.size()
1599: + " students in section " + section.getUuid());
1600:
1601: for (Iterator userIter = sectionUsers.iterator(); userIter
1602: .hasNext();) {
1603: ParticipationRecord record = (ParticipationRecord) userIter
1604: .next();
1605: sectionedStudentUids.add(record.getUser().getUserUid());
1606: }
1607: }
1608:
1609: // Now generate the list of unsectioned enrollments by subtracting the two collections
1610: // Since the APIs return different kinds of objects, we need to iterate
1611: List<EnrollmentRecord> unsectionedEnrollments = new ArrayList<EnrollmentRecord>();
1612: for (Iterator iter = siteEnrollments.iterator(); iter.hasNext();) {
1613: EnrollmentRecord record = (EnrollmentRecord) iter.next();
1614: if (!sectionedStudentUids.contains(record.getUser()
1615: .getUserUid())) {
1616: unsectionedEnrollments.add(record);
1617: }
1618: }
1619: return unsectionedEnrollments;
1620: }
1621:
1622: /**
1623: * {@inheritDoc}
1624: */
1625: public Set<EnrollmentRecord> getSectionEnrollments(String userUid,
1626: String courseUuid) {
1627: // Get the user
1628: org.sakaiproject.user.api.User sakaiUser;
1629: try {
1630: sakaiUser = userDirectoryService.getUser(userUid);
1631: } catch (UserNotDefinedException ide) {
1632: log.error("Can not find user with id " + userUid);
1633: return new HashSet<EnrollmentRecord>();
1634: }
1635: User sectionUser = SakaiUtil.convertUser(sakaiUser);
1636:
1637: // Get all of the sections
1638: Reference siteRef = entityManager.newReference(courseUuid);
1639: String siteId = siteRef.getId();
1640: List sections = getSections(siteId);
1641:
1642: // Generate a set of sections for which this user is enrolled
1643: Set<EnrollmentRecord> sectionEnrollments = new HashSet<EnrollmentRecord>();
1644: for (Iterator sectionIter = sections.iterator(); sectionIter
1645: .hasNext();) {
1646: CourseSectionImpl section = (CourseSectionImpl) sectionIter
1647: .next();
1648: Group group = section.getGroup();
1649: Member member = group.getMember(userUid);
1650: if (member == null) {
1651: continue;
1652: }
1653: if (member.getRole().isAllowed(
1654: SectionAwareness.STUDENT_MARKER)) {
1655: sectionEnrollments.add(new EnrollmentRecordImpl(
1656: section, null, sectionUser));
1657: }
1658: }
1659: return sectionEnrollments;
1660: }
1661:
1662: /**
1663: * {@inheritDoc}
1664: */
1665: public User getSiteEnrollment(String siteContext, String studentUid) {
1666: return SakaiUtil.getUserFromSakai(studentUid);
1667: }
1668:
1669: public ExternalIntegrationConfig getConfiguration(Object obj) {
1670: if (config == null) {
1671: log
1672: .warn("No integration configuration property has been set. Using "
1673: + ExternalIntegrationConfig.MANUAL_DEFAULT);
1674: config = ExternalIntegrationConfig.MANUAL_DEFAULT;
1675: }
1676: return config;
1677: }
1678:
1679: // Dependency injection
1680:
1681: public void setAuthzGroupService(AuthzGroupService authzGroupService) {
1682: this .authzGroupService = authzGroupService;
1683: }
1684:
1685: public void setSessionManager(SessionManager sessionManager) {
1686: this .sessionManager = sessionManager;
1687: }
1688:
1689: public void setEntityManager(EntityManager entityManager) {
1690: this .entityManager = entityManager;
1691: }
1692:
1693: public void setSecurityService(SecurityService securityService) {
1694: this .securityService = securityService;
1695: }
1696:
1697: public void setUserDirectoryService(
1698: UserDirectoryService userDirectoryService) {
1699: this .userDirectoryService = userDirectoryService;
1700: }
1701:
1702: public void setEventTrackingService(
1703: EventTrackingService eventTrackingService) {
1704: this .eventTrackingService = eventTrackingService;
1705: }
1706:
1707: public void setConfig(String config) {
1708: if (ExternalIntegrationConfig.AUTOMATIC_MANDATORY.toString()
1709: .equals(config)) {
1710: this .config = ExternalIntegrationConfig.AUTOMATIC_MANDATORY;
1711: } else if (ExternalIntegrationConfig.AUTOMATIC_DEFAULT
1712: .toString().equals(config)) {
1713: this .config = ExternalIntegrationConfig.AUTOMATIC_DEFAULT;
1714: } else if (ExternalIntegrationConfig.MANUAL_DEFAULT.toString()
1715: .equals(config)) {
1716: this .config = ExternalIntegrationConfig.MANUAL_DEFAULT;
1717: } else if (ExternalIntegrationConfig.MANUAL_MANDATORY
1718: .toString().equals(config)) {
1719: this .config = ExternalIntegrationConfig.MANUAL_MANDATORY;
1720: } else {
1721: log.warn("Unknown section integration config specified: "
1722: + config + ". Using "
1723: + ExternalIntegrationConfig.MANUAL_DEFAULT);
1724: }
1725: }
1726:
1727: public void setCourseManagementService(
1728: CourseManagementService courseManagementService) {
1729: this .courseManagementService = courseManagementService;
1730: }
1731:
1732: public void setGroupProvider(GroupProvider groupProvider) {
1733: this.groupProvider = groupProvider;
1734: }
1735: }
|