Index: lams_admin/src/java/org/lamsfoundation/lams/admin/web/UserManageAction.java =================================================================== diff -u -r5e39743e8bd500d0e121cf4c95b9c9fc77eb80fc -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_admin/src/java/org/lamsfoundation/lams/admin/web/UserManageAction.java (.../UserManageAction.java) (revision 5e39743e8bd500d0e121cf4c95b9c9fc77eb80fc) +++ lams_admin/src/java/org/lamsfoundation/lams/admin/web/UserManageAction.java (.../UserManageAction.java) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -57,115 +57,111 @@ /** * struts doclets * - * @struts:action path="/usermanage" - * validate="false" + * @struts:action path="/usermanage" validate="false" * - * @struts:action-forward name="userlist" - * path=".userlist" + * @struts:action-forward name="userlist" path=".userlist" */ public class UserManageAction extends Action { - - private static final Logger log = Logger.getLogger(UserManageAction.class); - private static IUserManagementService service; - private static MessageService messageService; - - @SuppressWarnings("unchecked") - public ActionForward execute(ActionMapping mapping, - ActionForm form, - HttpServletRequest request, - HttpServletResponse response) throws Exception { - - service = AdminServiceProxy.getService(getServlet().getServletContext()); - messageService = AdminServiceProxy.getMessageService(getServlet().getServletContext()); - - // get id of org to list users for - Integer orgId = WebUtil.readIntParam(request,"org",true); - if(orgId==null){ - orgId = (Integer)request.getAttribute("org"); - } - if((orgId==null)||(orgId<=0)){ - return forwardError(mapping, request, "error.org.invalid"); - } - log.debug("orgId: "+orgId); - - // get org name - Organisation organisation = (Organisation)service.findById(Organisation.class,orgId); - if(organisation==null) { - return forwardError(mapping, request, "error.org.invalid"); - } - String orgName = organisation.getName(); - log.debug("orgName: "+orgName); - - Organisation pOrg = organisation.getParentOrganisation(); - if(pOrg!=null){ - request.setAttribute("pOrgId",pOrg.getOrganisationId()); - request.setAttribute("pOrgName",pOrg.getName()); - } - OrganisationType orgType = organisation.getOrganisationType(); - request.setAttribute("orgType",orgType.getOrganisationTypeId()); - - // create form object - UserListDTO userManageForm = new UserListDTO(); - - Integer userId = ((UserDTO)SessionManager.getSession().getAttribute(AttributeNames.USER)).getUserID(); - Organisation orgOfCourseAdmin = (orgType.getOrganisationTypeId().equals(OrganisationType.CLASS_TYPE)) ? pOrg : organisation; - // check permission - Integer rootOrgId = service.getRootOrganisation().getOrganisationId(); - if(request.isUserInRole(Role.SYSADMIN) - || (service.isUserGlobalGroupAdmin() && !orgId.equals(rootOrgId))){ - userManageForm.setCourseAdminCanAddNewUsers(true); - userManageForm.setCourseAdminCanBrowseAllUsers(true); - request.setAttribute("canDeleteUser", true); - }else if((service.isUserInRole(userId,orgOfCourseAdmin.getOrganisationId(),Role.GROUP_ADMIN) - || service.isUserInRole(userId,orgOfCourseAdmin.getOrganisationId(),Role.GROUP_MANAGER)) - && !orgId.equals(rootOrgId)){ - userManageForm.setCourseAdminCanAddNewUsers(orgOfCourseAdmin.getCourseAdminCanAddNewUsers()); - userManageForm.setCourseAdminCanBrowseAllUsers(orgOfCourseAdmin.getCourseAdminCanBrowseAllUsers()); - }else{ - return forwardError(mapping, request, "error.authorisation"); - } - - userManageForm.setOrgId(orgId); - userManageForm.setOrgName(orgName); - List userManageBeans = service.getUserManageBeans(orgId); - Collections.sort(userManageBeans); - userManageForm.setUserManageBeans(userManageBeans); - request.setAttribute("UserManageForm", userManageForm); - - // heading - String[] args = { orgName }; - request.setAttribute("heading", messageService.getMessage("heading.manage.group.users", args)); - - // count roles in the org - HashMap roleCount = new HashMap(); - if (orgId.equals(rootOrgId)) { - roleCount.put(Role.SYSADMIN, Role.ROLE_SYSADMIN); - roleCount.put(Role.GROUP_ADMIN, Role.ROLE_GROUP_ADMIN); - } else { - roleCount.put(Role.LEARNER, Role.ROLE_LEARNER); - roleCount.put(Role.MONITOR, Role.ROLE_MONITOR); - roleCount.put(Role.AUTHOR, Role.ROLE_AUTHOR); - roleCount.put(Role.GROUP_MANAGER, Role.ROLE_GROUP_MANAGER); - roleCount.put(Role.GROUP_ADMIN, Role.ROLE_GROUP_ADMIN); - } - for (String role : roleCount.keySet()) { - Integer count = service.getCountRoleForOrg(orgId, roleCount.get(role)); - request.setAttribute(role.replace(' ', '_'), count); - } - - // count users in the org - // TODO use hql that does a count instead of getting whole objects - Integer numUsers = Integer.valueOf(service.getUsersFromOrganisation(orgId).size()); - args[0] = numUsers.toString(); - request.setAttribute("numUsers", messageService.getMessage("label.users.in.group", args)); - - return mapping.findForward("userlist"); + + private static final Logger log = Logger.getLogger(UserManageAction.class); + private static IUserManagementService service; + private static MessageService messageService; + + @SuppressWarnings("unchecked") + public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws Exception { + + service = AdminServiceProxy.getService(getServlet().getServletContext()); + messageService = AdminServiceProxy.getMessageService(getServlet().getServletContext()); + + // get id of org to list users for + Integer orgId = WebUtil.readIntParam(request, "org", true); + if (orgId == null) { + orgId = (Integer) request.getAttribute("org"); } - - private ActionForward forwardError(ActionMapping mapping, HttpServletRequest request, String key) { - request.setAttribute("errorName","UserManageAction"); - request.setAttribute("errorMessage",messageService.getMessage(key)); - return mapping.findForward("error"); + if ((orgId == null) || (orgId <= 0)) { + return forwardError(mapping, request, "error.org.invalid"); } + log.debug("orgId: " + orgId); + // get org name + Organisation organisation = (Organisation) service.findById(Organisation.class, orgId); + if (organisation == null) { + return forwardError(mapping, request, "error.org.invalid"); + } + String orgName = organisation.getName(); + log.debug("orgName: " + orgName); + + Organisation pOrg = organisation.getParentOrganisation(); + if (pOrg != null) { + request.setAttribute("pOrgId", pOrg.getOrganisationId()); + request.setAttribute("pOrgName", pOrg.getName()); + } + OrganisationType orgType = organisation.getOrganisationType(); + request.setAttribute("orgType", orgType.getOrganisationTypeId()); + + // create form object + UserListDTO userManageForm = new UserListDTO(); + + Integer userId = ((UserDTO) SessionManager.getSession().getAttribute(AttributeNames.USER)).getUserID(); + Organisation orgOfCourseAdmin = (orgType.getOrganisationTypeId().equals(OrganisationType.CLASS_TYPE)) ? pOrg + : organisation; + // check permission + Integer rootOrgId = service.getRootOrganisation().getOrganisationId(); + if (request.isUserInRole(Role.SYSADMIN) || (service.isUserGlobalGroupAdmin() && !orgId.equals(rootOrgId))) { + userManageForm.setCourseAdminCanAddNewUsers(true); + userManageForm.setCourseAdminCanBrowseAllUsers(true); + request.setAttribute("canDeleteUser", true); + } else if ((service.isUserInRole(userId, orgOfCourseAdmin.getOrganisationId(), Role.GROUP_ADMIN) + || service.isUserInRole(userId, orgOfCourseAdmin.getOrganisationId(), Role.GROUP_MANAGER)) + && !orgId.equals(rootOrgId)) { + userManageForm.setCourseAdminCanAddNewUsers(orgOfCourseAdmin.getCourseAdminCanAddNewUsers()); + userManageForm.setCourseAdminCanBrowseAllUsers(orgOfCourseAdmin.getCourseAdminCanBrowseAllUsers()); + } else { + return forwardError(mapping, request, "error.authorisation"); + } + + userManageForm.setOrgId(orgId); + userManageForm.setOrgName(orgName); + List userManageBeans = service.getUserManageBeans(orgId); + Collections.sort(userManageBeans); + userManageForm.setUserManageBeans(userManageBeans); + request.setAttribute("UserManageForm", userManageForm); + + // heading + String[] args = { orgName }; + request.setAttribute("heading", messageService.getMessage("heading.manage.group.users", args)); + + // count roles in the org + HashMap roleCount = new HashMap(); + if (orgId.equals(rootOrgId)) { + roleCount.put(Role.SYSADMIN, Role.ROLE_SYSADMIN); + roleCount.put(Role.GROUP_ADMIN, Role.ROLE_GROUP_ADMIN); + } else { + roleCount.put(Role.LEARNER, Role.ROLE_LEARNER); + roleCount.put(Role.MONITOR, Role.ROLE_MONITOR); + roleCount.put(Role.AUTHOR, Role.ROLE_AUTHOR); + roleCount.put(Role.GROUP_MANAGER, Role.ROLE_GROUP_MANAGER); + roleCount.put(Role.GROUP_ADMIN, Role.ROLE_GROUP_ADMIN); + } + for (String role : roleCount.keySet()) { + Integer count = service.getCountRoleForOrg(orgId, roleCount.get(role), null); + request.setAttribute(role.replace(' ', '_'), count); + } + + // count users in the org + // TODO use hql that does a count instead of getting whole objects + Integer numUsers = Integer.valueOf(service.getUsersFromOrganisation(orgId).size()); + args[0] = numUsers.toString(); + request.setAttribute("numUsers", messageService.getMessage("label.users.in.group", args)); + + return mapping.findForward("userlist"); + } + + private ActionForward forwardError(ActionMapping mapping, HttpServletRequest request, String key) { + request.setAttribute("errorName", "UserManageAction"); + request.setAttribute("errorMessage", messageService.getMessage(key)); + return mapping.findForward("error"); + } + } Index: lams_build/lib/lams/lams.jar =================================================================== diff -u -r95574f488cd396cd8e0e246a091a23dca4190e4f -r509d15bcc570bff483a13152ef8c45cbae767e78 Binary files differ Index: lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILessonDAO.java =================================================================== diff -u -r95574f488cd396cd8e0e246a091a23dca4190e4f -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILessonDAO.java (.../ILessonDAO.java) (revision 95574f488cd396cd8e0e246a091a23dca4190e4f) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILessonDAO.java (.../ILessonDAO.java) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -25,6 +25,7 @@ import java.util.Date; import java.util.List; +import java.util.Map; import org.lamsfoundation.lams.dao.IBaseDAO; import org.lamsfoundation.lams.lesson.Lesson; @@ -144,6 +145,13 @@ Integer getCountLearnersByLesson(long lessonId, String searchPhrase); /** + * Maps users from an organisation with the given role to a boolean value saying whether they participate in the + * given lesson. + */ + Map getUsersWithLessonParticipation(Long lessonId, String role, String searchPhrase, Integer limit, + Integer offset, boolean orderAscending); + + /** * Get all the preview lessons more with the creation date before the given date. * * @param startDate Index: lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LessonDAO.java =================================================================== diff -u -r95574f488cd396cd8e0e246a091a23dca4190e4f -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LessonDAO.java (.../LessonDAO.java) (revision 95574f488cd396cd8e0e246a091a23dca4190e4f) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LessonDAO.java (.../LessonDAO.java) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -24,7 +24,9 @@ package org.lamsfoundation.lams.lesson.dao.hibernate; import java.util.Date; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.apache.commons.lang.StringUtils; import org.hibernate.FetchMode; @@ -71,9 +73,17 @@ private final static String LOAD_LEARNERS_BY_LESSON = "FROM Lesson AS lesson " + "INNER JOIN lesson.lessonClass AS lessonClass INNER JOIN lessonClass.groups AS groups " - + "INNER JOIN groups.users AS users " - + "WHERE lesson.id = :lessonId AND groups.groupName NOT LIKE '%Staff%'"; + + "INNER JOIN groups.users AS users WHERE lesson.id = :lessonId AND lessonClass.staffGroup != groups"; + private final static String LOAD_USERS_WITH_LESSON_PARTICIPATION = "SELECT users.*, ug.user_id IS NOT NULL AS participant " + + "FROM lams_lesson AS l " + + "JOIN lams_user_organisation AS uo ON l.lesson_id = :lessonId AND l.organisation_id = uo.organisation_id " + + "JOIN lams_user_organisation_role AS r ON r.role_id = :roleId AND r.user_organisation_id = uo.user_organisation_id " + + "JOIN lams_user AS users ON uo.user_id = users.user_id " + + "JOIN lams_grouping AS ging ON l.class_grouping_id = ging.grouping_id " + + "JOIN lams_group AS g ON g.group_id ging.staff_group_id AND g.grouping_id = ging.grouping_id " + + "LEFT JOIN lams_user_group AS ug ON ug.group_id = g.group_id AND users.user_id = ug.user_id"; + /** * Retrieves the Lesson. Used in instances where it cannot be lazy loaded so it forces an initialize. * @@ -199,10 +209,21 @@ @Override public List getLearnersByLesson(final Long lessonId, String searchPhrase, final Integer limit, final Integer offset, boolean orderAscending) { - final String queryText = LessonDAO.buildLearnersByLessonQuery(false, searchPhrase, orderAscending); + StringBuilder queryTextBuilder = new StringBuilder("SELECT users ").append(LessonDAO.LOAD_LEARNERS_BY_LESSON); + if (!StringUtils.isBlank(searchPhrase)) { + String[] tokens = searchPhrase.trim().split("\\s+"); + for (String token : tokens) { + queryTextBuilder.append(" AND (users.firstName LIKE '%").append(token) + .append("%' OR users.lastName LIKE '%").append(token).append("%' OR users.login LIKE '%") + .append(token).append("%')"); + } + } + String order = orderAscending ? "ASC" : "DESC"; + queryTextBuilder.append(" ORDER BY users.firstName ").append(order).append(", users.lastName ").append(order) + .append(", users.login ").append(order); + final String queryText = queryTextBuilder.toString(); HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); - return (List) hibernateTemplate.execute(new HibernateCallback() { @Override public Object doInHibernate(Session session) throws HibernateException { @@ -220,7 +241,18 @@ @Override public Integer getCountLearnersByLesson(final long lessonId, String searchPhrase) { - final String queryText = LessonDAO.buildLearnersByLessonQuery(true, searchPhrase, true); + StringBuilder queryTextBuilder = new StringBuilder("SELECT COUNT(*) ") + .append(LessonDAO.LOAD_LEARNERS_BY_LESSON); + if (!StringUtils.isBlank(searchPhrase)) { + String[] tokens = searchPhrase.trim().split("\\s+"); + for (String token : tokens) { + queryTextBuilder.append(" AND (users.firstName LIKE '%").append(token) + .append("%' OR users.lastName LIKE '%").append(token).append("%' OR users.login LIKE '%") + .append(token).append("%')"); + } + } + final String queryText = queryTextBuilder.toString(); + HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); return (Integer) hibernateTemplate.execute(new HibernateCallback() { @Override @@ -426,21 +458,58 @@ }); } - private static String buildLearnersByLessonQuery(boolean count, String searchPhrase, boolean orderAscending) { - StringBuilder queryText = new StringBuilder("SELECT ").append(count ? "COUNT(*) " : "users ") - .append(LessonDAO.LOAD_LEARNERS_BY_LESSON); + /** + * Maps users from an organisation with the given role to a boolean value saying whether they participate in the + * given lesson. + */ + @SuppressWarnings("unchecked") + @Override + public Map getUsersWithLessonParticipation(final Long lessonId, final String role, + String searchPhrase, final Integer limit, final Integer offset, boolean orderAscending) { + String queryTextBase = LessonDAO.LOAD_USERS_WITH_LESSON_PARTICIPATION; + // whether to exclude staff group or make it the only group that counts + queryTextBase = queryTextBase.replace("", role.equals(Role.MONITOR) ? "=" : "<>"); + StringBuilder queryTextBuilder = new StringBuilder(queryTextBase); + // is it a result of a search? if (!StringUtils.isBlank(searchPhrase)) { + queryTextBuilder.append(" WHERE"); String[] tokens = searchPhrase.trim().split("\\s+"); for (String token : tokens) { - queryText.append(" AND (users.firstName LIKE '%").append(token).append("%' OR users.lastName LIKE '%") - .append(token).append("%' OR users.login LIKE '%").append(token).append("%')"); + queryTextBuilder.append(" (users.first_name LIKE '%").append(token) + .append("%' OR users.last_name LIKE '%").append(token).append("%' OR users.login LIKE '%") + .append(token).append("%') AND"); } + queryTextBuilder.delete(queryTextBuilder.length() - 4, queryTextBuilder.length()); } - if (!count) { - String order = orderAscending ? "ASC" : "DESC"; - queryText.append(" ORDER BY users.firstName ").append(order).append(", users.lastName ").append(order) - .append(", users.login ").append(order); - } - return queryText.toString(); + String order = orderAscending ? "ASC" : "DESC"; + queryTextBuilder.append(" ORDER BY users.first_name ").append(order).append(", users.last_name ").append(order) + .append(", users.login ").append(order); + final String queryText = queryTextBuilder.toString(); + + HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); + return (Map) hibernateTemplate.execute(new HibernateCallback() { + @Override + public Object doInHibernate(Session session) throws HibernateException { + // the query returns User object and 0/1 value saying if he is a part of the lesson + Query query = session.createSQLQuery(queryText).addEntity(User.class).addScalar("participant") + .setLong("lessonId", lessonId) + .setInteger("roleId", role.equals(Role.MONITOR) ? Role.ROLE_MONITOR : Role.ROLE_LEARNER); + if (limit != null) { + query.setMaxResults(limit); + } + if (offset != null) { + query.setFirstResult(offset); + } + List resultQuery = query.list(); + + // this map keeps the insertion order + Map result = new LinkedHashMap(); + // make the result easier to process + for (Object[] entry : resultQuery) { + result.put((User) entry[0], ((Integer) entry[1]).equals(1)); + } + return result; + } + }); } } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/lesson/service/ILessonService.java =================================================================== diff -u -r95574f488cd396cd8e0e246a091a23dca4190e4f -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_common/src/java/org/lamsfoundation/lams/lesson/service/ILessonService.java (.../ILessonService.java) (revision 95574f488cd396cd8e0e246a091a23dca4190e4f) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/service/ILessonService.java (.../ILessonService.java) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -76,6 +76,13 @@ boolean orderAscending); /** + * Maps users from an organisation with the given role to a boolean value saying whether they participate in the + * given lesson. + */ + Map getUsersWithLessonParticipation(Long lessonId, String role, String searchPhrase, Integer limit, + Integer offset, boolean orderAscending); + + /** * Get the count of all the learners who are a part of the lesson class. */ Integer getCountLessonLearners(Long lessonId, String searchPhrase); @@ -243,6 +250,11 @@ void addLearners(Lesson lesson, Collection users) throws LessonServiceException; /** + * Removes the learner from the lesson. + */ + boolean removeLearner(Long lessonId, Integer userId); + + /** * Set the learners in a lesson class. Learners not in the users collection will be removed. To be called within * LAMS. * @@ -285,6 +297,9 @@ */ void addStaffMembers(Lesson lesson, Collection users) throws LessonServiceException; + // removes the staff member from the lesson + boolean removeStaffMember(Long lessonId, Integer userId); + /** * Set the staff members in a lesson class. Staff members not in the users collection will be removed. To be called * within LAMS. Index: lams_common/src/java/org/lamsfoundation/lams/lesson/service/LessonService.java =================================================================== diff -u -r95574f488cd396cd8e0e246a091a23dca4190e4f -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_common/src/java/org/lamsfoundation/lams/lesson/service/LessonService.java (.../LessonService.java) (revision 95574f488cd396cd8e0e246a091a23dca4190e4f) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/service/LessonService.java (.../LessonService.java) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -123,6 +123,12 @@ } @Override + public Map getUsersWithLessonParticipation(Long lessonId, String role, String searchPhrase, + Integer limit, Integer offset, boolean orderAscending) { + return lessonDAO.getUsersWithLessonParticipation(lessonId, role, searchPhrase, limit, offset, orderAscending); + } + + @Override public Integer getCountLessonLearners(Long lessonId, String searchPhrase) { return lessonDAO.getCountLearnersByLesson(lessonId, searchPhrase); } @@ -341,6 +347,20 @@ } @Override + public boolean removeLearner(Long lessonId, Integer userId) { + Lesson lesson = lessonDAO.getLesson(lessonId); + LessonClass lessonClass = lesson.getLessonClass(); + User user = (User) baseDAO.find(User.class, userId); + Group learnerGroup = lessonClass.getGroupBy(user); + boolean result = learnerGroup.getUsers().remove(user); + + if (result) { + lessonClassDAO.updateLessonClass(lessonClass); + } + return result; + } + + @Override public void addLearners(Long lessonId, Integer[] userIds) throws LessonServiceException { Lesson lesson = lessonDAO.getLesson(lessonId); @@ -420,6 +440,20 @@ } @Override + public boolean removeStaffMember(Long lessonId, Integer userId) { + Lesson lesson = lessonDAO.getLesson(lessonId); + LessonClass lessonClass = lesson.getLessonClass(); + User user = (User) baseDAO.find(User.class, userId); + Group staffGroup = lessonClass.getStaffGroup(); + boolean result = staffGroup.getUsers().remove(user); + + if (result) { + lessonClassDAO.updateLessonClass(lessonClass); + } + return result; + } + + @Override public void addStaffMembers(Long lessonId, Integer[] userIds) throws LessonServiceException { Lesson lesson = lessonDAO.getLesson(lessonId); Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/dao/IRoleDAO.java =================================================================== diff -u -r7ab9e2377fd1ff4c3c5554bd1f6dffe3384f6f9a -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/dao/IRoleDAO.java (.../IRoleDAO.java) (revision 7ab9e2377fd1ff4c3c5554bd1f6dffe3384f6f9a) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/dao/IRoleDAO.java (.../IRoleDAO.java) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -23,41 +23,17 @@ /* $$Id$$ */ package org.lamsfoundation.lams.usermanagement.dao; -import java.util.List; - import org.lamsfoundation.lams.dao.IBaseDAO; import org.lamsfoundation.lams.usermanagement.Organisation; import org.lamsfoundation.lams.usermanagement.User; /** - * Inteface defines Lesson DAO Methods - * @author chris + * Inteface defines Role DAO Methods */ -public interface IRoleDAO extends IBaseDAO -{ - /** - * - * @param userId - * @param roleId - * @param organisation - * @return - */ - public User getUserByOrganisationAndRole(final Integer userId, final Integer roleId, final Organisation organisation); - - - /** - * - * @param roleId - * @return - */ - public Integer getCountRoleForSystem(final Integer roleId); - - /** - * Get number of users with roleId in orgId. - * @param roleId - * @param orgId - * @return - */ - public Integer getCountRoleForOrg(final Integer roleId, final Integer orgId); +public interface IRoleDAO extends IBaseDAO { + User getUserByOrganisationAndRole(Integer userId, Integer roleId, Organisation organisation); -} + Integer getCountRoleForSystem(final Integer roleId); + + Integer getCountRoleForOrg(Integer roleId, Integer orgId, String searchPhrase); +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/dao/hibernate/RoleDAO.java =================================================================== diff -u -r7ab9e2377fd1ff4c3c5554bd1f6dffe3384f6f9a -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/dao/hibernate/RoleDAO.java (.../RoleDAO.java) (revision 7ab9e2377fd1ff4c3c5554bd1f6dffe3384f6f9a) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/dao/hibernate/RoleDAO.java (.../RoleDAO.java) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -23,6 +23,7 @@ /* $$Id$$ */ package org.lamsfoundation.lams.usermanagement.dao.hibernate; +import org.apache.commons.lang.StringUtils; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; @@ -33,80 +34,80 @@ import org.lamsfoundation.lams.usermanagement.dao.IRoleDAO; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.HibernateTemplate; + /** * Hibernate implementation of IRoleDAO + * * @author chris */ -public class RoleDAO extends BaseDAO implements IRoleDAO -{ - private final static String LOAD_USER_BY_ORG_AND_ROLE = - "select u " - +"from User u, UserOrganisation uo, UserOrganisationRole uor " - +"where u.id = :userId and " - +"u.id = uo.user.id and " - +"uo.organisation = :org and " - +"uor.userOrganisation.id = uo.id and " - +"uor.role.id = :roleId"; - - private final static String COUNT_ROLE = "select count(distinct userOrganisationRole.userOrganisation.user)" - + " from "+UserOrganisationRole.class.getName()+" userOrganisationRole" - + " where userOrganisationRole.role.roleId = :roleId"; +public class RoleDAO extends BaseDAO implements IRoleDAO { + private final static String LOAD_USER_BY_ORG_AND_ROLE = "select u " + + "from User u, UserOrganisation uo, UserOrganisationRole uor " + "where u.id = :userId and " + + "u.id = uo.user.id and " + "uo.organisation = :org and " + "uor.userOrganisation.id = uo.id and " + + "uor.role.id = :roleId"; - private final static String COUNT_ROLE_FOR_ORG = "select count(distinct uor.userOrganisation.user)" - + " from "+UserOrganisationRole.class.getName()+" uor" - + " where uor.role.roleId = :roleId" - + " and uor.userOrganisation.organisation.organisationId = :orgId"; - - public User getUserByOrganisationAndRole(final Integer userId, final Integer roleId, final Organisation organisation) - { - HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); + private final static String COUNT_ROLE = "select count(distinct userOrganisationRole.userOrganisation.user)" + + " from " + UserOrganisationRole.class.getName() + " userOrganisationRole" + + " where userOrganisationRole.role.roleId = :roleId"; - return (User)hibernateTemplate.execute( - new HibernateCallback() - { - public Object doInHibernate(Session session) throws HibernateException - { - return session.createQuery(LOAD_USER_BY_ORG_AND_ROLE) - .setInteger("userId",userId) - .setEntity("org", organisation) - .setInteger("roleId",roleId) - .uniqueResult(); - } - } - ); + private final static String COUNT_ROLE_FOR_ORG = "select count(distinct uor.userOrganisation.user) from " + + UserOrganisationRole.class.getName() + " uor where uor.role.roleId = :roleId" + + " and uor.userOrganisation.organisation.organisationId = :orgId"; + + @Override + public User getUserByOrganisationAndRole(final Integer userId, final Integer roleId, + final Organisation organisation) { + HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); + + return (User) hibernateTemplate.execute(new HibernateCallback() { + @Override + public Object doInHibernate(Session session) throws HibernateException { + return session.createQuery(RoleDAO.LOAD_USER_BY_ORG_AND_ROLE).setInteger("userId", userId) + .setEntity("org", organisation).setInteger("roleId", roleId).uniqueResult(); + } + }); } - - public Integer getCountRoleForSystem(final Integer roleId) - { - HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); - return (Integer) hibernateTemplate.execute(new HibernateCallback() { - public Object doInHibernate(Session session) - throws HibernateException { - Query query = session.createQuery(COUNT_ROLE); - query.setInteger("roleId", roleId.intValue()); - Object value = query.uniqueResult(); - return new Integer (((Number)value).intValue()); - } - }); - + @Override + public Integer getCountRoleForSystem(final Integer roleId) { + HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); + + return (Integer) hibernateTemplate.execute(new HibernateCallback() { + @Override + public Object doInHibernate(Session session) throws HibernateException { + Query query = session.createQuery(RoleDAO.COUNT_ROLE); + query.setInteger("roleId", roleId.intValue()); + Object value = query.uniqueResult(); + return new Integer(((Number) value).intValue()); + } + }); + } - - public Integer getCountRoleForOrg(final Integer roleId, final Integer orgId) - { - HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); - return (Integer) hibernateTemplate.execute(new HibernateCallback() { - public Object doInHibernate(Session session) - throws HibernateException { - Query query = session.createQuery(COUNT_ROLE_FOR_ORG); - query.setInteger("roleId", roleId.intValue()); - query.setInteger("orgId", orgId.intValue()); - Object value = query.uniqueResult(); - return new Integer (((Number)value).intValue()); - } - }); + @Override + public Integer getCountRoleForOrg(final Integer roleId, final Integer orgId, String searchPhrase) { + StringBuilder queryTextBuilder = new StringBuilder(RoleDAO.COUNT_ROLE_FOR_ORG); + if (!StringUtils.isBlank(searchPhrase)) { + String[] tokens = searchPhrase.trim().split("\\s+"); + for (String token : tokens) { + queryTextBuilder.append(" AND (uor.userOrganisation.user.firstName LIKE '%").append(token) + .append("%' OR uor.userOrganisation.user.lastName LIKE '%").append(token) + .append("%' OR uor.userOrganisation.user.login LIKE '%").append(token).append("%')"); + } + } + final String queryText = queryTextBuilder.toString(); + + HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); + return (Integer) hibernateTemplate.execute(new HibernateCallback() { + @Override + public Object doInHibernate(Session session) throws HibernateException { + Query query = session.createQuery(queryText); + query.setInteger("roleId", roleId.intValue()); + query.setInteger("orgId", orgId.intValue()); + Object value = query.uniqueResult(); + return new Integer(((Number) value).intValue()); + } + }); } - } Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/IUserManagementService.java =================================================================== diff -u -r76f496c7c7d8cafecbe2b6c0a85a408c60d568b8 -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/IUserManagementService.java (.../IUserManagementService.java) (revision 76f496c7c7d8cafecbe2b6c0a85a408c60d568b8) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/IUserManagementService.java (.../IUserManagementService.java) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -271,8 +271,8 @@ * filters results by course name. It can be null and then doesn't affect results * @return */ - List getPagedCourses(final Integer parentOrgId, final Integer typeId, final Integer stateId, - int page, int size, String sortBy, String sortOrder, String searchString); + List getPagedCourses(final Integer parentOrgId, final Integer typeId, final Integer stateId, int page, + int size, String sortBy, String sortOrder, String searchString); /** * Counts courses with specified type, state and parent course. @@ -456,7 +456,7 @@ * @param roleId * @return */ - Integer getCountRoleForOrg(Integer orgId, Integer roleId); + Integer getCountRoleForOrg(Integer orgId, Integer roleId, String searchPhrase); /** * Get default flash theme of server. Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/UserManagementService.java =================================================================== diff -u -r76f496c7c7d8cafecbe2b6c0a85a408c60d568b8 -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/UserManagementService.java (.../UserManagementService.java) (revision 76f496c7c7d8cafecbe2b6c0a85a408c60d568b8) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/service/UserManagementService.java (.../UserManagementService.java) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -110,8 +110,8 @@ private IAuditService getAuditService() { if (UserManagementService.auditService == null) { - WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(HttpSessionManager - .getInstance().getServletContext()); + WebApplicationContext ctx = WebApplicationContextUtils + .getWebApplicationContext(HttpSessionManager.getInstance().getServletContext()); UserManagementService.auditService = (IAuditService) ctx.getBean("auditService"); } return UserManagementService.auditService; @@ -341,7 +341,8 @@ // now, process any children of this org Organisation childOrganisation = userOrganisation.getOrganisation(); if (org.getChildOrganisations().size() > 0) { - getChildOrganisations(user, childOrganisation, restrictToRoleNames, restrictToClassIds, dtolist); + getChildOrganisations(user, childOrganisation, restrictToRoleNames, restrictToClassIds, + dtolist); } } } @@ -380,8 +381,8 @@ Map map = new HashMap(); map.put("user.userId", user.getUserId()); map.put("organisation.organisationId", organisationId); - UserOrganisation userOrganisation = (UserOrganisation) baseDAO - .findByProperties(UserOrganisation.class, map).get(0); + UserOrganisation userOrganisation = (UserOrganisation) baseDAO.findByProperties(UserOrganisation.class, map) + .get(0); OrganisationDTO dto = userOrganisation.getOrganisation().getOrganisationDTO(); addRolesToDTO(null, userOrganisation, dto); return dto; @@ -431,7 +432,7 @@ String query = "SELECT uo.user FROM UserOrganisation uo INNER JOIN uo.userOrganisationRoles r WHERE uo.organisation.organisationId=" + organisationID + " AND r.role.name= '" + roleName + "'"; List queryResult = (List) baseDAO.find(query); - + for (User user : queryResult) { if (isFlashCall && !getUser) { users.add(user.getUserFlashDTO()); @@ -441,14 +442,15 @@ users.add(user.getUserDTO()); } } - + return users; } @Override public Organisation getRootOrganisation() { - return (Organisation) baseDAO.findByProperty(Organisation.class, "organisationType.organisationTypeId", - OrganisationType.ROOT_TYPE).get(0); + return (Organisation) baseDAO + .findByProperty(Organisation.class, "organisationType.organisationTypeId", OrganisationType.ROOT_TYPE) + .get(0); } @Override @@ -613,8 +615,8 @@ User user = (User) findById(User.class, m.getUserID()); UserOrganisation uo = new UserOrganisation(user, organisation); log.debug("adding course manager: " + user.getUserId() + " as staff"); - UserOrganisationRole uor = new UserOrganisationRole(uo, (Role) findById(Role.class, - Role.ROLE_MONITOR)); + UserOrganisationRole uor = new UserOrganisationRole(uo, + (Role) findById(Role.class, Role.ROLE_MONITOR)); HashSet uors = new HashSet(); uors.add(uor); uo.setUserOrganisationRoles(uors); @@ -1071,8 +1073,8 @@ } } - public Integer getCountRoleForOrg(Integer orgId, Integer roleId) { - Integer count = roleDAO.getCountRoleForOrg(roleId, orgId); + public Integer getCountRoleForOrg(Integer orgId, Integer roleId, String searchPhrase) { + Integer count = roleDAO.getCountRoleForOrg(roleId, orgId, searchPhrase); if (count != null) { return count; } else { Index: lams_monitoring/conf/language/lams/ApplicationResources_en_AU.properties =================================================================== diff -u -rf07b6d46b4d80759f57539f8cdcc37fa0339fce3 -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_monitoring/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision f07b6d46b4d80759f57539f8cdcc37fa0339fce3) +++ lams_monitoring/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -298,7 +298,6 @@ lesson.learner.url =Learner URL: button.select =Select lesson.copy.prompt =(now press Ctrl+C to copy to clipboard) -learner.group.select.all =Select/Unselect all email.notifications.problems.sending.emails =Some problems occurred while sending emails. Please, contact your system administrator. learner.group.remove.progress =You are about to remove student(s) from a lesson. The student(s) will not have access to this lesson any longer. Do you also want to remove the student(s) progress? force.complete.remove.content =You are moving learner "[0]" to activity "[1]". You can opt to delete the content of activities that have been previously done, so the learner has to enter the content/answers again. Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java =================================================================== diff -u -rfbb5dc973034ba3d8ab19914110ce6eb3d253cf9 -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java (.../MonitoringAction.java) (revision fbb5dc973034ba3d8ab19914110ce6eb3d253cf9) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java (.../MonitoringAction.java) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -38,6 +38,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; @@ -136,8 +137,9 @@ private static final String ERROR = "error"; private static final DateFormat LESSON_SCHEDULING_DATETIME_FORMAT = new SimpleDateFormat("MM/dd/yy HH:mm"); - private static final Integer LATEST_LEARNER_PROGRESS_LESSON_DISPLAY_LIMIT = 53; - private static final Integer LATEST_LEARNER_PROGRESS_ACTIVITY_DISPLAY_LIMIT = 7; + private static final int LATEST_LEARNER_PROGRESS_LESSON_DISPLAY_LIMIT = 53; + private static final int LATEST_LEARNER_PROGRESS_ACTIVITY_DISPLAY_LIMIT = 7; + private static final int USER_PAGE_SIZE = 10; private static IAuditService auditService; @@ -149,6 +151,8 @@ private static IMonitoringService monitoringService; + private static IUserManagementService userManagementService; + private Integer getUserId() { HttpSession ss = SessionManager.getSession(); UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER); @@ -668,8 +672,8 @@ boolean orderAscending = WebUtil.readBooleanParam(request, "orderAscending", true); long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); - List learners = getLessonService().getLessonLearners(lessonId, null, 10, (pageNumber - 1) * 10, - orderAscending); + List learners = getLessonService().getLessonLearners(lessonId, null, MonitoringAction.USER_PAGE_SIZE, + (pageNumber - 1) * MonitoringAction.USER_PAGE_SIZE, orderAscending); JSONArray learnersJSON = new JSONArray(); for (User learner : learners) { learnersJSON.put(WebUtil.userToJSON(learner)); @@ -688,40 +692,46 @@ /** * Gets learners or monitors of the lesson and organisation containing it. */ - @SuppressWarnings("unchecked") public ActionForward getClassMembers(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, JSONException { long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); - String role = WebUtil.readStrParam(request, AttributeNames.PARAM_ROLE); - boolean getMonitors = Role.MONITOR.equalsIgnoreCase(role); Lesson lesson = getLessonService().getLesson(lessonId); - Set classUsers = getMonitors ? lesson.getLessonClass().getStaffGroup().getUsers() - : lesson.getLessonClass().getLearners(); - JSONArray responseJSON = new JSONArray(); + String role = WebUtil.readStrParam(request, AttributeNames.PARAM_ROLE); + boolean isMonitor = role.equals(Role.MONITOR); + User creator = isMonitor ? lesson.getUser() : null; + Integer currentUserId = isMonitor ? getUserId() : null; + String searchPhrase = request.getParameter("searchPhrase"); + Integer pageNumber = WebUtil.readIntParam(request, "pageNumber", true); + if (pageNumber == null) { + pageNumber = 1; + } + boolean orderAscending = WebUtil.readBooleanParam(request, "orderAscending", true); + JSONObject responseJSON = new JSONObject(); - for (User user : classUsers) { + // find organisation users and whether they participate in the current lesson + Map users = getLessonService().getUsersWithLessonParticipation(lessonId, role, searchPhrase, + MonitoringAction.USER_PAGE_SIZE, (pageNumber - 1) * MonitoringAction.USER_PAGE_SIZE, orderAscending); + + // if the result is less then page size, then no need for full check of user count + Integer userCount = users.size() < MonitoringAction.USER_PAGE_SIZE ? users.size() + : getUserManagementService().getCountRoleForOrg(lesson.getOrganisation().getOrganisationId(), + isMonitor ? Role.ROLE_MONITOR : Role.ROLE_LEARNER, searchPhrase); + + responseJSON.put("userCount", userCount); + + JSONArray usersJSON = new JSONArray(); + for (Entry userEntry : users.entrySet()) { + User user = userEntry.getKey(); JSONObject userJSON = WebUtil.userToJSON(user); - // mark that this user is a class member - userJSON.put("classMember", true); - if (lesson.getUser().equals(user)) { - // mark this user is lesson author - userJSON.put("lessonCreator", true); + userJSON.put("classMember", userEntry.getValue()); + // teacher can't remove lesson creator and himself from the lesson staff + if (isMonitor && (creator.getUserId().equals(user.getUserId()) || currentUserId.equals(user.getUserId()))) { + userJSON.put("readonly", true); } - responseJSON.put(userJSON); + usersJSON.put(userJSON); } + responseJSON.put("users", usersJSON); - IUserManagementService userManagementService = MonitoringServiceProxy - .getUserManagementService(getServlet().getServletContext()); - List orgUsers = userManagementService.getUsersFromOrganisationByRole( - lesson.getOrganisation().getOrganisationId(), getMonitors ? Role.MONITOR : Role.LEARNER, false, true); - for (User user : orgUsers) { - if (!classUsers.contains(user)) { - JSONObject userJSON = WebUtil.userToJSON(user); - userJSON.put("classMember", false); - responseJSON.put(userJSON); - } - } - response.setContentType("application/json;charset=utf-8"); response.getWriter().write(responseJSON.toString()); return null; @@ -744,7 +754,8 @@ Long activityId = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID, true); if (activityId == null) { long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); - List learners = getMonitoringService().getUsersCompletedLesson(lessonId, 10, (pageNumber - 1) * 10, + List learners = getMonitoringService().getUsersCompletedLesson(lessonId, + MonitoringAction.USER_PAGE_SIZE, (pageNumber - 1) * MonitoringAction.USER_PAGE_SIZE, orderAscending); for (User learner : learners) { learnersJSON.put(WebUtil.userToJSON(learner)); @@ -767,7 +778,8 @@ } List learners = getMonitoringService().getLearnersByActivities(activities.toArray(new Long[] {}), - null, null, orderAscending); + MonitoringAction.USER_PAGE_SIZE, (pageNumber - 1) * MonitoringAction.USER_PAGE_SIZE, + orderAscending); for (User learner : learners) { learnersJSON.put(WebUtil.userToJSON(learner)); } @@ -789,28 +801,34 @@ public ActionForward updateLessonClass(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, JSONException { long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); - Lesson lesson = getLessonService().getLesson(lessonId); + int userId = WebUtil.readIntParam(request, AttributeNames.PARAM_USER_ID); + String role = WebUtil.readStrParam(request, AttributeNames.PARAM_ROLE); + boolean add = WebUtil.readBooleanParam(request, "add"); + boolean result = false; - // monitor user opted for removing lesson progress for following users - IUserManagementService userManagementService = MonitoringServiceProxy - .getUserManagementService(getServlet().getServletContext()); - List allUsers = userManagementService - .getUsersFromOrganisation(lesson.getOrganisation().getOrganisationId()); - List removedLearners = parseUserList(request, "removedLearners", allUsers); - for (User removedLearner : removedLearners) { - getLessonService().removeLearnerProgress(lessonId, removedLearner.getUserId()); - if (LamsDispatchAction.log.isDebugEnabled()) { - LamsDispatchAction.log.debug( - "Removed progress for user ID: " + removedLearner.getUserId() + " in lesson ID: " + lessonId); + if (role.equals(Role.MONITOR)) { + if (add) { + result = getLessonService().addStaffMember(lessonId, userId); + } else { + result = getLessonService().removeStaffMember(lessonId, userId); } + } else if (role.equals(Role.LEARNER)) { + if (add) { + result = getLessonService().addLearner(lessonId, userId); + } else { + getLessonService().removeLearnerProgress(lessonId, userId); + result = getLessonService().removeLearner(lessonId, userId); + } } - List learners = parseUserList(request, "learners", allUsers); - getLessonService().setLearners(lesson, learners); + if (result) { + LamsDispatchAction.log.info((add ? "Added a " : "Removed a ") + role + " with ID " + userId + + (add ? " to" : " from") + " lesson " + lessonId); + } else { + LamsDispatchAction.log.warn("Failed when trying to " + (add ? "add a " : "remove a ") + role + " with ID " + + userId + (add ? " to" : " from") + " lesson " + lessonId); + } - List staff = parseUserList(request, "monitors", allUsers); - getLessonService().setStaffMembers(lesson, staff); - return null; } @@ -1299,7 +1317,8 @@ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); String searchPhrase = request.getParameter("term"); - List learners = getLessonService().getLessonLearners(lessonId, searchPhrase, 10, null, true); + List learners = getLessonService().getLessonLearners(lessonId, searchPhrase, + MonitoringAction.USER_PAGE_SIZE, null, true); JSONArray responseJSON = new JSONArray(); for (User learner : learners) { JSONObject learnerJSON = new JSONObject(); @@ -1434,6 +1453,15 @@ return MonitoringAction.securityService; } + private IUserManagementService getUserManagementService() { + if (MonitoringAction.userManagementService == null) { + WebApplicationContext ctx = WebApplicationContextUtils + .getRequiredWebApplicationContext(getServlet().getServletContext()); + MonitoringAction.userManagementService = (IUserManagementService) ctx.getBean("userManagementService"); + } + return MonitoringAction.userManagementService; + } + /** * Set whether or not the export portfolio button is available in learner. Expects parameters lessonID and * learnerExportPortfolio. Index: lams_monitoring/web/css/monitorLesson.css =================================================================== diff -u -rfbb5dc973034ba3d8ab19914110ce6eb3d253cf9 -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_monitoring/web/css/monitorLesson.css (.../monitorLesson.css) (revision fbb5dc973034ba3d8ab19914110ce6eb3d253cf9) +++ lams_monitoring/web/css/monitorLesson.css (.../monitorLesson.css) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -43,9 +43,8 @@ border: none; } -.dialogList { +.dialogContainer .dialogList { overflow: auto; - border: thin solid black; padding: 5px; } @@ -71,31 +70,24 @@ text-align: center; } -.dialogListSortButton { - text-align: right; - padding-right: 3px; - float: right; - cursor: pointer; -} - -#learnerGroupDialog td { +div.dialogContainer td { padding: 0; } -#learnerGroupDialog td.learnerGroupNav { +div.dialogContainer td.navCell { padding: 5px 0; cursor: pointer; } -#learnerGroupDialog td.learnerGroupNav:hover { +div.dialogContainer td.navCell:hover { background-color: #D0E5F5; } -td.learnerGroupNav span { +div.dialogContainer td.navCell span { margin: auto; } -#learnerGroupPage { +div.dialogContainer td.pageCell { text-align: center; width: 100px; } @@ -171,20 +163,42 @@ float: right; } -#classDialogTable { - border-spacing: 5px 0; -} - #classDialogTable td { - width: 50%; vertical-align: top; } +#classDialogTable #classMonitorSearchRow { + height: 23px; +} + +#classDialogTable #classSearchPhrase { + width: 90%; +} + +#classDialogTable #classSearchPhraseIcon { + margin-top: 2px; +} + +#classDialogTable #classSearchPhraseClear { + visibility: hidden; + margin-top: 3px; + cursor: pointer; +} + #classDialogTable input[type="checkbox"] { margin: 0 5px 0px 0px; border: none; } +#classLearnerTable { + width: 320px; + border-right: thin solid black; +} + +#classMonitorTable { + margin-left: 10px; +} + #emailDialog { width: 100%; } Index: lams_monitoring/web/includes/javascript/monitorLesson.js =================================================================== diff -u -rfbb5dc973034ba3d8ab19914110ce6eb3d253cf9 -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_monitoring/web/includes/javascript/monitorLesson.js (.../monitorLesson.js) (revision fbb5dc973034ba3d8ab19914110ce6eb3d253cf9) +++ lams_monitoring/web/includes/javascript/monitorLesson.js (.../monitorLesson.js) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -13,11 +13,6 @@ sequenceInfoTimeout = 10000, // which learner was selected in the search box sequenceSearchedLearner = null, -// how learners in pop up lists are currently sorted - sortOrderAsc = { - classLearner : false, - classMonitor : false - }, // container for learners' progress bars metadata bars = null, // placeholder for single learner's progress bar and title @@ -188,7 +183,7 @@ // sets up dialog for editing class $('#classDialog').dialog({ 'autoOpen' : false, - 'height' : 360, + 'height' : 435, 'width' : 700, 'minWidth' : 700, 'modal' : true, @@ -197,66 +192,13 @@ 'hide' : 'fold', 'open' : function(){ autoRefreshBlocked = true; - // reset sort order - sortOrderAsc['classLearner'] = false; - sortOrderAsc['classMonitor'] = false; - sortLessonClassDialogList('classLearner'); - sortLessonClassDialogList('classMonitor'); - colorDialogList('classLearner'); - colorDialogList('classMonitor'); - - var selectedLearners = getSelectedClassUserList('classLearnerList'); - $(this).dialog('option', 'initSelectedLearners', selectedLearners); }, 'close' : function(){ autoRefreshBlocked = false; + refreshMonitor(); }, 'buttons' : [ { - 'text' : LABELS.SAVE_BUTTON, - 'id' : 'classDialogSaveButton', - 'click' : function() { - var removedLearners = [], - dialog = $(this), - initSelectedLearners = dialog.dialog('option', 'initSelectedLearners'), - learners = getSelectedClassUserList('classLearnerList'), - monitors = getSelectedClassUserList('classMonitorList'); - - // check for learners removed from lesson - $.each(initSelectedLearners, function(index, selectedLearnerId){ - if ($.inArray(selectedLearnerId, learners) == -1) { - removedLearners.push(selectedLearnerId); - } - }); - - // check if monitoring user really wants to remove progress - if (removedLearners.length > 0 && !confirm(LABELS.LEARNER_GROUP_REMOVE_PROGRESS)){ - removedLearners = []; - } - - $.ajax({ - url : LAMS_URL + 'monitoring/monitoring.do', - type : 'POST', - cache : false, - data : { - 'method' : 'updateLessonClass', - 'lessonID' : lessonId, - 'learners' : learners.join(), - 'monitors' : monitors.join(), - 'removedLearners' : removedLearners.join() - }, - success : function() { - dialog.dialog('close'); - if (removedLearners.length > 0) { - refreshMonitor(); - } else { - refreshMonitor('lesson'); - } - } - }); - } - }, - { 'text' : LABELS.CANCEL_BUTTON, 'id' : 'classDialogCancelButton', 'click' : function() { @@ -266,6 +208,38 @@ ] }); + + // search for users with the term the Monitor entered + $("#classSearchPhrase").autocomplete({ + 'source' : LAMS_URL + "monitoring/monitoring.do?method=autocompleteMonitoringLearners&lessonID=" + lessonId, + 'delay' : 700, + 'select' : function(event, ui){ + var phraseField = $(this); + // learner's ID in ui.item.value is not used here + phraseField.val(ui.item.label); + $('#classSearchPhraseClear').css('visibility', 'visible'); + // reset to page 1 + $('#classDialog').dialog('option','LearnerAjaxProperties').pageNumber = 1; + showClassDialog('Learner'); + return false; + } + }) + // run the real search when the Monitor presses Enter + .keypress(function(e){ + if (e.which == 13) { + var phraseField = $(this); + + phraseField.autocomplete("close"); + if (phraseField.val()) { + $('#classSearchPhraseClear').css('visibility', 'visible'); + } + // reset to page 1 + $('#classDialog').dialog('option','LearnerAjaxProperties').pageNumber = 1; + showClassDialog('Learner'); + } + }); + + // sets up dialog for emailing learners $('#emailDialog').dialog({ 'autoOpen' : false, @@ -596,7 +570,7 @@ // initialise lesson dialog $('#learnerGroupDialog').dialog({ 'autoOpen' : false, - 'height' : 360, + 'height' : 365, 'width' : 400, 'minWidth' : 400, 'modal' : true, @@ -1395,112 +1369,131 @@ /** * Shows Edit Class dialog for class manipulation. */ -function showClassDialog(){ - var learners = []; - var monitors = []; - +function showClassDialog(role){ // fetch available and already participating learners and monitors - $.ajax({ - dataType : 'json', - url : LAMS_URL + 'monitoring/monitoring.do', - cache : false, - async : false, - data : { - 'method' : 'getClassMembers', - 'lessonID' : lessonId, - 'role' : 'LEARNER' - }, - success : function(response) { - learners = response; - } - }); - $.ajax({ - dataType : 'json', - url : LAMS_URL + 'monitoring/monitoring.do', - cache : false, - async : false, - data : { - 'method' : 'getClassMembers', - 'lessonID' : lessonId, - 'role' : 'MONITOR' - }, - success : function(response) { - monitors = response; - } - }); - - // fill lists - fillClassDialogList('classLearner', learners, false); - fillClassDialogList('classMonitor', monitors, true); + if (!role) { + // first time show, fill both lists + fillClassList('Learner', false); + fillClassList('Monitor', true); - $('#classDialog') - .dialog('option', - { - 'title' : LABELS.LESSON_EDIT_CLASS - }) - .dialog('open'); + $('#classDialog') + .dialog('option', + { + 'title' : LABELS.LESSON_EDIT_CLASS + }) + .dialog('open'); + } else { + // refresh after page shift or search + fillClassList(role, role.toLowerCase() == 'monitor'); + } } /** * Fills class member list with user information. */ -function fillClassDialogList(listId, users, disableCreator) { - var list = $('#' + listId + 'List').empty(); - var selectAllInitState = true; +function fillClassList(role, disableCreator) { + var dialog = $('#classDialog'), + tableID = 'class' + role + 'Table', + table = $('#' + tableID, dialog), + list = $('.dialogList', table).empty(), + searchPhrase = role == 'Learner' ? $('#classSearchPhrase', table).val() : null, + ajaxProperties = dialog.dialog('option', role + 'AjaxProperties'), + users = null, + userCount = null; + if (!ajaxProperties) { + // initialise ajax config + ajaxProperties = { + dataType : 'json', + url : LAMS_URL + 'monitoring/monitoring.do', + cache : false, + async : false, + data : { + 'method' : 'getClassMembers', + 'lessonID' : lessonId, + 'role' : role.toUpperCase(), + 'pageNumber' : 1, + 'orderAscending' : true + } + }; + + dialog.dialog('option', role + 'AjaxProperties', ajaxProperties); + } + + // add properties for this call only + if (searchPhrase && searchPhrase.trim() == ''){ + searchPhrase = null; + } + ajaxProperties.data.searchPhrase = searchPhrase; + ajaxProperties.success = function(response) { + users = response.users; + userCount = response.userCount; + } + + $.ajax(ajaxProperties); + + // hide unnecessary controls + togglePagingCells(table, ajaxProperties.data.pageNumber, Math.ceil(userCount / 10)); + $.each(users, function(userIndex, user) { var checkbox = $('').attr({ 'type' : 'checkbox' }).change(function(){ - var itemState = $(this).is(':checked'); - if (itemState) { - var selectAllState = true; - $('input', list).each(function(){ - if (!$(this).is(':checked')) { - selectAllState = false; - return false; - } - }); - - if (selectAllState) { - $('#' + listId + 'SelectAll').prop('checked', 'checked'); - } - } else { - $('#' + listId + 'SelectAll').prop('checked', null); - } - - }); + editClassMember($(this)); + }), + + userDiv = $('
').attr({ + 'userId' : user.id + }) + .addClass('dialogListItem') + .html(getLearnerDisplayName(user)) + .prepend(checkbox) + .appendTo(list); + if (user.classMember) { checkbox.prop('checked', 'checked'); - if (disableCreator && user.lessonCreator) { + if (user.readonly) { // user creator must not be deselected checkbox.attr('disabled', 'disabled'); } - } else { - selectAllInitState = false; } - var userDiv = $('
').attr({ - 'userId' : user.id - }) - .addClass('dialogListItem') - .html(getLearnerDisplayName(user)) - .prepend(checkbox) - .appendTo(list); - if (disableCreator && user.lessonCreator) { userDiv.addClass('dialogListItemDisabled'); } else { userDiv.click(function(event){ - if (event.target == this) { + if (event.target == this && !checkbox.is(':disabled')) { checkbox.prop('checked', checkbox.is(':checked') ? null : 'checked'); + checkbox.change(); } }) } - }); + }); + + colorDialogList(tableID); +} - $('#' + listId + 'SelectAll').prop('checked', selectAllInitState ? 'checked' : null); +/** + * Adds/removes a Learner/Monitor to/from the class. + */ +function editClassMember(userCheckbox){ + var userID = userCheckbox.parent().attr('userId'), + role = userCheckbox.closest('table').is('#classMonitorTable') ? 'MONITOR' : 'LEARNER', + add = userCheckbox.is(':checked'); + + $.ajax({ + url : LAMS_URL + 'monitoring/monitoring.do', + type : 'POST', + cache : false, + data : { + 'method' : 'updateLessonClass', + 'lessonID' : lessonId, + 'userID' : userID, + 'role' : role, + 'add' : add + } + }); } @@ -1807,7 +1800,19 @@ $('#learnersSearchPhraseClear').hide(); } +/** + * Clears previous run search for phrase. + */ +function classClearSearchPhrase(){ + $('#classSearchPhrase').val('').autocomplete("close"); + $('#classDialog').dialog('option', 'LearnerAjaxProperties').pageNumber = 1; + showClassDialog('Learner'); + $('#classSearchPhraseClear').css('visibility', 'hidden'); +} + + + //********** COMMON FUNCTIONS ********** /** @@ -1858,7 +1863,7 @@ */ function showLearnerGroupDialog(ajaxProperties, dialogTitle, allowForceComplete, allowView, allowEmail) { var learnerGroupDialog = $('#learnerGroupDialog'), - learnerGroupList = $('#learnerGroupList', learnerGroupDialog).empty(), + learnerGroupList = $('.dialogList', learnerGroupDialog).empty(), // no parameters provided? just work on what we saved isRefresh = ajaxProperties == null, learners = null, @@ -1913,31 +1918,7 @@ } // hide unnecessary controls - if (pageNumber + 10 <= maxPageNumber) { - $('#learnerGroupPagePlus10', learnerGroupDialog).css('visibility', 'visible'); - } else { - $('#learnerGroupPagePlus10', learnerGroupDialog).css('visibility', 'hidden'); - } - if (pageNumber - 10 < 1) { - $('#learnerGroupPageMinus10', learnerGroupDialog).css('visibility', 'hidden'); - } else { - $('#learnerGroupPageMinus10', learnerGroupDialog).css('visibility', 'visible'); - } - if (pageNumber + 1 <= maxPageNumber) { - $('#learnerGroupPagePlus1', learnerGroupDialog).css('visibility', 'visible'); - } else { - $('#learnerGroupPagePlus1', learnerGroupDialog).css('visibility', 'hidden'); - } - if (pageNumber - 1 < 1) { - $('#learnerGroupPageMinus1', learnerGroupDialog).css('visibility', 'hidden'); - } else { - $('#learnerGroupPageMinus1', learnerGroupDialog).css('visibility', 'visible'); - } - if (maxPageNumber < 2) { - $('#learnerGroupPage', learnerGroupDialog).css('visibility', 'hidden'); - } else { - $('#learnerGroupPage', learnerGroupDialog).css('visibility', 'visible').text(pageNumber + ' / ' + maxPageNumber); - } + togglePagingCells(learnerGroupDialog, pageNumber, maxPageNumber); $.each(learners, function(learnerIndex, learner) { var viewUrl = allowView ? LAMS_URL + 'monitoring/monitoring.do?method=getLearnerActivityURL&userID=' @@ -1970,7 +1951,7 @@ } }); - colorDialogList('learnerGroup'); + colorDialogList('learnerGroupDialog'); if (!isRefresh) { // show buttons depending on parameters @@ -2021,43 +2002,12 @@ .replace(/'/g, "'"); } - /** - * Change order of learner sorting in class dialog. - */ -function sortLessonClassDialogList(listId) { - var list = $('#' + listId + 'List'), - items = list.children('div.dialogListItem'), - orderAsc = sortOrderAsc[listId]; - if (items.length > 1) { - items.each(function(){ - $(this).detach(); - }).sort(function(a, b){ - var keyA = $(a).text().toLowerCase(); - var keyB = $(b).text().toLowerCase(); - var result = keyA > keyB ? 1 : keyA < keyB ? -1 : 0; - return orderAsc ? -result : result; - }).each(function(){ - $(this).appendTo(list); - }); - - var button = $('#' + listId + 'SortButton'); - if (orderAsc) { - button.html('▼'); - sortOrderAsc[listId] = false; - } else { - button.html('▲'); - sortOrderAsc[listId] = true; - } - } -} - -/** * Change order of learner sorting in group dialog. */ -function sortLearnerGroupDialogList() { +function sortLearnerGroupList() { var learnerGroupDialog = $('#learnerGroupDialog'), - sortIcon = $('#learnerGroupSort span', learnerGroupDialog), + sortIcon = $('td.sortCell span', learnerGroupDialog), ajaxProperties = learnerGroupDialog.dialog('option', 'ajaxProperties'), // reverse current order after click orderAscending = !ajaxProperties.data.orderAscending; @@ -2073,18 +2023,33 @@ showLearnerGroupDialog(); } -function selectAllInDialogList(listId) { - var targetState = $('#' + listId + 'SelectAll').is(':checked') ? 'checked' : null; - $('#' + listId + 'List input').each(function(){ - if (!$(this).is(':disabled')) { - $(this).prop('checked', targetState); - } - }); -} +/** + * Change order of learner sorting in Edit Class dialog. + */ +function sortClassList(role) { + var classDialog = $('#classDialog'), + table = $('#class' + role + 'Table', classDialog), + sortIcon = $('td.sortCell span', table), + ajaxProperties = classDialog.dialog('option', role + 'AjaxProperties'), + // reverse current order after click + orderAscending = !ajaxProperties.data.orderAscending; + if (orderAscending) { + sortIcon.removeClass('ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-n'); + } else { + sortIcon.removeClass('ui-icon-triangle-1-n').addClass('ui-icon-triangle-1-s'); + } -function colorDialogList(listId) { - $('#' + listId + 'List div.dialogListItem').each(function(userIndex, userDiv){ + ajaxProperties.data.orderAscending = orderAscending; + // refresh the list + showClassDialog(role); +} + +/** + * Colours a list of users + */ +function colorDialogList(parent) { + $('#' + parent + ' .dialogList div.dialogListItem').each(function(userIndex, userDiv){ // every odd learner has different background $(userDiv).css('background-color', userIndex % 2 ? '#dfeffc' : 'inherit'); }); @@ -2105,8 +2070,55 @@ showLearnerGroupDialog(); } +/** +* Change page in the Edit Class dialog. +*/ +function shiftClassList(role, shift) { + var classDialog = $('#classDialog'), + ajaxProperties = classDialog.dialog('option', role + 'AjaxProperties'), + pageNumber = ajaxProperties.data.pageNumber + shift; + if (pageNumber < 0) { + pageNumber = 1; + } + ajaxProperties.data.pageNumber = pageNumber; + // refresh the dialog with new parameters + showClassDialog(role); +} + /** + * Hides/shows paging controls + */ +function togglePagingCells(parent, pageNumber, maxPageNumber) { + if (pageNumber + 10 <= maxPageNumber) { + $('td.pagePlus10Cell', parent).css('visibility', 'visible'); + } else { + $('td.pagePlus10Cell', parent).css('visibility', 'hidden'); + } + if (pageNumber - 10 < 1) { + $('td.pageMinus10Cell', parent).css('visibility', 'hidden'); + } else { + $('td.pageMinus10Cell', parent).css('visibility', 'visible'); + } + if (pageNumber + 1 <= maxPageNumber) { + $('td.pagePlus1Cell', parent).css('visibility', 'visible'); + } else { + $('td.pagePlus1Cell', parent).css('visibility', 'hidden'); + } + if (pageNumber - 1 < 1) { + $('td.pageMinus1Cell', parent).css('visibility', 'hidden'); + } else { + $('td.pageMinus1Cell', parent).css('visibility', 'visible'); + } + if (maxPageNumber < 2) { + $('td.pageCell', parent).css('visibility', 'hidden'); + } else { + $('td.pageCell', parent).css('visibility', 'visible').text(pageNumber + ' / ' + maxPageNumber); + } +} + + +/** * Makes a XML element with given attributes. * jQuery does not work well with SVG in Chrome, so all this manipulation need to be done manually. */ Index: lams_monitoring/web/monitor.jsp =================================================================== diff -u -rfbb5dc973034ba3d8ab19914110ce6eb3d253cf9 -r509d15bcc570bff483a13152ef8c45cbae767e78 --- lams_monitoring/web/monitor.jsp (.../monitor.jsp) (revision fbb5dc973034ba3d8ab19914110ce6eb3d253cf9) +++ lams_monitoring/web/monitor.jsp (.../monitor.jsp) (revision 509d15bcc570bff483a13152ef8c45cbae767e78) @@ -510,80 +510,153 @@
- - - - - - - + - +
+ + onClick="javascript:sortLearnerGroupList()">
- - - - - - - -
- - - - - -
- - + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + +
+
+
- - + + + + + + + + + + + + + + + + + + +
+ +
 
+
+
- -