Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r99b60c7fbaa34e32bf02d6676087d98312f4d51b -r3ead910e766fb518a08fc2d2cf53382431313cbf --- lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 99b60c7fbaa34e32bf02d6676087d98312f4d51b) +++ lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -419,9 +419,11 @@ index.search.lesson.title =Search learners across lessons index.orggroup =Course groupings index.course.groups.title =Course groupings -label.course.groups.grouping.title =Groupings -label.course.groups.grouping.create =Create new +label.course.groups.grouping.title =Groupings +label.course.groups.grouping.create =Create new +label.course.groups.grouping.count.tooltip =Number of groups in this grouping label.course.groups.grouping.remove.confirm =Are you sure you want to delete this grouping? +label.course.groups.grouping.use.confirm =Are you sure you want to use grouping "[0]"? Groups will be created for this lesson and learners assigned to them. label.course.groups.name =Grouping name: label.course.groups.name.blank =Grouping name can not be blank. label.course.groups.edit.title =Please use drag n' drop to move users between groups. @@ -434,6 +436,7 @@ label.course.groups.sort.tooltip =Sort users label.course.groups.remove.tooltip =Remove label.course.groups.back =Groupings +label.course.groups.locked =Some users in this group already used the grouping. The group is now locked: you can not remove it or move users to another group. +label.course.groups.locked.transfer =Are you sure you want to move these users to a locked group? You will not be allowed to move them to another group later. - #======= End labels: Exported 428 labels for en AU ===== Index: lams_central/src/java/org/lamsfoundation/lams/web/OrganisationGroupAction.java =================================================================== diff -u -re923dff715d473f8bb19d3593f1419662544111c -r3ead910e766fb518a08fc2d2cf53382431313cbf --- lams_central/src/java/org/lamsfoundation/lams/web/OrganisationGroupAction.java (.../OrganisationGroupAction.java) (revision e923dff715d473f8bb19d3593f1419662544111c) +++ lams_central/src/java/org/lamsfoundation/lams/web/OrganisationGroupAction.java (.../OrganisationGroupAction.java) (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -23,6 +23,7 @@ /* $Id$ */ package org.lamsfoundation.lams.web; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; @@ -44,6 +45,11 @@ import org.apache.tomcat.util.json.JSONArray; import org.apache.tomcat.util.json.JSONException; import org.apache.tomcat.util.json.JSONObject; +import org.lamsfoundation.lams.learningdesign.Group; +import org.lamsfoundation.lams.learningdesign.Grouping; +import org.lamsfoundation.lams.learningdesign.GroupingActivity; +import org.lamsfoundation.lams.lesson.Lesson; +import org.lamsfoundation.lams.monitoring.web.GroupingAJAXAction; import org.lamsfoundation.lams.usermanagement.OrganisationGroup; import org.lamsfoundation.lams.usermanagement.OrganisationGrouping; import org.lamsfoundation.lams.usermanagement.Role; @@ -64,7 +70,7 @@ */ public class OrganisationGroupAction extends DispatchAction { /** - * Class for displaying on groupings page. + * Class for displaying data on groupings page. */ public class OrganisationGroupingDTO implements Comparable { private Long groupingId; @@ -113,9 +119,11 @@ } } - private static final Comparator GROUP_COMPARATOR = new Comparator() { + // name comparators + private static final Comparator ORG_GROUP_COMPARATOR = new Comparator() { + @Override public int compare(OrganisationGroup o1, OrganisationGroup o2) { - if (o1 == null && o2 == null) { + if ((o1 == null) && (o2 == null)) { return 0; } if (o1 == null) { @@ -131,19 +139,54 @@ } }; + private static final Comparator GROUP_COMPARATOR = new Comparator() { + @Override + public int compare(Group o1, Group o2) { + if ((o1 == null) && (o2 == null)) { + return 0; + } + if (o1 == null) { + return -1; + } + if (o2 == null) { + return 1; + } + if (o1.getGroupName() == null) { + return o2.getGroupName() == null ? 0 : -1; + } + return o1.getGroupName().compareTo(o2.getGroupName()); + } + }; + private static Logger log = Logger.getLogger(OrganisationGroupAction.class); private static IUserManagementService userManagementService; private static final String MAPPING_VIEW_GROUPINGS = "viewGroupings"; private static final String MAPPING_VIEW_GROUPS = "viewGroups"; + /** + * Shows course grouping list or redirects to groups if a grouping was already chosen. + */ @SuppressWarnings("unchecked") public ActionForward viewGroupings(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws JSONException { - // check if user is allowed to view and edit groups + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID, true); + boolean lessonGroupsExist = getLessonGroups(request, activityID, false) != null; + if (lessonGroupsExist) { + // this is lesson mode and user have already chosen a grouping before, so show it + return viewGroups(mapping, form, request, response); + } + Integer userId = getUserDTO().getUserID(); - int organisationId = WebUtil.readIntParam(request, AttributeNames.PARAM_ORGANISATION_ID); + Integer organisationId = WebUtil.readIntParam(request, AttributeNames.PARAM_ORGANISATION_ID, true); + if (organisationId == null) { + // read organisation ID from lesson + Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); + organisationId = ((Lesson) getUserManagementService().findById(Lesson.class, lessonId)).getOrganisation() + .getOrganisationId(); + } + // check if user is allowed to view and edit groups boolean isGroupSuperuser = getUserManagementService().isUserInRole(userId, organisationId, Role.GROUP_ADMIN) || getUserManagementService().isUserInRole(userId, organisationId, Role.GROUP_MANAGER); if (!isGroupSuperuser && !getUserManagementService().isUserInRole(userId, organisationId, Role.AUTHOR) @@ -157,25 +200,35 @@ + organisationId); } request.setAttribute(AttributeNames.PARAM_ORGANISATION_ID, organisationId); - request.setAttribute("canEdit", isGroupSuperuser); + request.setAttribute("canEdit", isGroupSuperuser || activityID != null); - Set groupingDTOs = new TreeSet(); - List groupings = getUserManagementService().findByProperty(OrganisationGrouping.class, + Set orgGroupingDTOs = new TreeSet(); + List orgGroupings = getUserManagementService().findByProperty(OrganisationGrouping.class, "organisationId", organisationId); - for (OrganisationGrouping grouping : groupings) { - groupingDTOs.add(new OrganisationGroupingDTO(grouping)); + for (OrganisationGrouping orgGrouping : orgGroupings) { + orgGroupingDTOs.add(new OrganisationGroupingDTO(orgGrouping)); } - request.setAttribute("groupings", groupingDTOs); + request.setAttribute("groupings", orgGroupingDTOs); return mapping.findForward(OrganisationGroupAction.MAPPING_VIEW_GROUPINGS); } + /** + * View groups of the given grouping. + */ @SuppressWarnings("unchecked") public ActionForward viewGroups(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws JSONException { - // check if user is allowed to view and edit groups Integer userId = getUserDTO().getUserID(); - int organisationId = WebUtil.readIntParam(request, AttributeNames.PARAM_ORGANISATION_ID); + Integer organisationId = WebUtil.readIntParam(request, AttributeNames.PARAM_ORGANISATION_ID, true); + if (organisationId == null) { + // read organisation ID from lesson + Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); + organisationId = ((Lesson) getUserManagementService().findById(Lesson.class, lessonId)).getOrganisation() + .getOrganisationId(); + } + + // check if user is allowed to view and edit groups boolean isGroupSuperuser = getUserManagementService().isUserInRole(userId, organisationId, Role.GROUP_ADMIN) || getUserManagementService().isUserInRole(userId, organisationId, Role.GROUP_MANAGER); if (!isGroupSuperuser && !getUserManagementService().isUserInRole(userId, organisationId, Role.AUTHOR) @@ -187,50 +240,40 @@ OrganisationGroupAction.log.debug("Displaying course groups for user " + userId + " and organisation " + organisationId); } - request.setAttribute("canEdit", isGroupSuperuser); + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID, true); + request.setAttribute("canEdit", isGroupSuperuser || activityID != null); - JSONObject groupingJSON = new JSONObject(); - groupingJSON.put("organisationId", organisationId); + JSONObject orgGroupingJSON = new JSONObject(); + orgGroupingJSON.put("organisationId", organisationId); - Long groupingId = WebUtil.readLongParam(request, "groupingId", true); - OrganisationGrouping grouping = null; - // check if grouping already exists - if (groupingId != null) { - grouping = (OrganisationGrouping) getUserManagementService().findById(OrganisationGrouping.class, - groupingId); - if (grouping != null) { - groupingJSON.put("groupingId", groupingId); - groupingJSON.put("name", grouping.getName()); + Long orgGroupingId = WebUtil.readLongParam(request, "groupingId", true); + OrganisationGrouping orgGrouping = null; + // check if course grouping already exists or it is a new one + if (orgGroupingId != null) { + orgGrouping = (OrganisationGrouping) getUserManagementService().findById(OrganisationGrouping.class, + orgGroupingId); + if (orgGrouping != null) { + orgGroupingJSON.put("groupingId", orgGroupingId); + orgGroupingJSON.put("name", orgGrouping.getName()); } } - // serialize database group objects into JSON + // check if any groups already exist in this grouping + Set lessonGroups = getLessonGroups(request, activityID, true); + + JSONArray orgGroupsJSON = null; Vector learners = getUserManagementService().getUsersFromOrganisationByRole(organisationId, Role.LEARNER, false, true); - Set groups = grouping == null ? null : grouping.getGroups(); - JSONArray groupsJSON = new JSONArray(); - if (groups != null) { - // sort groups by their name - List groupList = new LinkedList(groups); - Collections.sort(groupList, GROUP_COMPARATOR); - - for (OrganisationGroup group : groupList) { - JSONObject groupJSON = new JSONObject(); - groupJSON.put("name", group.getName()); - groupJSON.put("groupId", group.getGroupId()); - for (User groupUser : group.getUsers()) { - JSONObject groupUserJSON = WebUtil.userToJSON(groupUser); - groupJSON.append("users", groupUserJSON); - - // remove the user who is already assigned to a group - learners.remove(groupUser); - } - - groupsJSON.put(groupJSON); - } + // select source for groups (course or lesson) + if ((lessonGroups == null) || lessonGroups.isEmpty()) { + Set orgGroups = orgGrouping == null ? null : orgGrouping.getGroups(); + orgGroupsJSON = getOrgGroupsDetails(orgGroups, learners); + } else { + orgGroupsJSON = getLessonGroupsDetails(lessonGroups, learners); + request.setAttribute("skipInitialAssigning", true); } - groupingJSON.put("groups", groupsJSON); - request.setAttribute("grouping", groupingJSON); + orgGroupingJSON.put("groups", orgGroupsJSON); + request.setAttribute("grouping", orgGroupingJSON); // all the remaining users are unassigned to any group JSONArray unassignedUsersJSON = new JSONArray(); @@ -243,6 +286,9 @@ return mapping.findForward(OrganisationGroupAction.MAPPING_VIEW_GROUPS); } + /** + * Saves a course grouping. + */ public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws JSONException { // check if user is allowed to edit groups @@ -260,61 +306,61 @@ } // deserialize grouping - JSONObject groupingJSON = new JSONObject(request.getParameter("grouping")); + JSONObject orgGroupingJSON = new JSONObject(request.getParameter("grouping")); // check if already exists - Long groupingId = groupingJSON.optLong("groupingId"); - if (groupingId == 0L) { - groupingId = null; + Long orgGroupingId = orgGroupingJSON.optLong("groupingId"); + if (orgGroupingId == 0L) { + orgGroupingId = null; } // iterate over groups - List groups = new LinkedList(); - JSONArray groupsJSON = groupingJSON.optJSONArray("groups"); - if (groups != null) { - for (int i = 0; i < groupsJSON.length(); i++) { + List orgGroups = new LinkedList(); + JSONArray orgGroupsJSON = orgGroupingJSON.optJSONArray("groups"); + if (orgGroupsJSON != null) { + for (int i = 0; i < orgGroupsJSON.length(); i++) { // just overwrite existing groups; they will be updated if already exist Set users = new HashSet(); - JSONObject groupJSON = groupsJSON.getJSONObject(i); - JSONArray newGroupUsers = groupJSON.optJSONArray("users"); - if (newGroupUsers != null) { + JSONObject orgGroupJSON = orgGroupsJSON.getJSONObject(i); + JSONArray usersJSON = orgGroupJSON.optJSONArray("users"); + if (usersJSON != null) { // find user objects based on delivered IDs - for (int j = 0; j < newGroupUsers.length(); j++) { - Integer learnerId = newGroupUsers.getInt(j); + for (int j = 0; j < usersJSON.length(); j++) { + Integer learnerId = usersJSON.getInt(j); User user = (User) getUserManagementService().findById(User.class, learnerId); users.add(user); } } - OrganisationGroup group = new OrganisationGroup(); - Long groupId = groupJSON.optLong("groupId"); - if (groupId > 0) { - group.setGroupId(groupId); - group.setGroupingId(groupingId); + OrganisationGroup orgGroup = new OrganisationGroup(); + Long orgGroupId = orgGroupJSON.optLong("groupId"); + if (orgGroupId > 0) { + orgGroup.setGroupId(orgGroupId); + orgGroup.setGroupingId(orgGroupingId); } - group.setName(groupJSON.optString("name", null)); - group.setUsers(users); + orgGroup.setName(orgGroupJSON.optString("name", null)); + orgGroup.setUsers(users); - groups.add(group); + orgGroups.add(orgGroup); } } - OrganisationGrouping grouping = null; - if (groupingId != null) { - grouping = (OrganisationGrouping) getUserManagementService().findById(OrganisationGrouping.class, - groupingId); + OrganisationGrouping orgGrouping = null; + if (orgGroupingId != null) { + orgGrouping = (OrganisationGrouping) getUserManagementService().findById(OrganisationGrouping.class, + orgGroupingId); } - if (grouping == null) { - grouping = new OrganisationGrouping(); - grouping.setOrganisationId(organisationId); + if (orgGrouping == null) { + orgGrouping = new OrganisationGrouping(); + orgGrouping.setOrganisationId(organisationId); } - grouping.setName(groupingJSON.getString("name")); + orgGrouping.setName(orgGroupingJSON.getString("name")); - getUserManagementService().saveOrganisationGrouping(grouping, groups); + getUserManagementService().saveOrganisationGrouping(orgGrouping, orgGroups); return null; } /** - * Deletes Organisation Grouping with the given ID. + * Deletes course grouping with the given ID. */ public ActionForward removeGrouping(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws JSONException { @@ -339,6 +385,107 @@ return viewGroupings(mapping, form, request, response); } + /** + * Build JSON objects based on existing lesson-level groups. + */ + @SuppressWarnings("unchecked") + private JSONArray getLessonGroupsDetails(Set groups, Collection learners) throws JSONException { + // serialize database group objects into JSON + JSONArray groupsJSON = new JSONArray(); + if (groups != null) { + // sort groups by their name + List groupList = new LinkedList(groups); + Collections.sort(groupList, OrganisationGroupAction.GROUP_COMPARATOR); + + for (Group group : groupList) { + JSONObject groupJSON = new JSONObject(); + groupJSON.put("name", group.getGroupName()); + groupJSON.put("groupId", group.getGroupId()); + groupJSON.put("locked", !group.mayBeDeleted()); + if (group.getUsers() != null) { + for (User groupUser : (Set) group.getUsers()) { + JSONObject groupUserJSON = WebUtil.userToJSON(groupUser); + groupJSON.append("users", groupUserJSON); + + // remove the user who is already assigned to a group + learners.remove(groupUser); + } + } + groupsJSON.put(groupJSON); + } + } + + return groupsJSON; + } + + /** + * Build JSON objects based on existing course-level groups. + */ + private JSONArray getOrgGroupsDetails(Set groups, Collection learners) + throws JSONException { + // serialize database group objects into JSON + JSONArray groupsJSON = new JSONArray(); + if (groups != null) { + // sort groups by their name + List groupList = new LinkedList(groups); + Collections.sort(groupList, OrganisationGroupAction.ORG_GROUP_COMPARATOR); + + for (OrganisationGroup group : groupList) { + JSONObject groupJSON = new JSONObject(); + groupJSON.put("name", group.getName()); + groupJSON.put("groupId", group.getGroupId()); + for (User groupUser : group.getUsers()) { + JSONObject groupUserJSON = WebUtil.userToJSON(groupUser); + groupJSON.append("users", groupUserJSON); + + // remove the user who is already assigned to a group + learners.remove(groupUser); + } + + groupsJSON.put(groupJSON); + } + } + + return groupsJSON; + } + + /** + * Checks if lesson-level groups exist for the given activity. + */ + @SuppressWarnings("unchecked") + private Set getLessonGroups(HttpServletRequest request, Long activityID, boolean allowEmpty) { + if (activityID != null) { + GroupingActivity groupingActivity = (GroupingActivity) getUserManagementService().findById( + GroupingActivity.class, activityID); + Grouping grouping = groupingActivity.getCreateGrouping(); + + if ((grouping != null) && (grouping.getGroups() != null) && !grouping.getGroups().isEmpty()) { + // not very obvious place to use it, but it made most sense + boolean isUsedForBranching = grouping.isUsedForBranching(); + request.setAttribute(GroupingAJAXAction.PARAM_USED_FOR_BRANCHING, isUsedForBranching); + + // to check if a grouping exists at all, empty groups are allowed + // to check if a grouping can be discarded, there must be no groups at all + if (allowEmpty || isUsedForBranching) { + return grouping.getGroups(); + } + + for (Group existingGroup : (Set) grouping.getGroups()) { + if (!existingGroup.getUsers().isEmpty()) { + return grouping.getGroups(); + } + } + } + } + + return null; + } + + private UserDTO getUserDTO() { + HttpSession ss = SessionManager.getSession(); + return (UserDTO) ss.getAttribute(AttributeNames.USER); + } + private IUserManagementService getUserManagementService() { if (OrganisationGroupAction.userManagementService == null) { WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() @@ -348,9 +495,4 @@ } return OrganisationGroupAction.userManagementService; } - - private UserDTO getUserDTO() { - HttpSession ss = SessionManager.getSession(); - return (UserDTO) ss.getAttribute(AttributeNames.USER); - } } \ No newline at end of file Index: lams_central/web/css/orgGroup.css =================================================================== diff -u -re923dff715d473f8bb19d3593f1419662544111c -r3ead910e766fb518a08fc2d2cf53382431313cbf --- lams_central/web/css/orgGroup.css (.../orgGroup.css) (revision e923dff715d473f8bb19d3593f1419662544111c) +++ lams_central/web/css/orgGroup.css (.../orgGroup.css) (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -10,24 +10,29 @@ } div#titleDiv { + padding: 0 5px 0 5px; font-size: small; font-weight: bold; - padding: 5px 10px 5px 10px; border-bottom: thin dotted #2E6E9E; } div#titleDiv input { - margin-left: 5px; + margin: 5px 0 5px 0; width: 290px; } -#titleInstructions{ - padding-top: 4px; +div#titleDiv .ui-button { + margin-bottom: 5px; + font-size: inherit; +} + +#titleInstructions { + margin-top: 7px; float: right; } table#groupsTable { - height: 380px; + height: 375px; } table#groupsTable td { @@ -104,11 +109,20 @@ cursor: default; } +.userContainer .ui-state-disabled { + opacity: 1 !important; +} + .droppableHighlight { padding : 1px !important; border: 5px solid #5c9ccc !important; } +.locked { + padding : 4px; + border: 2px solid red; +} + .draggableUserSelected { background-color: #5c9ccc !important; color: white !important; Index: lams_central/web/css/orgGrouping.css =================================================================== diff -u --- lams_central/web/css/orgGrouping.css (revision 0) +++ lams_central/web/css/orgGrouping.css (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -0,0 +1,41 @@ +div#titleDiv { + font-size: small; + font-weight: bold; + padding: 5px 0; + text-align: center; + border-bottom: thin dotted #2E6E9E; +} + +.removeGroupingButton { + float: right; + cursor: pointer; + padding-right: 3px; + width: 16px; + height: 16px; +} + +#addGroupingButton { + float: right; + margin: 20px 10px 10px 0; +} + +.groupingContainer { + padding: 10px 10px 0 10px; +} + +.groupingName { + font-size: 12px; + border-bottom: 1px dotted #0087E5; + color: #0087E5; + text-decoration: none; +} + +.groupingName:hover { + border-bottom: 1px solid #0087E5; + color: #0087E5; +} + +.groupCount { + font-size: 12px; + padding-left: 10px; +} \ No newline at end of file Index: lams_central/web/includes/javascript/orgGroup.js =================================================================== diff -u -re923dff715d473f8bb19d3593f1419662544111c -r3ead910e766fb518a08fc2d2cf53382431313cbf --- lams_central/web/includes/javascript/orgGroup.js (.../orgGroup.js) (revision e923dff715d473f8bb19d3593f1419662544111c) +++ lams_central/web/includes/javascript/orgGroup.js (.../orgGroup.js) (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -1,22 +1,36 @@ -// ********** MAIN FUNCTIONS ********** +// for user selecting and sorting purposes var sortOrderAscending = {}; var lastSelectedUsers = {}; $(document).ready(function(){ - $('#groupingName').val(grouping.name); + $('.ui-button').button(); // show unassigned users on the left fillGroup(unassignedUsers, $('#unassignedUserCell')); + // add existing groups $.each(grouping.groups, function(){ - // add existing groups - addGroup(this.groupId, this.name, this.users); + // if a course grouping is being copied as lesson grouping, + // do not use group IDs as they will be created when assiging users to created groups + var groupId = !lessonMode || skipInitialAssigning ? this.groupId : null; + var group = addGroup(groupId, this.name, this.users); + if (this.locked) { + markGroupLocked(group); + } }); - // move Save button to the titlebar, i.e. outside of this iframe to the enveloping dialog - $('div.ui-dialog-titlebar', window.parent.document).prepend($('.customDialogButton')); + // initialisation based on mode + if (lessonMode) { + toggleBackButton(); + } else { + $('#groupingName').val(grouping.name); + // move Save button to the titlebar, i.e. outside of this iframe to the enveloping dialog + $('div.ui-dialog-titlebar', window.parent.document).prepend($('.customDialogButton')); + } + if (canEdit) { // allow adding new groups $('#newGroupPlaceholder').click(function(){ - addGroup(null, LABELS.GROUP_PREFIX_LABEL + $('table .groupContainer').length, null); + // the label is "Group X" where X is the top group number + addGroup(null, LABELS.GROUP_PREFIX_LABEL + ' ' + $('#groupsTable .groupContainer').length, null); }); } }); @@ -26,23 +40,48 @@ * Add a group and fills it with given users. */ function addGroup(groupId, name, users) { - var groupCount = $('table .groupContainer').length; var group = $('#groupTemplate').clone() .attr({ - 'id' : 'group-' + groupCount, + // remove template's HTML ID + 'id' : null, 'groupId' : groupId }) .css('display', null); + + var groupNameFields = $('#groupsTable .groupContainer input'); + var groupTopIndex = groupNameFields.length; + // names can be blank in course groups, + // but in lesson groups they have to exist and be unique + if (lessonMode && !name) { + name = LABELS.GROUP_PREFIX_LABEL + ' ' + groupTopIndex; + } + var nameIsUnique; + do { + nameIsUnique = true; + groupNameFields.each(function(){ + if (name == $(this).val()) { + nameIsUnique = false; + return false; + } + }); + if (!nameIsUnique) { + groupTopIndex++; + name = LABELS.GROUP_PREFIX_LABEL + groupTopIndex; + } + } while (!nameIsUnique); + group.find('input').val(name); - // there is no placeholder in read-only mode if (canEdit) { group.insertBefore('#newGroupPlaceholder'); } else { + // there is no placeholder in read-only mode $('#groupsCell').append(group); } fillGroup(users, group); + + return group; } @@ -63,7 +102,7 @@ if (canEdit) { userDiv.draggable({ 'appendTo' : 'body', - 'containment' : 'table', + 'containment' : '#groupsTable', 'revert' : 'invalid', 'distance' : 20, 'scroll' : false, @@ -117,7 +156,17 @@ }); } - $('.userContainer', container).append(userDiv); + if (lessonMode && !skipInitialAssigning && container.attr('id') != 'unassignedUserCell') { + // copy course groups as lesson groups, creating new instances + if (assignUsersToGroup([userJSON.id], container)) { + $('.userContainer', container).append(userDiv); + } else { + // if it fails, put them in unassigned list + $('#unassignedUserCell .userContainer').append(userDiv); + } + } else { + $('.userContainer', container).append(userDiv); + } }); sortUsers(container); @@ -131,39 +180,67 @@ $(container).droppable({ 'activeClass' : 'droppableHighlight', 'tolerance' : 'touch', - 'drop' : function (event, draggable) { - var draggableUserContainer = $(draggable.draggable).parent(); + 'drop' : function (event, ui) { + var draggableUserContainer = $(ui.draggable).parent(); var thisUserContainer = $('.userContainer', this); // do not do anything if it is the same container // using "accept" feature breaks the layout if (draggableUserContainer[0] != thisUserContainer[0]) { - transferUsers(draggableUserContainer, thisUserContainer); - } + var executeDrop = !lessonMode; + if (!executeDrop) { + var transferToLocked = $(this).hasClass('locked'); + // make sure user wants to transfer learners to a group which is already in use + executeDrop = !transferToLocked || confirm(LABELS.TRANSFER_LOCKED_LABEL); + if (executeDrop) { + var userIds = []; + $('div.draggableUserSelected', draggableUserContainer).each(function(){ + userIds.push($(this).attr('userId')); + }); + // execute transfer on server side + executeDrop = assignUsersToGroup(userIds, $(this)); + } + } + + if (executeDrop) { + transferUsers(draggableUserContainer, thisUserContainer); + } + } } }); $('.removeGroupButton', container).click(function(){ removeGroup(container); }); + + if (lessonMode) { + $('input', container).blur(function(){ + renameGroup(container); + }); + } } } - +/** + * Move user DIVs from one group to another + */ function transferUsers(fromContainer, toContainer) { var selectedUsers = $('.draggableUserSelected', fromContainer); - if (selectedUsers.length > 0){ + var locked = toContainer.parent().hasClass('locked'); + if (selectedUsers.length > 0){ // move the selected users - selectedUsers.each(function(){ + selectedUsers.each(function(){ $(this).css({'top' : '0px', 'left' : '0px', - }) - .appendTo(toContainer); + }).removeClass('draggableUserSelected') + .appendTo(toContainer); + + if (locked) { + $(this).draggable('disable'); + } }); - // recolour both containers - toContainer.children().removeClass('draggableUserSelected'); colorDraggableUsers(toContainer); colorDraggableUsers(fromContainer); } @@ -211,34 +288,87 @@ } } +/** + * Remove a group both from the server and the page. + */ function removeGroup(container) { // no groupId means this group was just added and it was not persisted in DB yet - var executeDelete = !$(container).attr('groupId'); - if (!executeDelete) { + var groupId = container.attr('groupId'); + var executeDelete = true; + if (groupId) { executeDelete = confirm(LABELS.GROUP_REMOVE_LABEL); } if (executeDelete) { - $('#unassignedUserCell .userContainer').append($('.userContainer div.draggableUser', container)); - container.remove(); + executeDelete = !lessonMode; + + if (lessonMode) { + $.ajax({ + async : false, + cache : false, + dataType : 'json', + url : LAMS_URL + 'monitoring/grouping.do', + data : { + 'method' : 'removeGroupJSON', + 'activityID' : groupingActivityId, + 'groupID' : groupId + }, + type : 'POST', + success : function(response) { + executeDelete = response.result; + } + }); + } + + if (executeDelete) { + $('#unassignedUserCell .userContainer').append($('.userContainer div.draggableUser', container)); + container.remove(); + toggleBackButton(); + } } } +/** + * Change name of a group both on the server and on the page. + */ +function renameGroup(container) { + var groupId = $(container).attr('groupId'); + var nameInput = $('input', container); + var inputEditable = !nameInput.attr('readonly'); + // only lesson groups which exist on the server need to have their names changed immediatelly + if (lessonMode && groupId && inputEditable) { + var groupName = nameInput.val(); + $.ajax({ + cache : false, + url : LAMS_URL + 'monitoring/grouping.do', + data : { + 'method' : 'changeGroupNameJSON', + 'groupID' : groupId, + 'name' : groupName + }, + type : 'POST' + }); + } +} +/** + * Save a course grouping. + */ function saveGroups(){ - if (!canEdit) { + if (!canEdit || lessonMode) { return false; } $('.errorMessage').hide(); var groupingName = $('#groupingName').val(); if (!groupingName) { + // course grouping name can not be blank $('#groupingNameBlankError').show(); return false; } // ask if removing new, empty groups is OK var acceptEmptyGroups = true; - var groupContainers = $('table .groupContainer').not('#newGroupPlaceholder'); + var groupContainers = $('#groupsTable .groupContainer').not('#newGroupPlaceholder'); $.each(groupContainers.not('[groupId]'), function(){ if ($('div.draggableUser', this).length == 0) { acceptEmptyGroups = false; @@ -270,13 +400,14 @@ userIds.push($(this).attr('userId')); }); + // add the group JSON to grouping JSON newGrouping.groups.push({ 'groupId' : groupId, 'name' : $('input', this).val(), 'users' : userIds }); }); - + $.ajax({ async : false, cache : false, @@ -287,10 +418,69 @@ 'grouping' : JSON.stringify(newGrouping) }, type : 'POST', - success : function(json) { + success : function() { groupsSaved = true; } }); return groupsSaved; +} + +/** + * Transfer learners between groups on the server. + */ +function assignUsersToGroup(userIds, groupContainer) { + // if removing from any groups, set group ID as -1; server understands this + var groupId = groupContainer.attr('id') == 'unassignedUserCell' ? -1 : groupContainer.attr('groupId'); + // name is only needed when creating a new group, i.e. a group which does not have ID yet + var groupName = groupId ? null : $('input', groupContainer).val(); + var result = false; + + $.ajax({ + async : false, + cache : false, + dataType : 'json', + url : LAMS_URL + 'monitoring/grouping.do', + data : { + 'method' : 'addMembersJSON', + 'activityID' : groupingActivityId, + 'groupID' : groupId, + 'name' : groupName, + 'members' : userIds.join() + }, + type : 'POST', + success : function(response) { + result = response.result; + if (response.groupId) { + groupContainer.attr('groupId', response.groupId); + } + if (!result && response.locked) { + // the server says that the group became in use while teacher was working with this screen + markGroupLocked($('div[userId="' + userIds[0] + '"]').parents('.groupContainer')); + alert(LABELS.GROUP_LOCK_LABEL); + } + toggleBackButton(); + } + }); + + return result; +} + +/** + * If there are any existing (not new) groups, forbid going back to grouping list. + */ +function toggleBackButton() { + if (lessonMode) { + $('#backButton').button('option', 'disabled', $('.groupContainer[groupId]').length > 0); + } +} + +/** + * Makes a group almost immutable as server does not allow changed. + */ +function markGroupLocked(container) { + container.addClass('locked'); + $('.removeGroupButton', container).remove(); + // $('input', container).attr('readonly', 'readonly'); + $('div.draggableUser', container).off('click').draggable('disable'); } \ No newline at end of file Index: lams_central/web/includes/javascript/orgGrouping.js =================================================================== diff -u --- lams_central/web/includes/javascript/orgGrouping.js (revision 0) +++ lams_central/web/includes/javascript/orgGrouping.js (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -0,0 +1,35 @@ +$(document).ready(function() { + $(".ui-button").button(); +}); + +function removeGrouping(groupingId) { + if (!lessonMode && confirm(LABELS.REMOVE_GROUPING_CONFIRM_LABEL)) { + document.location.href = "OrganisationGroup.do?method=removeGrouping&organisationID=" + + organisationId + "&groupingId=" + groupingId; + } +} + +function showGroups(groupingId) { + var url = LAMS_URL + 'OrganisationGroup.do?method=viewGroups&organisationID=' + + organisationId; + // no grouping ID means we open a brand new grouping + if (groupingId) { + url += '&groupingId=' + groupingId; + } + + if (lessonMode) { + var executeShow = true; + if (groupingId) { + // make sure user want to use this grouping + var groupingName = $('#grouping-' + groupingId + ' .groupingName').text().trim(); + executeShow = confirm(LABELS.USE_GROUPING_CONFIRM_LABEL.replace('[0]', groupingName)); + } + if (executeShow) { + // load to current iframe + document.location.href = url + '&activityID=' + groupingActivityId; + } + } else { + // load to dialog + window.parent.loadDialogContents(null, 880, 460, url); + } +} \ No newline at end of file Index: lams_central/web/orgGroup.jsp =================================================================== diff -u -re923dff715d473f8bb19d3593f1419662544111c -r3ead910e766fb518a08fc2d2cf53382431313cbf --- lams_central/web/orgGroup.jsp (.../orgGroup.jsp) (revision e923dff715d473f8bb19d3593f1419662544111c) +++ lams_central/web/orgGroup.jsp (.../orgGroup.jsp) (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -10,59 +10,81 @@ + + - - - - - - - - - -
- - - readonly="readonly" - -/> - - - - - - - - + + +
+ +
- + + + + + + + + + + + + + readonly="readonly" + + /> + + + +
-
+ + + + + + + + + + +
@@ -76,7 +98,7 @@
- +
@@ -85,9 +107,10 @@
+
- + @@ -97,7 +120,7 @@ /> + title="">▲
Index: lams_central/web/orgGrouping.jsp =================================================================== diff -u -re923dff715d473f8bb19d3593f1419662544111c -r3ead910e766fb518a08fc2d2cf53382431313cbf --- lams_central/web/orgGrouping.jsp (.../orgGrouping.jsp) (revision e923dff715d473f8bb19d3593f1419662544111c) +++ lams_central/web/orgGrouping.jsp (.../orgGrouping.jsp) (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -8,89 +8,62 @@ - + - +
- + + + + + + + +
-
- - - - (${grouping.groupCount}) - - - -
+ <%-- In lesson mode do not show groupings with zero groups --%> + +
+ + + + + (${grouping.groupCount}) + + + + +
+
-
- +
+
Index: lams_monitoring/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -ra58695ef2068ecbf54fef279141bf52da648d63e -r3ead910e766fb518a08fc2d2cf53382431313cbf --- lams_monitoring/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision a58695ef2068ecbf54fef279141bf52da648d63e) +++ lams_monitoring/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -21,8 +21,8 @@ label.gate.waiting.learners ={0} out of {1} are waiting in front of the gate. label.grouping.max.num.in.group.heading =Maximum number of groups: label.grouping.general.instructions.heading =General Instructions: -label.grouping.general.instructions.line1 =Place the lesson participants in their groups. Initially you can add and remove users, but once the grouping is used (that is, a participant starts an activity that uses the grouping) you will not be able to remove users from groups. If you try to remove someone from a group and they will not remove check their progress - if they start using the group while you are on this screen you will not get any errors but you will not be able to remove them from their group. You will still be able to add users to groups. -label.grouping.general.instructions.line2 =To create a group, simply click the "+" button in the group functions. To remove a group, click the "-" button either on the group panel itself or in the group functions (deletes the last group added). Repeat as required. To assign learners to groups, simply drag their names into the appropriate group panel. The changes are saved immediately. +label.grouping.general.instructions.line1 =Choose a course grouping to use in this lesson or create a new one. You are working with copies, so any changes made here have no influence on the original course groupings. Grouping with zero groups are not listed here.
Once a grouping is chosen, the only way to come back to the grouping list is to manually remove all groups. +label.grouping.general.instructions.line2 =Place the lesson participants in their groups. Initially you can add and remove users, but once a groups is used (that is, a participant starts an activity that uses the grouping) you will not be able to remove users from it. Even if a group is in use you can still add users. The changes are saved immediately. label.grouping.group.heading =Groups label.grouping.non.grouped.users.heading =Learners without a Group label.grouping.grouped.users.heading =Members of selected Group @@ -89,7 +89,7 @@ button.no =No label.grouping.popup.drag.selection.message =Because some learners have already entered this grouping activity, it will only be possible to add new learners to groups. Learners can not be removed from groups. Once a learner is added to a group, the action can not be undone. label.branching.popup.drag.selection.message =Because some learners have already entered this branching activity, it will only be possible to add new learners to branches. Learners can not be removed from branches, therefore, once a learner is added to a branch, the action can not be undone. -label.grouping.general.instructions.branching =This grouping activity is used for branching. Groups can therefore not be added or deleted. However, learners can still be assigned to groups. To do so, simply drag their names into the appropriate group panel. The changes are saved immediately +label.grouping.general.instructions.branching =This grouping activity is used for branching. Groups can therefore not be added or deleted. label.grouping.popup.viewmode.message =You are presently in group view mode. Groups can not be modified. label.gate.list.have.not.met.conditions.learners =Have not met conditions message.gate.condition.force.pass =Note: This will allow all learners to pass regardless of the specified conditions. Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GroupingAJAXAction.java =================================================================== diff -u -rd57d10fd9f86194c618f307736fe212e54637ca2 -r3ead910e766fb518a08fc2d2cf53382431313cbf --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GroupingAJAXAction.java (.../GroupingAJAXAction.java) (revision d57d10fd9f86194c618f307736fe212e54637ca2) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GroupingAJAXAction.java (.../GroupingAJAXAction.java) (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -25,9 +25,7 @@ package org.lamsfoundation.lams.monitoring.web; import java.io.IOException; -import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Collection; import java.util.Iterator; import java.util.Set; import java.util.SortedSet; @@ -41,9 +39,8 @@ import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; -import org.lamsfoundation.lams.learning.web.util.LearningWebUtil; -import org.lamsfoundation.lams.learningdesign.Activity; -import org.lamsfoundation.lams.learningdesign.ChosenGrouping; +import org.apache.tomcat.util.json.JSONException; +import org.apache.tomcat.util.json.JSONObject; import org.lamsfoundation.lams.learningdesign.Group; import org.lamsfoundation.lams.learningdesign.GroupComparator; import org.lamsfoundation.lams.learningdesign.Grouping; @@ -62,475 +59,617 @@ import org.lamsfoundation.lams.web.util.AttributeNames; /** -* The action servlet that provides the support for the -*
    -*
  • AJAX based Chosen Grouping screen
  • -*
  • forwards to the learner's view grouping screen for Random Grouping.
  • -*
-* -* @author Fiona Malikoff - -* ----------------XDoclet Tags-------------------- -* -* @struts:action path="/grouping" -* parameter="method" -* validate="false" -* @struts.action-forward name = "chosenGrouping" path = "/grouping/chosenGrouping.jsp" -* @struts.action-forward name = "viewGroups" path = ".viewGroups" -* -* ----------------XDoclet Tags-------------------- -*/ + * The action servlet that provides the support for the + *
    + *
  • AJAX based Chosen Grouping screen
  • + *
  • forwards to the learner's view grouping screen for Random Grouping.
  • + *
+ * + * @author Fiona Malikoff + * + * ----------------XDoclet Tags-------------------- + * + * @struts:action path="/grouping" parameter="method" validate="false" + * @struts.action-forward name = "chosenGrouping" path = "/grouping/chosenGrouping.jsp" + * @struts.action-forward name = "viewGroups" path = ".viewGroups" + * + * ----------------XDoclet Tags-------------------- + */ public class GroupingAJAXAction extends LamsDispatchAction { - //--------------------------------------------------------------------- + // --------------------------------------------------------------------- - private static final String CHOSEN_GROUPING_SCREEN = "chosenGrouping"; - private static final String VIEW_GROUPS_SCREEN = "viewGroups"; - private static final String PARAM_ACTIVITY_TITLE = "title"; - private static final String PARAM_ACTIVITY_DESCRIPTION = "description"; - public static final String PARAM_MAX_NUM_GROUPS = "maxNumberOfGroups"; - public static final String PARAM_NAME = "name"; - public static final String PARAM_GROUPS = "groups"; - public static final String PARAM_MEMBERS = "members"; - public static final String PARAM_MAY_DELETE = "mayDelete"; - public static final String PARAM_USED_FOR_BRANCHING = "usedForBranching"; - public static final String PARAM_MODULE_LANGUAGE_XML = "languageXML"; - public static final String PARAM_VIEW_MODE = "viewMode"; - - private Integer getUserId(HttpServletRequest request) { - HttpSession ss = SessionManager.getSession(); - UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER); - return user != null ? user.getUserID() : null; + private static final String CHOSEN_GROUPING_SCREEN = "chosenGrouping"; + private static final String VIEW_GROUPS_SCREEN = "viewGroups"; + private static final String PARAM_ACTIVITY_TITLE = "title"; + private static final String PARAM_ACTIVITY_DESCRIPTION = "description"; + public static final String PARAM_MAX_NUM_GROUPS = "maxNumberOfGroups"; + public static final String PARAM_NAME = "name"; + public static final String PARAM_GROUPS = "groups"; + public static final String PARAM_MEMBERS = "members"; + public static final String PARAM_MAY_DELETE = "mayDelete"; + public static final String PARAM_USED_FOR_BRANCHING = "usedForBranching"; + public static final String PARAM_MODULE_LANGUAGE_XML = "languageXML"; + public static final String PARAM_VIEW_MODE = "viewMode"; + + private Integer getUserId(HttpServletRequest request) { + HttpSession ss = SessionManager.getSession(); + UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER); + return user != null ? user.getUserID() : null; + } + + private Grouping getGrouping(GroupingActivity activity) { + Grouping grouping = activity.getCreateGrouping(); + if (grouping == null) { + String error = "Grouping activity missing grouping. Activity was " + activity + " Grouping was " + grouping; + LamsDispatchAction.log.error(error); + throw new MonitoringServiceException(error); } - + return grouping; + } - private Grouping getGrouping(GroupingActivity activity) { - Grouping grouping = activity.getCreateGrouping(); - if ( grouping == null ) { - String error = "Grouping activity missing grouping. Activity was "+activity+" Grouping was "+grouping; - log.error(error); - throw new MonitoringServiceException(error); - } - return grouping; + /** + * Start the process of doing the chosen grouping + * + * Input parameters: activityID + */ + public ActionForward startGrouping(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + GroupingActivity activity = monitoringService.getGroupingActivityById(activityID); + Grouping grouping = activity.getCreateGrouping(); + if (grouping == null) { + String error = "Grouping activity missing grouping. Activity was " + activity; + LamsDispatchAction.log.error(error); + throw new MonitoringServiceException(error); } - - /** - * Start the process of doing the chosen grouping - * - * Input parameters: activityID - */ - public ActionForward startGrouping(ActionMapping mapping, - ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { - - Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); - Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); - IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - GroupingActivity activity = monitoringService.getGroupingActivityById(activityID); - Grouping grouping = activity.getCreateGrouping(); - if ( grouping == null ) { - String error = "Grouping activity missing grouping. Activity was "+activity; - log.error(error); - throw new MonitoringServiceException(error); - } - - request.setAttribute(AttributeNames.PARAM_ACTIVITY_ID, activityID); - request.setAttribute(AttributeNames.PARAM_LESSON_ID, lessonId); - request.setAttribute(PARAM_ACTIVITY_TITLE, activity.getTitle()); - request.setAttribute(PARAM_ACTIVITY_DESCRIPTION, activity.getDescription()); - request.setAttribute(PARAM_MODULE_LANGUAGE_XML, getLanguageXML()); - - if ( grouping.isChosenGrouping() ) { - // can I remove groups/users - can't if tool sessions have been created - Set groups = grouping.getGroups(); - Iterator iter = groups.iterator(); - boolean mayDelete = true; - while (mayDelete && iter.hasNext()) { - Group group = (Group) iter.next(); - mayDelete = group.mayBeDeleted(); - } - // is this grouping used for branching. If it is, must honour the groups - // set in authoring or some groups won't have a branch. mayDelete can still - // be true or false as you can remove users from groups, you just can't remove - // groups due to the branching relationship. - boolean usedForBranching = grouping.isUsedForBranching(); - - request.setAttribute(PARAM_MAY_DELETE, mayDelete); - request.setAttribute(PARAM_USED_FOR_BRANCHING, usedForBranching); - request.setAttribute(PARAM_MAX_NUM_GROUPS, grouping.getMaxNumberOfGroups()); - request.setAttribute(PARAM_VIEW_MODE, Boolean.FALSE); + request.setAttribute(AttributeNames.PARAM_ACTIVITY_ID, activityID); + request.setAttribute(AttributeNames.PARAM_LESSON_ID, lessonId); + request.setAttribute(GroupingAJAXAction.PARAM_ACTIVITY_TITLE, activity.getTitle()); + request.setAttribute(GroupingAJAXAction.PARAM_ACTIVITY_DESCRIPTION, activity.getDescription()); + request.setAttribute(GroupingAJAXAction.PARAM_MODULE_LANGUAGE_XML, getLanguageXML()); - return mapping.findForward(CHOSEN_GROUPING_SCREEN); - - } else { - - // go to a view only screen for random grouping - request.setAttribute(PARAM_MAY_DELETE, Boolean.FALSE); - request.setAttribute(PARAM_VIEW_MODE, Boolean.TRUE); - return mapping.findForward(VIEW_GROUPS_SCREEN); - } - + if (grouping.isChosenGrouping()) { + // can I remove groups/users - can't if tool sessions have been created + Set groups = grouping.getGroups(); + Iterator iter = groups.iterator(); + boolean mayDelete = true; + while (mayDelete && iter.hasNext()) { + Group group = (Group) iter.next(); + mayDelete = group.mayBeDeleted(); + } + + // is this grouping used for branching. If it is, must honour the groups + // set in authoring or some groups won't have a branch. mayDelete can still + // be true or false as you can remove users from groups, you just can't remove + // groups due to the branching relationship. + boolean usedForBranching = grouping.isUsedForBranching(); + + request.setAttribute(GroupingAJAXAction.PARAM_MAY_DELETE, mayDelete); + request.setAttribute(GroupingAJAXAction.PARAM_USED_FOR_BRANCHING, usedForBranching); + request.setAttribute(GroupingAJAXAction.PARAM_MAX_NUM_GROUPS, grouping.getMaxNumberOfGroups()); + request.setAttribute(GroupingAJAXAction.PARAM_VIEW_MODE, Boolean.FALSE); + + return mapping.findForward(GroupingAJAXAction.CHOSEN_GROUPING_SCREEN); + + } else { + + // go to a view only screen for random grouping + request.setAttribute(GroupingAJAXAction.PARAM_MAY_DELETE, Boolean.FALSE); + request.setAttribute(GroupingAJAXAction.PARAM_VIEW_MODE, Boolean.TRUE); + return mapping.findForward(GroupingAJAXAction.VIEW_GROUPS_SCREEN); } - - /** - * Get a list of group names and the number of users in each group. Designed to respond to an AJAX call. - * - * Input parameters: activityID - * - * Output format: "groupid,name,num users;groupid,name,num users" - */ - public ActionForward getGroups(ActionMapping mapping, - ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { - // get the grouping data and sort it. - Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); - IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - GroupingActivity activity = monitoringService.getGroupingActivityById(activityID); - Grouping grouping = getGrouping(activity); - Set sortedGroups = new TreeSet(new GroupComparator()); - sortedGroups.addAll(grouping.getGroups()); - - String groupOutput = buildGroupsStringXML(sortedGroups); - writeAJAXResponse(response, groupOutput); - return null; + } + + /** + * Get a list of group names and the number of users in each group. Designed to respond to an AJAX call. + * + * Input parameters: activityID + * + * Output format: "groupid,name,num users;groupid,name,num users" + */ + public ActionForward getGroups(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + + // get the grouping data and sort it. + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + GroupingActivity activity = monitoringService.getGroupingActivityById(activityID); + Grouping grouping = getGrouping(activity); + Set sortedGroups = new TreeSet(new GroupComparator()); + sortedGroups.addAll(grouping.getGroups()); + + String groupOutput = buildGroupsStringXML(sortedGroups); + writeAJAXResponse(response, groupOutput); + return null; + } + + /** + * Get a list of all the class members who aren't grouped yet. Designed to respond to an AJAX call. + * + * Input parameters: activityID + * + * Output format: "groupid,name,num users;groupid,name,num users" + */ + public ActionForward getClassMembersNotGrouped(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + + // get the grouping data and sort it. + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + Long lessonID = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + SortedSet users = monitoringService.getClassMembersNotGrouped(lessonID, activityID, true); + String groupOutput = buildUserStringXML(-1, users); + writeAJAXResponse(response, groupOutput); + return null; + + } + + /** + * Get a list of group names and the number of users in each group. Designed to respond to an AJAX call. + * + * Input parameters: activityID, groupID + * + * Output format: "userid,lastname,firstname;" + */ + public ActionForward getGroupMembers(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + + // TODO optimise this call - we don't really need the activity and the grouping - go straight to the group in + // the db + // get the group, and from there the user data and sort the user data. + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + GroupingActivity activity = monitoringService.getGroupingActivityById(activityID); + Grouping grouping = getGrouping(activity); + Set groups = grouping.getGroups(); + Iterator iter = groups.iterator(); + Group group = null; + while ((group == null) && iter.hasNext()) { + Group candidateGroup = (Group) iter.next(); + if (groupID.equals(candidateGroup.getGroupId())) { + group = candidateGroup; + } } + if (group == null) { + String error = "Group cannot be found. Activity was " + activity + " Grouping was " + grouping + + " Grouping ID was " + groupID; + LamsDispatchAction.log.error(error); + throw new MonitoringServiceException(error); + } - /** - * Get a list of all the class members who aren't grouped yet. Designed to respond to an AJAX call. - * - * Input parameters: activityID - * - * Output format: "groupid,name,num users;groupid,name,num users" - */ - public ActionForward getClassMembersNotGrouped(ActionMapping mapping, - ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + Set users = group.getUsers(); + SortedSet sortedUsers = new TreeSet(new LastNameAlphabeticComparator()); + sortedUsers.addAll(users); + String userOutput = buildUserStringXML(groupID, sortedUsers); + writeAJAXResponse(response, userOutput); + return null; + } - // get the grouping data and sort it. - Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); - Long lessonID = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); - IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - SortedSet users = monitoringService.getClassMembersNotGrouped(lessonID, activityID, true); - String groupOutput = buildUserStringXML(-1 ,users); - writeAJAXResponse(response, groupOutput); - return null; - + /** + * Output format: "userid,lastname,firstname;" + * + * @param sortedUsers + * @return String of users + */ + private String buildUserString(SortedSet sortedUsers) { + String userOutput = ""; + boolean first = true; + for (User user : sortedUsers) { + Integer userID = user.getUserId(); + String lastName = user.getLastName(); + String firstName = user.getFirstName(); + if (!first) { + userOutput = userOutput + ";"; + } else { + first = false; + } + userOutput = userOutput + userID + "," + lastName + "," + firstName; } - - /** - * Get a list of group names and the number of users in each group. Designed to respond to an AJAX call. - * - * Input parameters: activityID, groupID - * - * Output format: "userid,lastname,firstname;" - */ - public ActionForward getGroupMembers(ActionMapping mapping, - ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException { + return userOutput; + } - // TODO optimise this call - we don't really need the activity and the grouping - go straight to the group in the db - // get the group, and from there the user data and sort the user data. - Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); - Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); - IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - GroupingActivity activity = monitoringService.getGroupingActivityById(activityID); - Grouping grouping = getGrouping(activity); - Set groups = grouping.getGroups(); - Iterator iter = groups.iterator(); - Group group = null; - while (group==null && iter.hasNext()) { - Group candidateGroup = (Group) iter.next(); - if ( groupID.equals(candidateGroup.getGroupId()) ) - group = candidateGroup; - } - if ( group == null ) { - String error = "Group cannot be found. Activity was "+activity+" Grouping was "+grouping+" Grouping ID was "+groupID; - log.error(error); - throw new MonitoringServiceException(error); - } + /** + * @param sortedUsers + * @return String of users + */ + private String buildUserStringXML(long groupId, SortedSet sortedUsers) { + String userOutput = ""; + userOutput += "" + groupId + ""; + userOutput += ""; + for (User user : sortedUsers) { + Integer userID = user.getUserId(); + String lastName = user.getLastName(); + String firstName = user.getFirstName(); - Set users = group.getUsers(); - SortedSet sortedUsers = new TreeSet(new LastNameAlphabeticComparator()); - sortedUsers.addAll(users); - String userOutput = buildUserStringXML(groupID, sortedUsers); - writeAJAXResponse(response, userOutput); - return null; + userOutput += ""; + userOutput += "" + userID + ""; + userOutput += "" + firstName + ""; + userOutput += "" + lastName + ""; + userOutput += "" + firstName + " " + lastName + ""; + userOutput += ""; } + userOutput += ""; + return userOutput; + } + /** + * @param sortedGroups + * @return String of groups + */ + private String buildGroupsString(Set sortedGroups) { + String groupOutput = ""; - /** - * Output format: "userid,lastname,firstname;" - * @param sortedUsers - * @return String of users - */ - private String buildUserString(SortedSet sortedUsers) { - String userOutput = ""; - boolean first = true; - for ( User user : sortedUsers ) { - Integer userID = user.getUserId(); - String lastName = user.getLastName(); - String firstName = user.getFirstName(); - if ( ! first ) { - userOutput=userOutput+";"; - } else { - first = false; - } - userOutput=userOutput+userID+","+lastName+","+firstName; - } - return userOutput; + boolean first = true; + for (Group group : sortedGroups) { + Long groupId = group.getGroupId(); + String name = group.getGroupName(); + Integer numberOfMembers = group.getUsers().size(); + if (!first) { + groupOutput = groupOutput + ";"; + } else { + first = false; + } + groupOutput = groupOutput + groupId + "," + name + "," + numberOfMembers; } - - /** - * @param sortedUsers - * @return String of users - */ - private String buildUserStringXML(long groupId, SortedSet sortedUsers) { - String userOutput = ""; - userOutput += "" + groupId + ""; - userOutput += ""; - for ( User user : sortedUsers ) { - Integer userID = user.getUserId(); - String lastName = user.getLastName(); - String firstName = user.getFirstName(); - - userOutput+=""; - userOutput+="" + userID + ""; - userOutput+="" + firstName + ""; - userOutput+="" + lastName + ""; - userOutput+="" + firstName + " " + lastName + ""; - userOutput+=""; - } - userOutput += ""; - return userOutput; + + return groupOutput; + } + + /** + * @param sortedGroups + * @return String of groups + */ + private String buildGroupsStringXML(Set sortedGroups) { + String groupOutput = ""; + for (Group group : sortedGroups) { + Long groupId = group.getGroupId(); + String name = group.getGroupName(); + Integer numberOfMembers = group.getUsers().size(); + + groupOutput += ""; + groupOutput += "" + groupId + ""; + groupOutput += "" + name + ""; + groupOutput += "" + numberOfMembers + ""; + groupOutput += ""; } + groupOutput += ""; - /** - * @param sortedGroups - * @return String of groups - */ - private String buildGroupsString(Set sortedGroups) { - String groupOutput = ""; + return groupOutput; + } - boolean first = true; - for ( Group group: sortedGroups ) { - Long groupId = group.getGroupId(); - String name = group.getGroupName(); - Integer numberOfMembers = group.getUsers().size(); - if ( ! first ) { - groupOutput=groupOutput+";"; - } else { - first = false; - } - groupOutput=groupOutput+groupId+","+name+","+numberOfMembers; - } - - return groupOutput; + /** + * @param group + * @return String of xml with group added + */ + private String buildAddGroupStringXML(Group group) { + String groupOutput = ""; + groupOutput += "" + group.getGroupId() + ""; + groupOutput += "" + group.getGroupName() + ""; + groupOutput += ""; + return groupOutput; + } + + /** + * @param group + * @return String of xml with group added + */ + private String buildRemoveGroupStringXML(Long groupID) { + String groupOutput = ""; + groupOutput += "" + groupID + ""; + groupOutput += ""; + return groupOutput; + } + + /** + * @param group + * @return String of xml with group added + */ + private String buildChangeGroupNameStringXML(Long groupID, String name) { + String groupOutput = ""; + groupOutput += "" + groupID + ""; + groupOutput += "" + name + ""; + groupOutput += ""; + return groupOutput; + } + + /** + * @return String of xml with all needed language elements + */ + private String getLanguageXML() { + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + MessageService messageService = monitoringService.getMessageService(); + ArrayList languageCollection = new ArrayList(); + languageCollection.add(new String("button.finished")); + languageCollection.add(new String("label.grouping.non.grouped.users.heading")); + languageCollection.add(new String("label.grouping.status")); + languageCollection.add(new String("label.grouping.functions")); + languageCollection.add(new String("label.grouping.popup.change.group.name")); + languageCollection.add(new String("label.grouping.popup.change.group.name.field")); + languageCollection.add(new String("label.grouping.learners")); + languageCollection.add(new String("label.grouping.popup.delete.group")); + languageCollection.add(new String("label.grouping.popup.delete.group.message")); + languageCollection.add(new String("button.ok")); + languageCollection.add(new String("button.cancel")); + languageCollection.add(new String("button.yes")); + languageCollection.add(new String("button.no")); + languageCollection.add(new String("error.title")); + languageCollection.add(new String("label.grouping.max.num.in.group.heading")); + languageCollection.add(new String("label.grouping.popup.drag.selection.message")); + languageCollection.add(new String("label.grouping.general.instructions.branching")); + languageCollection.add(new String("label.grouping.popup.viewmode.message")); + + String languageOutput = ""; + + for (int i = 0; i < languageCollection.size(); i++) { + languageOutput += "" + + messageService.getMessage(languageCollection.get(i)) + ""; } - - /** - * @param sortedGroups - * @return String of groups - */ - private String buildGroupsStringXML(Set sortedGroups) { - String groupOutput = ""; - for ( Group group: sortedGroups ) { - Long groupId = group.getGroupId(); - String name = group.getGroupName(); - Integer numberOfMembers = group.getUsers().size(); - - groupOutput+=""; - groupOutput+="" + groupId + ""; - groupOutput+="" + name + ""; - groupOutput+="" + numberOfMembers + ""; - groupOutput+=""; - } - groupOutput += ""; - - return groupOutput; + + languageOutput += ""; + + return languageOutput; + } + + /** + * Add a new group. Designed to respond to an AJAX call. If the teacher wants to add more groups than the number of + * groups set in authoring, and this grouping isn't used for branching then reset the max number of groups to avoid + * that validation. + * + * Input parameters: activityID, name (group name) + * + * Output format: no data returned - just the header + */ + public ActionForward addGroup(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException, LessonServiceException { + + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + String name = WebUtil.readStrParam(request, GroupingAJAXAction.PARAM_NAME); + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + Group group = monitoringService.addGroup(activityID, name, true); + String groupResponse = buildAddGroupStringXML(group); + writeAJAXResponse(response, groupResponse); + return null; + } + + /** + * Remove a group. Cannot remove the group if it is in use (tool session ids exist). Designed to respond to an AJAX + * call. + * + * Input parameters: activityID, name: group name + * + * Output format: no data returned - just the header + */ + public ActionForward removeGroup(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException, LessonServiceException { + + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); + + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + monitoringService.removeGroup(activityID, groupID); + String responseString = buildRemoveGroupStringXML(groupID); + writeAJAXResponse(response, responseString); + return null; + } + + /** + * Add learners to a group. Designed to respond to an AJAX call. + * + * Input parameters: activityID, name: group name, members: comma separated list of users + * + * Output format: no data returned - just the header + */ + public ActionForward addMembers(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException, LessonServiceException { + + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); + String members = WebUtil.readStrParam(request, GroupingAJAXAction.PARAM_MEMBERS, true); + if (members != null) { + String[] membersSplit = members.split(","); + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + monitoringService.addUsersToGroup(activityID, groupID, membersSplit); } + writeAJAXOKResponse(response); + return null; + } - /** - * @param group - * @return String of xml with group added - */ - private String buildAddGroupStringXML(Group group) { - String groupOutput = ""; - groupOutput += "" + group.getGroupId() + ""; - groupOutput += "" + group.getGroupName() + ""; - groupOutput += ""; - return groupOutput; - } - - /** - * @param group - * @return String of xml with group added - */ - private String buildRemoveGroupStringXML(Long groupID) { - String groupOutput = ""; - groupOutput += "" + groupID + ""; - groupOutput += ""; - return groupOutput; + /** + * Add learners to a group. Designed to respond to an AJAX call. + * + * Input parameters: activityID, name: group name, members: comma separated list of users + * + * Output format: no data returned - just the header + */ + public ActionForward changeGroupName(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException, LessonServiceException { + + Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); + String name = WebUtil.readStrParam(request, GroupingAJAXAction.PARAM_NAME, true); + if (name != null) { + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + monitoringService.setGroupName(groupID, name); } - - /** - * @param group - * @return String of xml with group added - */ - private String buildChangeGroupNameStringXML(Long groupID, String name) { - String groupOutput = ""; - groupOutput += "" + groupID + ""; - groupOutput += "" + name + ""; - groupOutput += ""; - return groupOutput; - } - - /** - * @return String of xml with all needed language elements - */ - private String getLanguageXML(){ - IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - MessageService messageService = monitoringService.getMessageService(); - ArrayList languageCollection = new ArrayList(); - languageCollection.add(new String("button.finished")); - languageCollection.add(new String("label.grouping.non.grouped.users.heading")); - languageCollection.add(new String("label.grouping.status")); - languageCollection.add(new String("label.grouping.functions")); - languageCollection.add(new String("label.grouping.popup.change.group.name")); - languageCollection.add(new String("label.grouping.popup.change.group.name.field")); - languageCollection.add(new String("label.grouping.learners")); - languageCollection.add(new String("label.grouping.popup.delete.group")); - languageCollection.add(new String("label.grouping.popup.delete.group.message")); - languageCollection.add(new String("button.ok")); - languageCollection.add(new String("button.cancel")); - languageCollection.add(new String("button.yes")); - languageCollection.add(new String("button.no")); - languageCollection.add(new String("error.title")); - languageCollection.add(new String("label.grouping.max.num.in.group.heading")); - languageCollection.add(new String("label.grouping.popup.drag.selection.message")); - languageCollection.add(new String("label.grouping.general.instructions.branching")); - languageCollection.add(new String("label.grouping.popup.viewmode.message")); - - String languageOutput = ""; - - for(int i = 0; i < languageCollection.size(); i++){ - languageOutput += "" + messageService.getMessage(languageCollection.get(i)) + ""; - } - - languageOutput += ""; - - return languageOutput; - } - - /** - * Add a new group. Designed to respond to an AJAX call. - * If the teacher wants to add more groups than the number of groups set in authoring, and this grouping - * isn't used for branching then reset the max number of groups to avoid that validation. + String reponseSting = buildChangeGroupNameStringXML(groupID, name); + writeAJAXResponse(response, reponseSting); + return null; + } + + /** + * Remove a list of users from a group. Designed to respond to an AJAX call. * - * Input parameters: activityID, name (group name) - * - * Output format: no data returned - just the header - */ - public ActionForward addGroup(ActionMapping mapping, - ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException, LessonServiceException { + * Input parameters: activityID, name: group name, members: comma separated list of users + * + * Output format: no data returned - just the header + */ + public ActionForward removeMembers(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException, LessonServiceException { - Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); - String name = WebUtil.readStrParam(request, PARAM_NAME); - IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - Group group = monitoringService.addGroup(activityID, name, true); - String groupResponse = buildAddGroupStringXML(group); - writeAJAXResponse(response,groupResponse); - return null; + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); + String members = WebUtil.readStrParam(request, GroupingAJAXAction.PARAM_MEMBERS, true); + if (members != null) { + String[] membersSplit = members.split(","); + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + monitoringService.removeUsersFromGroup(activityID, groupID, membersSplit); } + writeAJAXOKResponse(response); + return null; + } - /** - * Remove a group. Cannot remove the group if it is in use (tool session ids exist). Designed to respond to an AJAX call. - * - * Input parameters: activityID, name: group name - * - * Output format: no data returned - just the header - */ - public ActionForward removeGroup(ActionMapping mapping, - ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException, LessonServiceException { + /** + * Moves users between groups, removing them from previous group and creating a new one, if needed. + */ + public ActionForward addMembersJSON(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws JSONException, IOException { + response.setContentType("application/json;charset=utf-8"); + JSONObject responseJSON = new JSONObject(); + boolean result = true; - Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); - Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + String members = WebUtil.readStrParam(request, GroupingAJAXAction.PARAM_MEMBERS); + String[] membersSplit = members.split(","); - IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - monitoringService.removeGroup(activityID, groupID); - String responseString = buildRemoveGroupStringXML(groupID); - writeAJAXResponse(response, responseString); - return null; + // remove users from current group + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + GroupingActivity groupingActivity = monitoringService.getGroupingActivityById(activityID); + Grouping grouping = groupingActivity.getCreateGrouping(); + User exampleUser = (User) MonitoringServiceProxy.getUserManagementService(getServlet().getServletContext()) + .findById(User.class, Integer.valueOf(membersSplit[0])); + Group group = grouping.getGroupBy(exampleUser); + // null group means that user is not assigned anywhere in this grouping + if (!group.isNull()) { + // check if user can be moved outside of this group + result = group.mayBeDeleted(); + + if (result) { + if (LamsDispatchAction.log.isDebugEnabled()) { + LamsDispatchAction.log.debug("Removing users " + members.toString() + " from group " + + group.getGroupId() + " in activity " + activityID); + } + + try { + monitoringService.removeUsersFromGroup(activityID, group.getGroupId(), membersSplit); + } catch (LessonServiceException e) { + LamsDispatchAction.log.error(e); + result = false; + } + } + + if (!result) { + // let JSP page know that this group became immutable + responseJSON.put("locked", true); + } } - /** - * Add learners to a group. Designed to respond to an AJAX call. - * - * Input parameters: activityID, name: group name, members: comma separated list of users - * - * Output format: no data returned - just the header - */ - public ActionForward addMembers(ActionMapping mapping, - ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException, LessonServiceException { + Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID, true); + // no group ID means that it has to be created + // group ID = -1 means that user is not being assigned to any new group, i.e. becomse unassigned + if (result && ((groupID == null) || (groupID > 0))) { + if (groupID == null) { + String name = WebUtil.readStrParam(request, GroupingAJAXAction.PARAM_NAME); + if (LamsDispatchAction.log.isDebugEnabled()) { + LamsDispatchAction.log.debug("Creating group with name \"" + name + "\" in activity " + activityID); + } + group = monitoringService.addGroup(activityID, name, true); + if (group == null) { + // group creation failed + result = false; + } else { + groupID = group.getGroupId(); + // let JSP page know that the group was given this ID + responseJSON.put("groupId", groupID); + } + } - Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); - Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); - String members = WebUtil.readStrParam(request, PARAM_MEMBERS, true); - if ( members != null ) { - String[] membersSplit = members.split(","); - IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - monitoringService.addUsersToGroup(activityID, groupID, membersSplit); - } - writeAJAXOKResponse(response); - return null; + if (result) { + if (LamsDispatchAction.log.isDebugEnabled()) { + LamsDispatchAction.log.debug("Adding users " + members.toString() + " to group " + groupID + + " in activity " + activityID); + } + + // add users to the given group + try { + monitoringService.addUsersToGroup(activityID, groupID, membersSplit); + } catch (LessonServiceException e) { + LamsDispatchAction.log.error(e); + result = false; + } + } } - - /** - * Add learners to a group. Designed to respond to an AJAX call. - * - * Input parameters: activityID, name: group name, members: comma separated list of users - * - * Output format: no data returned - just the header - */ - public ActionForward changeGroupName(ActionMapping mapping, - ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException, LessonServiceException { - Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); - String name = WebUtil.readStrParam(request, PARAM_NAME, true); - if ( name != null ) { - IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - monitoringService.setGroupName(groupID, name); - } - String reponseSting = buildChangeGroupNameStringXML(groupID, name); - writeAJAXResponse(response, reponseSting); - return null; + responseJSON.put("result", result); + response.getWriter().write(responseJSON.toString()); + return null; + } + + /** + * Renames the group. + */ + public ActionForward changeGroupNameJSON(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) { + Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); + String name = WebUtil.readStrParam(request, GroupingAJAXAction.PARAM_NAME); + if (name != null) { + if (LamsDispatchAction.log.isDebugEnabled()) { + LamsDispatchAction.log.debug("Renaming group " + groupID + " to \"" + name + "\""); + } + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + monitoringService.setGroupName(groupID, name); } + return null; + } - /** - * Remove a list of users from a group. Designed to respond to an AJAX call. - * - * Input parameters: activityID, name: group name, members: comma separated list of users - * - * Output format: no data returned - just the header - */ - public ActionForward removeMembers(ActionMapping mapping, - ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws IOException, ServletException, LessonServiceException { + /** + * Checks if a group can be removed and performs it. + */ + public ActionForward removeGroupJSON(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws JSONException, IOException { + response.setContentType("application/json;charset=utf-8"); + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() + .getServletContext()); + boolean result = true; - Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); - Long groupID = WebUtil.readLongParam(request, AttributeNames.PARAM_GROUP_ID); - String members = WebUtil.readStrParam(request, PARAM_MEMBERS, true); - if ( members != null ) { - String[] membersSplit = members.split(","); - IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet().getServletContext()); - monitoringService.removeUsersFromGroup(activityID, groupID, membersSplit); - } - writeAJAXOKResponse(response); - return null; + // check if the group can be removed + Group group = (Group) MonitoringServiceProxy.getUserManagementService(getServlet().getServletContext()) + .findById(Group.class, groupID); + result = group.mayBeDeleted(); + + if (result) { + try { + if (LamsDispatchAction.log.isDebugEnabled()) { + LamsDispatchAction.log.debug("Removing group " + groupID + " from activity " + activityID); + } + monitoringService.removeGroup(activityID, groupID); + } catch (LessonServiceException e) { + LamsDispatchAction.log.error(e); + result = false; + } } -} + JSONObject responseJSON = new JSONObject(); + responseJSON.put("result", result); + response.getWriter().write(responseJSON.toString()); + return null; + } +} \ No newline at end of file Index: lams_monitoring/web/grouping/chosenGrouping.jsp =================================================================== diff -u -r704c3a315685475c43a0ebe31b5cb7c0fb38cae0 -r3ead910e766fb518a08fc2d2cf53382431313cbf --- lams_monitoring/web/grouping/chosenGrouping.jsp (.../chosenGrouping.jsp) (revision 704c3a315685475c43a0ebe31b5cb7c0fb38cae0) +++ lams_monitoring/web/grouping/chosenGrouping.jsp (.../chosenGrouping.jsp) (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -1,4 +1,4 @@ - <%-- +<%-- Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) License Information: http://lamsfoundation.org/licensing/lams/2.0/ @@ -20,135 +20,55 @@ --%> <%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %> -<%@ taglib uri="tags-tiles" prefix="tiles" %> -<%@ taglib uri="tags-html" prefix="html" %> <%@ taglib uri="tags-fmt" prefix="fmt" %> <%@ taglib uri="tags-core" prefix="c" %> <%@ taglib uri="tags-lams" prefix="lams" %> - + - - - - <c:out value="${title}"/> - - - - - - + - -
- +

- -
- +

-

+ +

+ +

- -

-

-
+ +

+ +

+
-
- - -
-
- - - +

+ +

+ + +

+
+ +
- -
+ \ No newline at end of file Index: lams_monitoring/web/grouping/chosenGroupingFlash.jsp =================================================================== diff -u --- lams_monitoring/web/grouping/chosenGroupingFlash.jsp (revision 0) +++ lams_monitoring/web/grouping/chosenGroupingFlash.jsp (revision 3ead910e766fb518a08fc2d2cf53382431313cbf) @@ -0,0 +1,154 @@ +<%-- +Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) +License Information: http://lamsfoundation.org/licensing/lams/2.0/ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + + http://www.gnu.org/licenses/gpl.txt +--%> +<%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %> + +<%@ taglib uri="tags-tiles" prefix="tiles" %> +<%@ taglib uri="tags-html" prefix="html" %> +<%@ taglib uri="tags-fmt" prefix="fmt" %> +<%@ taglib uri="tags-core" prefix="c" %> +<%@ taglib uri="tags-lams" prefix="lams" %> + + + + + + + <c:out value="${title}"/> + + + + + + + + + + +
+ +

+ +

+ + + + +

+
+ +

+ + +

+

+
+ +
+ + +
+
+ + + +
+ + + \ No newline at end of file