/** @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;
  }
  private Collection<SyncableGroup> readSakaiGroups() throws IdUnusedException {
    Collection<SyncableGroup> result = new ArrayList<SyncableGroup>();

    Collection<AuthzGroup> sakaiGroups = new ArrayList<AuthzGroup>();
    Site site = SiteService.getSite(siteId);
    sakaiGroups.add(site);
    sakaiGroups.addAll(site.getGroups());

    for (AuthzGroup sakaiGroup : sakaiGroups) {
      List<UserWithRole> members = new ArrayList<UserWithRole>();
      HashSet<String> inactiveUsers = new HashSet<String>();

      // Load direct members of this group
      for (Member m : sakaiGroup.getMembers()) {
        if (!m.isActive()) {
          inactiveUsers.add(m.getUserEid());
          continue;
        }

        if (!m.isProvided()) {
          // Provided users will be handled separately below.
          members.add(new UserWithRole(m.getUserEid(), m.getRole().getId()));
        }
      }

      String provider = sakaiGroup.getProviderGroupId();

      // Plus those provided by sections
      if (provider != null) {
        HashSet<String> seenUsers = new HashSet<String>();

        for (String providerId : provider.split("\\+")) {
          for (org.sakaiproject.coursemanagement.api.Membership m :
              cms.getSectionMemberships(providerId)) {
            if (seenUsers.contains(m.getUserId()) || inactiveUsers.contains(m.getUserId())) {
              continue;
            }

            members.add(new UserWithRole(m.getUserId(), m.getRole()));
            seenUsers.add(m.getUserId());
          }
        }
      }

      result.add(new SyncableGroup(sakaiGroup.getId(), sakaiGroup.getDescription(), members));
    }

    return result;
  }
  protected void processRoster(
      HttpServletRequest request,
      HttpServletResponse response,
      String lti_message_type,
      Site site,
      String siteId,
      String placement_id,
      Properties pitch,
      String user_id,
      Map<String, Object> theMap)
      throws java.io.IOException {
    // Check for permission in placement
    String allowRoster = pitch.getProperty(LTIService.LTI_ALLOWROSTER);
    if (!"on".equals(allowRoster)) {
      doError(
          request,
          response,
          theMap,
          "outcomes.invalid",
          "lti_message_type=" + lti_message_type,
          null);
      return;
    }

    String roleMapProp = pitch.getProperty("rolemap");
    String releaseName = pitch.getProperty(LTIService.LTI_SENDNAME);
    String releaseEmail = pitch.getProperty(LTIService.LTI_SENDEMAILADDR);
    String assignment = pitch.getProperty("assignment");
    String allowOutcomes =
        ServerConfigurationService.getString(
            SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED,
            SakaiBLTIUtil.BASICLTI_OUTCOMES_ENABLED_DEFAULT);
    if (!"true".equals(allowOutcomes)) allowOutcomes = null;

    String maintainRole = site.getMaintainRole();

    SakaiBLTIUtil.pushAdvisor();
    boolean success = false;
    try {
      List<Map<String, Object>> lm = new ArrayList<Map<String, Object>>();
      Set<Member> members = site.getMembers();
      Map<String, String> roleMap = SakaiBLTIUtil.convertRoleMapPropToMap(roleMapProp);
      for (Member member : members) {
        Map<String, Object> mm = new TreeMap<String, Object>();
        Role role = member.getRole();
        String ims_user_id = member.getUserId();
        mm.put("/user_id", ims_user_id);
        String ims_role = "Learner";

        // If there is a role mapping, it has precedence over site.update
        if (roleMap.containsKey(role.getId())) {
          ims_role = roleMap.get(role.getId());
        } else if (ComponentManager.get(AuthzGroupService.class)
            .isAllowed(ims_user_id, SiteService.SECURE_UPDATE_SITE, "/site/" + siteId)) {
          ims_role = "Instructor";
        }

        // Using "/role" is inconsistent with to
        // http://developers.imsglobal.org/ext_membership.html. It
        // should be roles. If we can determine that nobody is using
        // the role tag, we should remove it.

        mm.put("/role", ims_role);
        mm.put("/roles", ims_role);
        User user = null;
        if ("true".equals(allowOutcomes) && assignment != null) {
          user = UserDirectoryService.getUser(ims_user_id);
          String placement_secret = pitch.getProperty(LTIService.LTI_PLACEMENTSECRET);
          String result_sourcedid =
              SakaiBLTIUtil.getSourceDID(user, placement_id, placement_secret);
          if (result_sourcedid != null) mm.put("/lis_result_sourcedid", result_sourcedid);
        }

        if ("on".equals(releaseName) || "on".equals(releaseEmail)) {
          if (user == null) user = UserDirectoryService.getUser(ims_user_id);
          if ("on".equals(releaseName)) {
            mm.put("/person_name_given", user.getFirstName());
            mm.put("/person_name_family", user.getLastName());
            mm.put("/person_name_full", user.getDisplayName());
          }
          if ("on".equals(releaseEmail)) {
            mm.put("/person_contact_email_primary", user.getEmail());
            mm.put("/person_sourcedid", user.getEid());
          }
        }

        Collection groups = site.getGroupsWithMember(ims_user_id);

        if (groups.size() > 0) {
          List<Map<String, Object>> lgm = new ArrayList<Map<String, Object>>();
          for (Iterator i = groups.iterator(); i.hasNext(); ) {
            Group group = (Group) i.next();
            Map<String, Object> groupMap = new HashMap<String, Object>();
            groupMap.put("/id", group.getId());
            groupMap.put("/title", group.getTitle());
            groupMap.put("/set", new HashMap(groupMap));
            lgm.add(groupMap);
          }
          mm.put("/groups/group", lgm);
        }

        lm.add(mm);
      }
      theMap.put("/message_response/members/member", lm);
      success = true;
    } catch (Exception e) {
      doError(request, response, theMap, "memberships.fail", "", e);
    } finally {
      SakaiBLTIUtil.popAdvisor();
    }

    if (!success) return;

    theMap.put("/message_response/statusinfo/codemajor", "Success");
    theMap.put("/message_response/statusinfo/severity", "Status");
    theMap.put("/message_response/statusinfo/codeminor", "fullsuccess");
    String theXml = XMLMap.getXML(theMap, true);
    PrintWriter out = response.getWriter();
    out.println(theXml);
    M_log.debug(theXml);
  }
  public void testSiteRoles() throws Exception {
    actAsUserEid(officialInstructorOfA);
    Site site = siteService.getSite(courseOfferingA);

    // Check for the officially enrolled (but waitlisted) student.
    Member member = site.getMember(waitListedStudentALec1);
    Assert.assertNotNull(member);
    Assert.assertEquals("Student", member.getRole().getId());

    // Check for the student who was added directly to the site.
    member = site.getMember(unofficialStudentALec1);
    Assert.assertNotNull(member);
    Assert.assertEquals("Student", member.getRole().getId());

    // Check for the expelled student.
    member = site.getMember(expelledStudentALec1);
    Assert.assertNull(member);

    // Check for the dropped student.
    member = site.getMember(droppedStudentALec1);
    Assert.assertNull(member);

    // Check for the enrollment record mapped to another role.
    member = site.getMember(taAsEnrollmentALec1);
    Assert.assertNotNull(member);
    Assert.assertEquals("Teaching Assistant", member.getRole().getId());

    // Check for the student in a discussion section.
    member = site.getMember(studentADis1);
    Assert.assertNotNull(member);
    Assert.assertEquals("Student", member.getRole().getId());

    // Check for a student in another course site.
    member = site.getMember(enrolledStudentBLec1);
    Assert.assertNull(member);

    // Check for non-integration of the TA.
    member = site.getMember(gsiALec1);
    Assert.assertNull(member);

    // Check for non-integration of the TA who also happens to be a DeptAdmin.
    member = site.getMember(deptAdminAndGsi);
    Assert.assertNull(member);

    // Check for the official instructor of the enrollment set.
    member = site.getMember(officialInstructorOfA);
    Assert.assertNotNull(member);
    Assert.assertEquals("Instructor", member.getRole().getId());

    // Check for non-integration of the course offering member in the instructor role.
    member = site.getMember(unofficialInstructorOfA);
    Assert.assertNull(member);

    // Check for non-integration of the department administrator.
    member = site.getMember(deptAdminOfADept);
    Assert.assertNull(member);

    // Check for non-integration of the course admin.
    member = site.getMember(adminOfOfferingA);
    Assert.assertNull(member);

    // Check for an instructor in another course site.
    member = site.getMember(instructorOfB);
    Assert.assertNull(member);

    // Check for another department's administrator.
    member = site.getMember(deptAdminOfBDept);
    Assert.assertNull(member);
  }