Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903 --- lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903) @@ -526,7 +526,10 @@ authoring.fla.properties.dialog.title =Properties authoring.fla.group.naming.dialog.title =Group Naming authoring.fla.groups.to.branches.match.dialog_title =Match Groups to Branches +authoring.fla.course.groups.to.branches.match.dialog.title =Match Course Groups to Branching Groups authoring.fla.branch.mapping.groups.header =Groups +authoring.fla.branch.mapping.course.groups.header =Course groups +authoring.fla.branch.mapping.branching.groups.header =Branching groups authoring.fla.branch.mapping.group.header =Group authoring.fla.conditions.dialog.title =Select Output Conditions for Input authoring.fla.branch.mapping.conditions.header =Conditions Index: lams_central/src/java/org/lamsfoundation/lams/web/OrganisationGroupAction.java =================================================================== diff -u -rfc7f1fe67a744c918c3bf334bfc2370121317fad -r76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903 --- lams_central/src/java/org/lamsfoundation/lams/web/OrganisationGroupAction.java (.../OrganisationGroupAction.java) (revision fc7f1fe67a744c918c3bf334bfc2370121317fad) +++ lams_central/src/java/org/lamsfoundation/lams/web/OrganisationGroupAction.java (.../OrganisationGroupAction.java) (revision 76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903) @@ -20,7 +20,6 @@ * **************************************************************** */ - package org.lamsfoundation.lams.web; import java.io.IOException; @@ -33,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; @@ -98,7 +98,7 @@ /** * Shows course grouping list or redirects to groups if a grouping was already chosen. - * + * * @throws Exception */ @SuppressWarnings("unchecked") @@ -120,7 +120,7 @@ .getOrganisationId(); } - // check if user is allowed to view and edit groups + // check if user is allowed to view and edit groupings if (!getSecurityService().hasOrgRole(organisationId, userId, new String[] { Role.GROUP_ADMIN, Role.GROUP_MANAGER, Role.MONITOR, Role.AUTHOR }, "view organisation groupings", false)) { @@ -136,41 +136,39 @@ // if this is a chosen group and lesson is created using integrations - show groups received from LMS instead of actual LAMS ones if (getIntegrationService().isIntegratedServerGroupFetchingAvailable(lessonId)) { - + if (lessonId == null) { //it's when a learner clicks back button on groups page Activity activity = getLearnerService().getActivity(activityID); lessonId = getLearnerService().getLessonByActivity(activity).getLessonId(); request.setAttribute("lessonID", lessonId); } - + List extGroups = getIntegrationService().getExtGroups(lessonId, null); request.setAttribute("extGroups", extGroups); // TODO ? show only with user number >0 return mapping.findForward(OrganisationGroupAction.MAPPING_VIEW_EXT_GROUPS); - } else { + } - boolean isGroupSuperuser = getUserManagementService().isUserInRole(userId, organisationId, Role.GROUP_ADMIN) - || getUserManagementService().isUserInRole(userId, organisationId, Role.GROUP_MANAGER); - request.setAttribute("canEdit", isGroupSuperuser || (activityID != null)); + boolean isGroupSuperuser = getUserManagementService().isUserInRole(userId, organisationId, Role.GROUP_ADMIN) + || getUserManagementService().isUserInRole(userId, organisationId, Role.GROUP_MANAGER); + request.setAttribute("canEdit", isGroupSuperuser || (activityID != null)); - Set orgGroupingDTOs = new TreeSet(); - List orgGroupings = getUserManagementService() - .findByProperty(OrganisationGrouping.class, "organisationId", organisationId); - for (OrganisationGrouping orgGrouping : orgGroupings) { - orgGroupingDTOs.add(new OrganisationGroupingDTO(orgGrouping)); - } - request.setAttribute("groupings", orgGroupingDTOs); - - return mapping.findForward(OrganisationGroupAction.MAPPING_VIEW_GROUPINGS); + Set orgGroupingDTOs = new TreeSet(); + List orgGroupings = getUserManagementService().findByProperty(OrganisationGrouping.class, + "organisationId", organisationId); + for (OrganisationGrouping orgGrouping : orgGroupings) { + orgGroupingDTOs.add(new OrganisationGroupingDTO(orgGrouping)); } + request.setAttribute("groupings", orgGroupingDTOs); + return mapping.findForward(OrganisationGroupAction.MAPPING_VIEW_GROUPINGS); } /** * View groups of the given grouping. - * + * * @throws Exception */ @SuppressWarnings("unchecked") @@ -426,7 +424,7 @@ /** * Deletes course grouping with the given ID. - * + * * @throws Exception */ public ActionForward removeGrouping(ActionMapping mapping, ActionForm form, HttpServletRequest request, @@ -451,9 +449,72 @@ } /** - * Build JSON objects based on existing lesson-level groups. + * Fetches course and branching so they can get matched by user. */ @SuppressWarnings("unchecked") + public ActionForward getGroupsForMapping(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, JSONException { + Long orgGroupingId = WebUtil.readLongParam(request, "groupingId"); + Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID); + + OrganisationGrouping orgGrouping = (OrganisationGrouping) getUserManagementService() + .findById(OrganisationGrouping.class, orgGroupingId); + JSONArray groupsJSON = new JSONArray(); + SortedSet orgGroups = new TreeSet(orgGrouping.getGroups()); + for (OrganisationGroup group : orgGroups) { + JSONObject groupJSON = new JSONObject(); + groupJSON.put("id", group.getGroupId()); + groupJSON.put("name", group.getName()); + groupsJSON.put(groupJSON); + } + + GroupingActivity branchingGrouping = (GroupingActivity) getUserManagementService().findById(Activity.class, + activityID); + JSONArray branchesJSON = new JSONArray(); + Grouping grouping = branchingGrouping.getCreateGrouping(); + SortedSet groups = new TreeSet(grouping.getGroups()); + for (Group group : groups) { + JSONObject groupJSON = new JSONObject(); + groupJSON.put("id", group.getGroupId()); + groupJSON.put("name", group.getGroupName()); + branchesJSON.put(groupJSON); + } + + JSONObject responseJSON = new JSONObject(); + responseJSON.put("branches", branchesJSON); + responseJSON.put("groups", groupsJSON); + + response.setContentType("application/json;charset=utf-8"); + response.getWriter().write(responseJSON.toString()); + return null; + } + + /** + * Stores course groups to branching groups mapping. + */ + public ActionForward saveGroupMappings(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws IOException, JSONException { + JSONArray groupMapping = new JSONArray(request.getParameter("mapping")); + for (int index = 0; index < groupMapping.length(); index++) { + JSONObject entry = groupMapping.getJSONObject(index); + Long orgGroupID = entry.getLong("groupID"); + Long branchingGroupID = entry.getLong("branchID"); + OrganisationGroup orgGroup = (OrganisationGroup) getUserManagementService() + .findById(OrganisationGroup.class, orgGroupID); + Group branchingGroup = (Group) getUserManagementService().findById(Group.class, branchingGroupID); + // put all users from course group to mapped branching group + // why does it get saved? ther is no transaction here... + branchingGroup.getUsers().addAll(orgGroup.getUsers()); + } + response.setContentType("text/plain;charset=utf-8"); + // Javascript waits for this response + response.getWriter().write("OK"); + return null; + } + + /** + * Build JSON objects based on existing lesson-level groups. + */ private JSONArray getLessonGroupsDetails(Set groups, Collection learners) throws JSONException { // serialize database group objects into JSON JSONArray groupsJSON = new JSONArray(); @@ -498,7 +559,7 @@ return comparator.compare(grp1Name, grp2Name); } }; - + // serialize database group objects into JSON JSONArray groupsJSON = new JSONArray(); if (groups != null) { @@ -531,9 +592,11 @@ @SuppressWarnings("unchecked") private Grouping getLessonGrouping(HttpServletRequest request, Long activityID, boolean allowDefault) { if (activityID != null) { - GroupingActivity groupingActivity = (GroupingActivity) getUserManagementService() - .findById(GroupingActivity.class, activityID); - Grouping grouping = groupingActivity.getCreateGrouping(); + // we need to fetch real objects instead of stubs/proxies + Activity activity = (Activity) getUserManagementService().findById(Activity.class, activityID); + Grouping grouping = activity.isChosenBranchingActivity() ? activity.getGrouping() + : ((GroupingActivity) getUserManagementService().findById(GroupingActivity.class, activityID)) + .getCreateGrouping(); if ((grouping != null) && (grouping.getGroups() != null)) { Set groups = grouping.getGroups(); @@ -542,16 +605,15 @@ request.setAttribute(GroupingAJAXAction.PARAM_USED_FOR_BRANCHING, isUsedForBranching); // check if it is immutable (for branching) or default groups are allowed - return !groups.isEmpty() && (isUsedForBranching || allowDefault || !isDefaultChosenGrouping(grouping)) - ? grouping : null; + return !groups.isEmpty() && (allowDefault || !isDefaultChosenGrouping(grouping)) ? grouping : null; } } return null; } /** - * Check if the give groups are default for chosen grouping. There is actually no good way to detect this, but even + * Check if the given groups are default for chosen grouping. There is actually no good way to detect this, but even * if a custom grouping is mistaken for the default one, it should bring little harm. */ @SuppressWarnings("unchecked") @@ -583,13 +645,12 @@ } return OrganisationGroupAction.userManagementService; } - + private ICoreLearnerService getLearnerService() { if (OrganisationGroupAction.learnerService == null) { WebApplicationContext ctx = WebApplicationContextUtils .getRequiredWebApplicationContext(getServlet().getServletContext()); - OrganisationGroupAction.learnerService = (ICoreLearnerService) ctx - .getBean("learnerService"); + OrganisationGroupAction.learnerService = (ICoreLearnerService) ctx.getBean("learnerService"); } return OrganisationGroupAction.learnerService; } Index: lams_central/web/includes/javascript/orgGrouping.js =================================================================== diff -u -r75f5cb01345becd855033a4219dab63cb1c667a1 -r76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903 --- lams_central/web/includes/javascript/orgGrouping.js (.../orgGrouping.js) (revision 75f5cb01345becd855033a4219dab63cb1c667a1) +++ lams_central/web/includes/javascript/orgGrouping.js (.../orgGrouping.js) (revision 76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903) @@ -1,4 +1,6 @@ -$(document).ready(function() { +var gtbDialog = null; + +$(document).ready(function() { $(".ui-button").button(); }); @@ -9,7 +11,7 @@ } } -function showGroups(groupingId) { +function showGroups(groupingId, force) { var url = LAMS_URL + 'OrganisationGroup.do?method=viewGroups&organisationID=' + organisationId; if (lessonId) { @@ -22,7 +24,7 @@ if (lessonMode) { var executeShow = true; - if (groupingId) { + if (groupingId && !force) { // 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)); @@ -35,4 +37,197 @@ // load to dialog window.parent.showOrgGroupDialogContents(null, 880, 500, url); } +} + +/** + * Opens dialog for matching course groups to groups assigned to branches in a branching activity. + */ +function openGroupMappingDialog(groupingId) { + /* dialog and labels are called "branching" because + * 1) it was taken from Authoring dialog for groups-to-branches mapping + * 2) two types of groups: course and branching could easily get mixed + */ + if (!gtbDialog) { + // create the dialog + gtbDialog = $('#groupMappingDialog') + .dialog({ + 'autoOpen' : false, + 'modal' : true, + 'show' : 'fold', + 'hide' : 'fold', + 'width' : 650, + 'height' : 400, + 'title' : LABELS.COURSE_GROUPS_TO_BRANCHES_MATCH_DIALOG_TITLE, + 'buttons' : [ + { + 'text' : LABELS.OK_BUTTON, + 'click' : function() { + var dialog = $(this), + groupsToBranches = []; + + // fill JSON with group pairs + $('.branchMappingBoundItemCell div, .branchMappingFreeItemCell div', dialog) + .each(function(){ + var groupID = $(this).attr('id'), + boundItem = $(this).data('boundItem'), + branchID = boundItem ? boundItem.attr('id') : null; + + // add the mapping + if (branchID) { + groupsToBranches.push({ + 'groupID' : groupID, + 'branchID' : branchID + }); + } + }); + + // save the mapping + $.ajax({ + url : LAMS_URL + 'OrganisationGroup.do', + data : { + 'method' : 'saveGroupMappings', + 'mapping' : JSON.stringify(groupsToBranches) + }, + success : function(response) { + // LAMS can reply 200 even if there is an error, so we need OK response + if (response == 'OK') { + // go straight to branching groups for final check + showGroups(groupingId, true); + } + } + }); + } + } + ], + 'open' : function(){ + var dialog = $(this), + groupsCell = $('.branchMappingFreeItemCell', dialog), + branchesCell = $('.branchMappingFreeBranchCell', dialog), + groupCell = $('.branchMappingBoundItemCell', dialog), + branchCell = $('.branchMappingBoundBranchCell', dialog); + + // clear out previous entries + $('.branchMappingListCell', dialog).empty(); + + // fetch course and branching groups + $.ajax({ + url : LAMS_URL + 'OrganisationGroup.do', + data : { + 'method' : 'getGroupsForMapping', + 'groupingId' : groupingId, + 'activityID' : groupingActivityId + }, + dataType : 'json', + success : function(response) { + // fill table with course and branching groups + $.each(response.groups, function(){ + var group = this, + groupElem = $('
').click(selectGroupMappingListItem) + .text(group.name).attr('id', group.id); + + $.each(response.branches, function() { + // check if a branching group alread exists with the same name as a course group + if (this.name == group.name) { + var branchElem = $('
').click(selectGroupMappingListItem) + .appendTo(branchCell) + .text(this.name) + .attr('id', this.id) + .data('boundItem', groupElem); + groupElem.appendTo(groupCell).data('boundItem', branchElem); + groupElem = null; + return false; + } + }); + + if (groupElem) { + // no existing mapping was found, make the group available for mapping + groupElem.appendTo(groupsCell); + } + }); + // fill in branch groups + $.each(response.branches, function(){ + $('
').click(selectGroupMappingListItem).appendTo(branchesCell) + .text(this.name).attr('id', this.id); + }); + } + }); + } + }); + + // initialise buttons and labels + $('.branchMappingAddButton', gtbDialog).button({ + 'icons' : { + 'primary' : 'ui-icon-seek-next' + }, + 'text' : false + }).click(function(){ + addGroupMapping(); + }); + $('.branchMappingRemoveButton', gtbDialog).button({ + 'icons' : { + 'primary' : 'ui-icon-seek-prev' + }, + 'text' : false + }).click(function(){ + removeGroupMapping(); + }); + } + + gtbDialog.dialog('open'); +}; + +/** + * Make a pair out of selected groups. + */ +function addGroupMapping(){ + var dialog = gtbDialog, + selectedItem = $('.branchMappingFreeItemCell .selected', dialog), + selectedBranch = $('.branchMappingFreeBranchCell .selected', dialog); + + if (selectedItem.length != 1 || selectedBranch.length != 1) { + return; + } + + // original branch stays in its list + selectedBranch = selectedBranch.clone().click(selectGroupMappingListItem); + // add info about the pair for later reference + selectedItem.data('boundItem', selectedBranch); + selectedBranch.data('boundItem', selectedItem); + var itemCell = $('.branchMappingBoundItemCell', dialog), + branchCell = $('.branchMappingBoundBranchCell', dialog); + // clear existing selection + $('.selected', itemCell).removeClass('selected'); + $('.selected', branchCell).removeClass('selected'); + itemCell.append(selectedItem); + branchCell.append(selectedBranch); +} + +function removeGroupMapping() { + var dialog = gtbDialog, + selectedItem = $('.branchMappingBoundItemCell .selected', dialog), + selectedBranch = $('.branchMappingBoundBranchCell .selected', dialog); + + if (selectedItem.length != 1 || selectedBranch.length != 1) { + return; + } + + selectedItem.removeData('boundItem'); + selectedBranch.remove(); + $('.branchMappingFreeItemCell', dialog).append(selectedItem); +} + +/** + * Highlight clicked group + */ +function selectGroupMappingListItem(){ + var item = $(this), + boundItem = item.data('boundItem'); + + item.siblings().removeClass('selected'); + item.addClass('selected'); + + if (boundItem) { + boundItem.siblings().removeClass('selected'); + boundItem.addClass('selected'); + } } \ No newline at end of file Index: lams_central/web/orgGrouping.jsp =================================================================== diff -u -rd68cf5c3211f63bf06f77a7c3ec6340561d120db -r76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903 --- lams_central/web/orgGrouping.jsp (.../orgGrouping.jsp) (revision d68cf5c3211f63bf06f77a7c3ec6340561d120db) +++ lams_central/web/orgGrouping.jsp (.../orgGrouping.jsp) (revision 76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903) @@ -9,16 +9,58 @@ + + -
- - - - - - - - -
+
+ + + + + + + + +
- - <%-- In lesson mode do not show groupings with zero groups --%> - -
- - - - - (${grouping.groupCount}) - - - - + + <%-- In lesson mode do not show groupings with zero groups --%> + +
+ + + (${grouping.groupCount}) + + + +
+
+
+ + +
+
- - - -
- -
-
+ + + + \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/Group.java =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903 --- lams_common/src/java/org/lamsfoundation/lams/learningdesign/Group.java (.../Group.java) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/Group.java (.../Group.java) (revision 76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903) @@ -35,7 +35,7 @@ import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.util.Nullable; -public class Group implements Serializable, Nullable, Comparable { +public class Group implements Serializable, Nullable, Comparable { public final static int STAFF_GROUP_ORDER_ID = 1; public final static String NAME_OF_STAFF_GROUP = "Staff Group"; @@ -263,11 +263,10 @@ * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override - public int compareTo(Object o) { - Group castOther = (Group) o; - return new CompareToBuilder().append(this.getOrderId(), castOther.getOrderId()) - .append(this.getGroupId(), castOther.getGroupId()).append(this.getGroupName(), castOther.getGroupName()) - .append(this.getGroupUIID(), castOther.getGroupUIID()).toComparison(); + public int compareTo(Group group) { + return new CompareToBuilder().append(this.getOrderId(), group.getOrderId()) + .append(this.getGroupId(), group.getGroupId()).append(this.getGroupName(), group.getGroupName()) + .append(this.getGroupUIID(), group.getGroupUIID()).toComparison(); } // --------------------------------------------------------------------- Index: lams_common/src/java/org/lamsfoundation/lams/usermanagement/OrganisationGroup.java =================================================================== diff -u -rd0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa -r76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903 --- lams_common/src/java/org/lamsfoundation/lams/usermanagement/OrganisationGroup.java (.../OrganisationGroup.java) (revision d0b6f213cba1026b0c9fdbdaa5dd44a49eddd3aa) +++ lams_common/src/java/org/lamsfoundation/lams/usermanagement/OrganisationGroup.java (.../OrganisationGroup.java) (revision 76986ed39cc2a2f0ca8e98ea1d8f2e36fed03903) @@ -26,14 +26,15 @@ import java.io.Serializable; import java.util.Set; +import org.apache.commons.lang.builder.CompareToBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; /** * This is a course-level group of learners. * * @hibernate.class table="lams_organisation_group" */ -public class OrganisationGroup implements Serializable { +public class OrganisationGroup implements Serializable, Comparable { /** identifier field */ private Long groupId; @@ -127,4 +128,10 @@ } return true; } + + @Override + public int compareTo(OrganisationGroup group) { + return new CompareToBuilder().append(this.getGroupId(), group.getGroupId()) + .append(this.getName(), group.getName()).toComparison(); + } } \ No newline at end of file