Index: lams_admin/conf/language/lams/ApplicationResources_en_AU.properties =================================================================== diff -u -r16058884fbadd2c9dc636cf25d6feb8c1724f43a -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_admin/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision 16058884fbadd2c9dc636cf25d6feb8c1724f43a) +++ lams_admin/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -522,5 +522,6 @@ config.live.edit =Enable live edit config.export.portfolio =Enable export portfolio +sysadmin.extGroupsUrl =External groups URL #======= End labels: Exported 515 labels for en AU ===== Index: lams_admin/conf/xdoclet/struts-forms.xml =================================================================== diff -u -r7911b538e3fd0e8ae2bb56acac7eed836d4b2320 -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_admin/conf/xdoclet/struts-forms.xml (.../struts-forms.xml) (revision 7911b538e3fd0e8ae2bb56acac7eed836d4b2320) +++ lams_admin/conf/xdoclet/struts-forms.xml (.../struts-forms.xml) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -83,6 +83,7 @@ + Index: lams_admin/web/servermaintain.jsp =================================================================== diff -u -r83f9fae798c6d3d5cd19495c8c2c2d72f98e648e -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_admin/web/servermaintain.jsp (.../servermaintain.jsp) (revision 83f9fae798c6d3d5cd19495c8c2c2d72f98e648e) +++ lams_admin/web/servermaintain.jsp (.../servermaintain.jsp) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -88,6 +88,10 @@ : + + : + +
@@ -130,7 +134,6 @@ prefix: "${namev}", userinfoUrl: "${namev}", timeoutUrl: "${namev}", - timeoutUrl: "${namev}", timeToLiveLoginRequest: { required: "${namev}", min: "" Index: lams_bb_integration/RELEASE_NOTES.TXT =================================================================== diff -u -r17a5d8bfb41b992d96a9a3c9b2ed0401feaeb116 -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_bb_integration/RELEASE_NOTES.TXT (.../RELEASE_NOTES.TXT) (revision 17a5d8bfb41b992d96a9a3c9b2ed0401feaeb116) +++ lams_bb_integration/RELEASE_NOTES.TXT (.../RELEASE_NOTES.TXT) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -114,4 +114,5 @@ ==================== * LDEV-3510: Previous releases were opening lessons in a new window due to a change made for LDEV-3510. Now reverted back to previous behaviour with lesson loading in the same tab. -* LKC-61: Gradebook syncing change \ No newline at end of file +* LKC-61: Gradebook syncing change +* LDEV-3621: Ability to import and use groups from integrated server \ No newline at end of file Index: lams_bb_integration/WEB-INF/web.xml =================================================================== diff -u -r00e1a3313316c91b7c556e047f23962e84cb3ac9 -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_bb_integration/WEB-INF/web.xml (.../web.xml) (revision 00e1a3313316c91b7c556e047f23962e84cb3ac9) +++ lams_bb_integration/WEB-INF/web.xml (.../web.xml) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -10,6 +10,10 @@ org.lamsfoundation.ld.integration.blackboard.UserDataServlet + GroupDataServlet + org.lamsfoundation.ld.integration.blackboard.GroupDataServlet + + LamsLearningDesignServlet org.lamsfoundation.ld.integration.blackboard.LamsLearningDesignServlet @@ -48,6 +52,10 @@ /UserData + GroupDataServlet + /GroupData + + GradebookServlet /Gradebook Index: lams_bb_integration/lib/gson-2.2.4.jar =================================================================== diff -u Binary files differ Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/Constants.java =================================================================== diff -u -re145b2587e58d80c54fb208848e06d290c5473cd -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_bb_integration/src/org/lamsfoundation/ld/integration/Constants.java (.../Constants.java) (revision e145b2587e58d80c54fb208848e06d290c5473cd) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/Constants.java (.../Constants.java) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -39,6 +39,7 @@ public static final String PARAM_LEARNING_DESIGN_ID = "ldid"; public static final String PARAM_COURSE_ID = "course_id"; public static final String PARAM_FOLDER_ID = "folderId"; + public static final String PARAM_IS_USER_DETAILS_REQUIRED = "isUserDetailsRequired"; public static final String SERVLET_LOGIN_REQUEST = "/lams/LoginRequest"; public static final String SERVLET_ACTION_REQUEST = "/LamsActionRequest"; Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GroupDataServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GroupDataServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GroupDataServlet.java (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -0,0 +1,172 @@ +/**************************************************************** + * 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.0 + * 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 + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.ld.integration.blackboard; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; +import org.lamsfoundation.ld.integration.Constants; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import blackboard.base.BbList; +import blackboard.data.course.Course; +import blackboard.data.course.Group; +import blackboard.data.user.User; +import blackboard.persist.BbPersistenceManager; +import blackboard.persist.Id; +import blackboard.persist.PersistenceException; +import blackboard.persist.course.CourseDbLoader; +import blackboard.persist.course.GroupDbLoader; +import blackboard.persist.user.UserDbLoader; +import blackboard.platform.BbServiceManager; + +/** + * Fetch groups of the specified course. Serves 2 different types of calls: 1-initial request for group names; + * 2-sequential request for selected group users. + * + * @author Andrey Balan + */ +public class GroupDataServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + private static Logger logger = Logger.getLogger(GroupDataServlet.class); + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + try { + + // get Parameter values + String usernameParam = request.getParameter(Constants.PARAM_USER_ID); + String tsParam = request.getParameter(Constants.PARAM_TIMESTAMP); + String hashParam = request.getParameter(Constants.PARAM_HASH); + String courseIdParam = request.getParameter(Constants.PARAM_COURSE_ID); + String[] groupIdsParam = request.getParameterValues("extGroupIds"); + + // check paramaeters + if (usernameParam == null || tsParam == null || hashParam == null || courseIdParam == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "missing expected parameters"); + return; + } + + String secretKey = LamsPluginUtil.getSecretKey(); + String serverId = LamsPluginUtil.getServerId(); + + if (!LamsSecurityUtil.sha1( + tsParam.toLowerCase() + usernameParam.toLowerCase() + serverId.toLowerCase() + + secretKey.toLowerCase()).equals(hashParam)) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "authentication failed"); + return; + } + + // get the persistence manager + BbPersistenceManager bbPm = BbServiceManager.getPersistenceService().getDbPersistenceManager(); + CourseDbLoader cLoader = CourseDbLoader.Default.getInstance(); + GroupDbLoader groupLoader = (GroupDbLoader) bbPm.getLoader(GroupDbLoader.TYPE); + UserDbLoader userDbLoader = (UserDbLoader) bbPm.getLoader(UserDbLoader.TYPE); +// Id courseId = bbPm.generateId(Course.DATA_TYPE, courseIdParam); + + //create JSON objects to return + Course course = cLoader.loadByCourseId(courseIdParam); + List groups = groupLoader.loadAvailableByCourseId(course.getId()); + JsonArray jsonGroups = new JsonArray(); + + //in case groupIds is supplied - it means we need to provide groups along with all users + if (groupIdsParam != null && groupIdsParam.length > 0) { + + for (String groupIdParam : groupIdsParam) { + for (Group group : groups) { + //only groups with ids requested by LAMS should be processed + Id groupId = group.getId(); + if (!groupId.toExternalString().equals(groupIdParam)) { + continue; + } + + JsonObject jsonGroup = new JsonObject(); + jsonGroup.addProperty("groupId", groupId.toExternalString()); + jsonGroup.addProperty("groupName", group.getTitle()); + jsonGroups.add(jsonGroup); + + JsonArray jsonUsers = new JsonArray(); + jsonGroup.add("users", jsonUsers); + + BbList users = userDbLoader.loadByGroupId(groupId, null, true); + for (User user : users) { + JsonObject jsonUser = new JsonObject(); + jsonUsers.add(jsonUser); + + jsonUser.addProperty("userName", user.getUserName()); + + // The CSV list should be the format below + // ,<First name>,<Last name>,<Address>,<City>,<State>, + // <Postcode>,<Country>,<Day time number>,<Mobile number>, + // <Fax number>,<Email>,<Locale language>,<Locale country> + jsonUser.addProperty("1", user.getTitle()); + jsonUser.addProperty("2", user.getGivenName()); + jsonUser.addProperty("3", user.getFamilyName()); + jsonUser.addProperty("4", user.getStreet1() + user.getStreet2()); + jsonUser.addProperty("5", user.getCity()); + jsonUser.addProperty("6", user.getState()); + jsonUser.addProperty("7", user.getZipCode()); + jsonUser.addProperty("8", user.getCountry()); + jsonUser.addProperty("9", user.getHomePhone1()); + jsonUser.addProperty("10", user.getMobilePhone()); + jsonUser.addProperty("11", user.getBusinessFax()); + jsonUser.addProperty("12", user.getEmailAddress()); + String locale = user.getLocale(); + String localeLang = LamsSecurityUtil.getLanguage(locale); + String localeCountry = LamsSecurityUtil.getCountry(locale); + jsonUser.addProperty("13", localeLang); + jsonUser.addProperty("14", localeCountry); + } + } + } + } else { + for (Group group : groups) { + Id groupId = group.getId(); + JsonObject jsonGroup = new JsonObject(); + jsonGroup.addProperty("groupId", groupId.toExternalString()); + jsonGroup.addProperty("groupName", group.getTitle()); + jsonGroup.addProperty("groupSize", group.getGroupMemberships().size()); + jsonGroups.add(jsonGroup); + + } + } + + response.getWriter().write(jsonGroups.toString()); + + } catch (PersistenceException e) { + throw new ServletException("Failed to fetch course's groups", e); + } + } + +} + Index: lams_build/lib/lams/lams.jar =================================================================== diff -u -r509d15bcc570bff483a13152ef8c45cbae767e78 -rd27ed028b0e16c263776418b7bce22099fed4eed Binary files differ Index: lams_central/conf/language/lams/ApplicationResources_en_AU.properties =================================================================== diff -u -r9b8cc26765417af1609c8fcf728c0a3488ed663c -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_central/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision 9b8cc26765417af1609c8fcf728c0a3488ed663c) +++ lams_central/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -647,4 +647,6 @@ authoring.fla.page.svg.generator.title =SVG Generator authoring.fla.tool.groups.all =All +label.select.groups =Select groups + #======= End labels: Exported 439 labels for en AU ===== Index: lams_central/src/java/org/lamsfoundation/lams/web/OrganisationGroupAction.java =================================================================== diff -u -rb128694883b5db515c3f98f6803b573e04d203d2 -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_central/src/java/org/lamsfoundation/lams/web/OrganisationGroupAction.java (.../OrganisationGroupAction.java) (revision b128694883b5db515c3f98f6803b573e04d203d2) +++ lams_central/src/java/org/lamsfoundation/lams/web/OrganisationGroupAction.java (.../OrganisationGroupAction.java) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -48,6 +48,8 @@ import org.apache.tomcat.util.json.JSONException; import org.apache.tomcat.util.json.JSONObject; import org.lamsfoundation.lams.contentrepository.InvalidParameterException; +import org.lamsfoundation.lams.integration.dto.ExtGroupDTO; +import org.lamsfoundation.lams.integration.service.IIntegrationService; import org.lamsfoundation.lams.learningdesign.Group; import org.lamsfoundation.lams.learningdesign.GroupComparator; import org.lamsfoundation.lams.learningdesign.Grouping; @@ -73,6 +75,7 @@ * @struts.action path = "/OrganisationGroup" parameter = "method" validate = "false" * @struts.action-forward name = "viewGroupings" path = "/orgGrouping.jsp" * @struts.action-forward name = "viewGroups" path = "/orgGroup.jsp" + * @struts.action-forward name = "viewExtGroups" path = "/extGroups.jsp" */ public class OrganisationGroupAction extends DispatchAction { /** @@ -141,18 +144,19 @@ private static IUserManagementService userManagementService; private static ILessonService lessonService; private static ISecurityService securityService; + private static IIntegrationService integrationService; private static final String MAPPING_VIEW_GROUPINGS = "viewGroupings"; private static final String MAPPING_VIEW_GROUPS = "viewGroups"; + private static final String MAPPING_VIEW_EXT_GROUPS = "viewExtGroups"; /** * Shows course grouping list or redirects to groups if a grouping was already chosen. - * - * @throws IOException + * @throws Exception */ @SuppressWarnings("unchecked") public ActionForward viewGroupings(ActionMapping mapping, ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws JSONException, IOException { + HttpServletResponse response) throws Exception { Long activityID = WebUtil.readLongParam(request, AttributeNames.PARAM_ACTIVITY_ID, true); boolean lessonGroupsExist = getLessonGrouping(request, activityID, false) != null; if (lessonGroupsExist) { @@ -162,9 +166,9 @@ Integer userId = getUserDTO().getUserID(); Integer organisationId = WebUtil.readIntParam(request, AttributeNames.PARAM_ORGANISATION_ID, true); + Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_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(); } @@ -177,35 +181,46 @@ return null; } - boolean isGroupSuperuser = getUserManagementService().isUserInRole(userId, organisationId, Role.GROUP_ADMIN) - || getUserManagementService().isUserInRole(userId, organisationId, Role.GROUP_MANAGER); - if (OrganisationGroupAction.log.isDebugEnabled()) { OrganisationGroupAction.log.debug("Displaying course groupings for user " + userId + " and organisation " + organisationId); } request.setAttribute(AttributeNames.PARAM_ORGANISATION_ID, organisationId); - request.setAttribute("canEdit", isGroupSuperuser || (activityID != null)); - Set<OrganisationGroupingDTO> orgGroupingDTOs = new TreeSet<OrganisationGroupingDTO>(); - List<OrganisationGrouping> orgGroupings = getUserManagementService().findByProperty(OrganisationGrouping.class, - "organisationId", organisationId); - for (OrganisationGrouping orgGrouping : orgGroupings) { - orgGroupingDTOs.add(new OrganisationGroupingDTO(orgGrouping)); + // if this is a chosen group and lesson is created using integrations - show groups received from LMS instead of actual LAMS ones + if (lessonId != null && getIntegrationService().isLessonCreatedUsingIntegrations(lessonId)) { + List<ExtGroupDTO> 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)); + + Set<OrganisationGroupingDTO> orgGroupingDTOs = new TreeSet<OrganisationGroupingDTO>(); + List<OrganisationGrouping> 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); } - request.setAttribute("groupings", orgGroupingDTOs); - return mapping.findForward(OrganisationGroupAction.MAPPING_VIEW_GROUPINGS); } /** * View groups of the given grouping. - * - * @throws IOException + * @throws Exception */ @SuppressWarnings("unchecked") public ActionForward viewGroups(ActionMapping mapping, ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws JSONException, IOException { + HttpServletResponse response) throws Exception { Integer userId = getUserDTO().getUserID(); Integer organisationId = WebUtil.readIntParam(request, AttributeNames.PARAM_ORGANISATION_ID, true); Long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID, true); @@ -248,11 +263,16 @@ orgGroupingJSON.put("name", orgGrouping.getName()); } } + + //selected groups from integrated server + String[] extGroupIds = request.getParameterValues("extGroupIds"); + boolean isExternalGroupsSelected = extGroupIds != null && extGroupIds.length > 0; // check if any groups already exist in this grouping Grouping lessonGrouping = getLessonGrouping(request, activityId, true); Set<Group> lessonGroups = lessonGrouping == null ? null : lessonGrouping.getGroups(); - if ((activityId != null) && (orgGroupingId != null) && isDefaultChosenGrouping(lessonGrouping)) { + if ((activityId != null) && (isExternalGroupsSelected || (orgGroupingId != null)) + && isDefaultChosenGrouping(lessonGrouping)) { if (OrganisationGroupAction.log.isDebugEnabled()) { OrganisationGroupAction.log.debug("Removing default groups for grouping " + orgGroupingId); } @@ -268,16 +288,52 @@ lessonGroups = null; } - JSONArray orgGroupsJSON = null; + JSONArray orgGroupsJSON = new JSONArray(); Collection<User> learners = null; - // select source for groups (course or lesson) - if ((lessonGroups == null) || lessonGroups.isEmpty()) { + // if teacher selected groups from integrated server - show them + if (isExternalGroupsSelected) { + + if (lesson == null) { + lesson = (Lesson) getUserManagementService().findById(Lesson.class, lessonId); + } + learners = lesson.getLessonClass().getLearners(); + + //request all users from selected groups from integrated server + List<ExtGroupDTO> extGroups = getIntegrationService().getExtGroups(lessonId, extGroupIds); + + // serialize database group objects into JSON + if (extGroups != null) { + // sort groups by their name + Collections.sort(extGroups); + for (ExtGroupDTO extGroup : extGroups) { + JSONObject groupJSON = new JSONObject(); + groupJSON.put("name", extGroup.getGroupName()); + groupJSON.put("groupId", extGroup.getGroupId()); + if (extGroup.getUsers() != null) { + for (User groupUser : (List<User>) extGroup.getUsers()) { + JSONObject groupUserJSON = WebUtil.userToJSON(groupUser); + groupJSON.append("users", groupUserJSON); + + // remove the user who is already assigned to a group + learners.remove(groupUser); + } + } + orgGroupsJSON.put(groupJSON); + } + } + + // if groups haven't been selected yet - show all available groups in organisation + } else if ((lessonGroups == null) || lessonGroups.isEmpty()) { + learners = getUserManagementService().getUsersFromOrganisationByRole(organisationId, Role.LEARNER, false, true); Set<OrganisationGroup> orgGroups = orgGrouping == null ? null : orgGrouping.getGroups(); orgGroupsJSON = getOrgGroupsDetails(orgGroups, learners); + + // show already selected groups } else { + if (lesson == null) { lesson = (Lesson) getUserManagementService().findById(Lesson.class, lessonId); } @@ -394,11 +450,10 @@ /** * Deletes course grouping with the given ID. - * - * @throws IOException + * @throws Exception */ public ActionForward removeGrouping(ActionMapping mapping, ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws JSONException, IOException { + HttpServletResponse response) throws Exception { // check if user is allowed to edit groups Integer userId = getUserDTO().getUserID(); int organisationId = WebUtil.readIntParam(request, AttributeNames.PARAM_ORGANISATION_ID); @@ -557,4 +612,13 @@ } return OrganisationGroupAction.securityService; } + + private IIntegrationService getIntegrationService() { + if (OrganisationGroupAction.integrationService == null) { + WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() + .getServletContext()); + OrganisationGroupAction.integrationService = (IIntegrationService) ctx.getBean("integrationService"); + } + return OrganisationGroupAction.integrationService; + } } \ No newline at end of file Index: lams_central/web/extGroups.jsp =================================================================== diff -u --- lams_central/web/extGroups.jsp (revision 0) +++ lams_central/web/extGroups.jsp (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -0,0 +1,43 @@ +<%@ page contentType="text/html; charset=utf-8" language="java"%> +<%@ taglib uri="tags-lams" prefix="lams"%> +<%@ taglib uri="tags-fmt" prefix="fmt"%> +<%@ taglib uri="tags-core" prefix="c"%> + +<!DOCTYPE HTML> +<lams:html> +<lams:head> + <lams:css style="main" /> + <link rel="stylesheet" href="css/jquery-ui-redmond-theme.css" type="text/css" media="screen" /> + <link rel="stylesheet" href="css/orgGrouping.css" type="text/css" media="screen" /> + + <script type="text/javascript" src="includes/javascript/jquery.js"></script> + <script type="text/javascript" src="includes/javascript/jquery-ui.js"></script> +</lams:head> +<body> + +<div id="titleDiv"> + <fmt:message key="index.course.groups.title" /> +</div> + +<form id="ext-groups-form" action="<lams:LAMSURL/>OrganisationGroup.do?method=selectExtGroups" method="POST"> + <input name="lessonId" value="${param.lessonID}" type="hidden"/> + + <c:forEach var="group" items="${extGroups}"> + <div class="groupingContainer"> + <input name="extGroupIds" value="${group.groupId}" type="checkbox"> + <c:out value="${group.groupName}" /> + </input> + + <span class="groupCount" title='<fmt:message key="label.course.groups.grouping.count.label" />'> + (<fmt:message key='authoring.fla.page.prop.groups.learners' /> ${group.numberUsers}) + </span> + </div> + </c:forEach> + + <button type="submit" id="get-lms-course-groups-button" class="ui-button space-top" > + <fmt:message key='label.select.groups' /> + </button> + +</form> +</body> +</lams:html> \ No newline at end of file Index: lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/integration/ExtServerOrgMap.hbm.xml =================================================================== diff -u -r83f9fae798c6d3d5cd19495c8c2c2d72f98e648e -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/integration/ExtServerOrgMap.hbm.xml (.../ExtServerOrgMap.hbm.xml) (revision 83f9fae798c6d3d5cd19495c8c2c2d72f98e648e) +++ lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/integration/ExtServerOrgMap.hbm.xml (.../ExtServerOrgMap.hbm.xml) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -115,6 +115,16 @@ length="65535" not-null="false" /> + + <property + name="extGroupsUrl" + type="java.lang.String" + update="true" + insert="true" + column="ext_groups_url" + length="65535" + not-null="false" + /> <property name="disabled" Index: lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch2040050.sql =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch2040050.sql (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch2040050.sql (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -0,0 +1,12 @@ +-- Turn off autocommit, so nothing is committed if there is an error + +SET AUTOCOMMIT = 0; +SET FOREIGN_KEY_CHECKS=0; + +-- LDEV-3621 Ability to import and use groups from integrated server +ALTER TABLE lams_ext_server_org_map ADD COLUMN ext_groups_url text DEFAULT NULL; + +-- If there were no errors, commit and restore autocommit to on +SET FOREIGN_KEY_CHECKS=0; +COMMIT; +SET AUTOCOMMIT = 1; Index: lams_common/src/java/org/lamsfoundation/lams/integration/ExtServerOrgMap.java =================================================================== diff -u -r83f9fae798c6d3d5cd19495c8c2c2d72f98e648e -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_common/src/java/org/lamsfoundation/lams/integration/ExtServerOrgMap.java (.../ExtServerOrgMap.java) (revision 83f9fae798c6d3d5cd19495c8c2c2d72f98e648e) +++ lams_common/src/java/org/lamsfoundation/lams/integration/ExtServerOrgMap.java (.../ExtServerOrgMap.java) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -38,6 +38,8 @@ /** persistent field */ private String lessonFinishUrl; + + private String extGroupsUrl; /** persistent field */ private Boolean disabled; @@ -140,7 +142,15 @@ public void setLessonFinishUrl(String lessonFinishUrl) { this.lessonFinishUrl = lessonFinishUrl; } + + public String getExtGroupsUrl() { + return this.extGroupsUrl; + } + public void setExtGroupsUrl(String extGroupsUrl) { + this.extGroupsUrl = extGroupsUrl; + } + public Boolean getDisabled() { return this.disabled; } Index: lams_common/src/java/org/lamsfoundation/lams/integration/dto/ExtGroupDTO.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/integration/dto/ExtGroupDTO.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/integration/dto/ExtGroupDTO.java (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -0,0 +1,82 @@ +/**************************************************************** + * 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.0 + * 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 + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.integration.dto; + +import java.util.List; + +import org.lamsfoundation.lams.util.AlphanumComparator; + +/** Grouping object, suitable for sending to Flash */ + +public class ExtGroupDTO implements Comparable{ + private String groupId; + private String groupName; + + private int numberUsers; + // list of org.lamsfoundation.lams.usermanagement.dto.UserDTO + private List users; + + @Override + public int compareTo(Object o) { + ExtGroupDTO castOther = (ExtGroupDTO) o; + + String grp1Name = castOther != null && castOther.getGroupName() != null ? castOther.getGroupName() : ""; + String grp2Name = this.groupName != null ? this.groupName : ""; + + AlphanumComparator comparator = new AlphanumComparator(); + return comparator.compare(grp1Name, grp2Name); + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public int getNumberUsers() { + return numberUsers; + } + + public void setNumberUsers(int numberUsers) { + this.numberUsers = numberUsers; + } + + public List getUsers() { + return users; + } + + public void setUsers(List userList) { + this.users = userList; + } +} Index: lams_common/src/java/org/lamsfoundation/lams/integration/service/IIntegrationService.java =================================================================== diff -u -rbbd946d0a18f784ff7f72d0cebaaa3828980dfd0 -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_common/src/java/org/lamsfoundation/lams/integration/service/IIntegrationService.java (.../IIntegrationService.java) (revision bbd946d0a18f784ff7f72d0cebaaa3828980dfd0) +++ lams_common/src/java/org/lamsfoundation/lams/integration/service/IIntegrationService.java (.../IIntegrationService.java) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -32,6 +32,7 @@ import org.lamsfoundation.lams.integration.ExtUserUseridMap; import org.lamsfoundation.lams.integration.UserInfoFetchException; import org.lamsfoundation.lams.integration.UserInfoValidationException; +import org.lamsfoundation.lams.integration.dto.ExtGroupDTO; import org.lamsfoundation.lams.lesson.Lesson; import org.lamsfoundation.lams.usermanagement.User; @@ -147,4 +148,14 @@ * @throws UnsupportedEncodingException */ String getLessonFinishCallbackUrl(User user, Lesson lesson) throws UnsupportedEncodingException; + + /** + * Checks whether lesson was created using integrations. + * + * @param lessonId + * @return true in case lesson was created using integrations, false otherwise + */ + boolean isLessonCreatedUsingIntegrations(Long lessonId); + + List<ExtGroupDTO> getExtGroups(Long lessonId, String[] extGroupIds) throws Exception; } Index: lams_common/src/java/org/lamsfoundation/lams/integration/service/IntegrationService.java =================================================================== diff -u -rbbd946d0a18f784ff7f72d0cebaaa3828980dfd0 -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_common/src/java/org/lamsfoundation/lams/integration/service/IntegrationService.java (.../IntegrationService.java) (revision bbd946d0a18f784ff7f72d0cebaaa3828980dfd0) +++ lams_common/src/java/org/lamsfoundation/lams/integration/service/IntegrationService.java (.../IntegrationService.java) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -34,24 +34,29 @@ import java.net.URLConnection; import java.net.URLEncoder; import java.text.ParseException; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; -import org.apache.struts.action.ActionMessage; +import org.apache.tomcat.util.json.JSONArray; +import org.apache.tomcat.util.json.JSONObject; import org.lamsfoundation.lams.integration.ExtCourseClassMap; import org.lamsfoundation.lams.integration.ExtServerLessonMap; import org.lamsfoundation.lams.integration.ExtServerOrgMap; import org.lamsfoundation.lams.integration.ExtServerToolAdapterMap; import org.lamsfoundation.lams.integration.ExtUserUseridMap; import org.lamsfoundation.lams.integration.UserInfoFetchException; import org.lamsfoundation.lams.integration.UserInfoValidationException; +import org.lamsfoundation.lams.integration.dto.ExtGroupDTO; import org.lamsfoundation.lams.integration.security.RandomPasswordGenerator; +import org.lamsfoundation.lams.integration.util.GroupInfoFetchException; import org.lamsfoundation.lams.integration.util.LoginRequestDispatcher; import org.lamsfoundation.lams.lesson.Lesson; +import org.lamsfoundation.lams.lesson.service.ILessonService; import org.lamsfoundation.lams.usermanagement.AuthenticationMethod; import org.lamsfoundation.lams.usermanagement.Organisation; import org.lamsfoundation.lams.usermanagement.OrganisationState; @@ -62,11 +67,10 @@ import org.lamsfoundation.lams.usermanagement.UserOrganisationRole; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.util.CSVUtil; -import org.lamsfoundation.lams.util.Configuration; -import org.lamsfoundation.lams.util.ConfigurationKeys; import org.lamsfoundation.lams.util.HashUtil; import org.lamsfoundation.lams.util.LanguageUtil; import org.lamsfoundation.lams.util.ValidationUtil; +import org.lamsfoundation.lams.util.WebUtil; /** * <p> @@ -80,15 +84,8 @@ private static Logger log = Logger.getLogger(IntegrationService.class); private IUserManagementService service; + private ILessonService lessonService; - public IUserManagementService getService() { - return service; - } - - public void setService(IUserManagementService service) { - this.service = service; - } - @Override public ExtServerOrgMap getExtServerOrgMap(String serverId) { List list = service.findByProperty(ExtServerOrgMap.class, "serverid", serverId); @@ -544,9 +541,10 @@ service.save(map); } + @Override public String getLessonFinishCallbackUrl(User user, Lesson lesson) throws UnsupportedEncodingException { // the callback url must contain %username%, %lessonid%, %timestamp% and %hash% eg: - // "http://test100.ics.mq.edu.au/webapps/lams-plglamscontent-bb_bb60/UserData?uid=%username%&lessonid=%lessonid%&ts=%timestamp%&hash=%hash%"; + // "http://server.com/lams--bb/UserData?uid=%username%&lessonid=%lessonid%&ts=%timestamp%&hash=%hash%"; // where %username%, %lessonid%, %timestamp% and %hash% will be replaced with their real values String lessonFinishCallbackUrl = null; @@ -578,7 +576,149 @@ return lessonFinishCallbackUrl; } + + @Override + public boolean isLessonCreatedUsingIntegrations(Long lessonId) { + boolean isLessonCreatedByIntegratedServer = false; + if (lessonId != null) { + ExtServerLessonMap extServerLesson = getExtServerLessonMap(lessonId); + isLessonCreatedByIntegratedServer = extServerLesson != null; + } + + return isLessonCreatedByIntegratedServer; + } + + @Override + public List<ExtGroupDTO> getExtGroups(Long lessonId, String[] extGroupIds) throws Exception { + // the callback url must contain %username%, %lessonid%, %timestamp% and %hash% eg: + // "http://server.org/lams-bb/UserData?uid=%username%&lessonid=%lessonid%&ts=%timestamp%&hash=%hash%"; + // where %username%, %lessonid%, %timestamp% and %hash% will be replaced with their real values + + if (lessonId == null) { + throw new GroupInfoFetchException("Fail to fetch group data from external server:" + + " specified lessonId is null"); + } + + Lesson lesson = (Lesson) service.findById(Lesson.class, lessonId); + if (lesson == null) { + throw new GroupInfoFetchException("Fail to fetch group data from external server:" + + " specified lesson is null"); + } + Organisation organisation = lesson.getOrganisation(); + Integer organisationId = lesson.getOrganisation().getOrganisationId(); + + ExtServerLessonMap extServerLesson = getExtServerLessonMap(lessonId); + ExtCourseClassMap extCourse = getExtCourseClassMap(organisationId); + + // checks whether the lesson was created from extServer and whether it has lessonFinishCallbackUrl setting + if (extServerLesson == null) { + throw new GroupInfoFetchException("Fail to fetch group data from external server:" + + " there is no corresponding ExtServerLessonMap for lessonId " + lessonId); + } + + if (StringUtils.isBlank(extServerLesson.getExtServer().getExtGroupsUrl())) { + throw new GroupInfoFetchException("Fail to fetch group data from external server:" + + " corresponding ExtCourseClassMap doesn't have extGroupsUrl specified."); + } + + if (extCourse == null) { + throw new GroupInfoFetchException("Fail to fetch group data from external server:" + + " there is no corresponding ExtCourseClassMap for lessonId " + lessonId); + } + + ExtServerOrgMap serverMap = extServerLesson.getExtServer(); + + // construct real lessonFinishCallbackUrl + String lmsGroupsUrl = serverMap.getExtGroupsUrl(); + + //in case group info is also requested - provide selected groups' ids + if (extGroupIds != null && extGroupIds.length > 0) { + + if (!lmsGroupsUrl.contains("?")) { + lmsGroupsUrl += "?"; + } + String extGroupIdsParam = ""; + for (String extGroupId : extGroupIds) { + extGroupIdsParam += "&extGroupIds=" + extGroupId; + } + lmsGroupsUrl += extGroupIdsParam; + } + + String timestamp = Long.toString(new Date().getTime()); + String hashUsername = "username"; + String hash = hash(serverMap, hashUsername, timestamp); + + // set values for the parameters + HashMap<String, String> params = new HashMap<String, String>(); + params.put("uid", hashUsername); + params.put("ts", timestamp); + params.put("hash", hash); + params.put("course_id", extCourse.getCourseid()); + + // send the request to the external server + InputStream is = WebUtil.getResponseInputStreamFromExternalServer(lmsGroupsUrl, params); + BufferedReader isReader = new BufferedReader(new InputStreamReader(is)); + String str = isReader.readLine(); + + JSONArray jsonGroups = new JSONArray(str); + List<ExtGroupDTO> extGroups = new ArrayList<ExtGroupDTO>(); + for (int i = 0; i < jsonGroups.length(); i++) { + JSONObject jsonGroup = jsonGroups.getJSONObject(i); + ExtGroupDTO group = new ExtGroupDTO(); + group.setGroupName(jsonGroup.getString("groupName")); + group.setGroupId(jsonGroup.getString("groupId")); + extGroups.add(group); + + // in case group info is also requested - provide selected groups' ids + if (extGroupIds != null && extGroupIds.length > 0) { + ArrayList<User> users = new ArrayList<User>(); + + JSONArray jsonUsers = jsonGroup.getJSONArray("users"); + for (int j = 0; j < jsonUsers.length(); j++) { + JSONObject jsonUser = jsonUsers.getJSONObject(j); + + String extUsername = jsonUser.getString("userName"); + + ExtUserUseridMap extUserUseridMap = getExistingExtUserUseridMap(serverMap, extUsername); + + //create extUserUseridMap if it's not available + if (extUserUseridMap == null) { + // User properties list format: <Title>,<First name>,<Last name>,<Address>,<City>,<State>, + // <Postcode>,<Country>,<Day time number>,<Mobile number>,<Fax number>,<Email>,<Locale + // language>,<Locale country> + String[] userData = new String[14]; + for (int k=1; k <= 14; k++) { + String userProperty = jsonUser.getString("" + k); + userData[k-1] = userProperty; + } + String password = HashUtil.sha1(RandomPasswordGenerator.nextPassword(10)); + extUserUseridMap = createExtUserUseridMap(serverMap, extUsername, password, userData, true); + } + User user = extUserUseridMap.getUser(); + Integer userId = extUserUseridMap.getUser().getUserId(); + + //add user to organisation if it's not there + updateUserRoles(user, organisation, false); + + //check if user belong to the lesson. and if not - add it + if (!lesson.getLessonClass().getLearnersGroup().hasLearner(user)) { + lessonService.addLearner(lessonId, userId); + } + + users.add(user); + } + + group.setUsers(users); + } else { + group.setNumberUsers(jsonGroup.getInt("groupSize")); + } + + } + + return extGroups; + } + private ExtServerLessonMap getExtServerLessonMap(Long lessonId) { List list = service.findByProperty(ExtServerLessonMap.class, "lessonId", lessonId); if (list == null || list.size() == 0) { @@ -599,4 +739,21 @@ return (ExtUserUseridMap) list.get(0); } } + + private ExtCourseClassMap getExtCourseClassMap(Integer organisationId) { + List list = service.findByProperty(ExtCourseClassMap.class, "organisation.organisationId", organisationId); + if (list == null || list.size() == 0) { + return null; + } else { + return (ExtCourseClassMap) list.get(0); + } + } + + public void setService(IUserManagementService service) { + this.service = service; + } + + public void setLessonService(ILessonService lessonService) { + this.lessonService = lessonService; + } } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/integration/util/GroupInfoFetchException.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/integration/util/GroupInfoFetchException.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/integration/util/GroupInfoFetchException.java (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -0,0 +1,61 @@ +/**************************************************************** + * 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.0 + * 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 + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.integration.util; + +/** + * <p> + * <a href="GroupInfoFetchException.java.html"><i>View Source</i><a> + * </p> + * + * @author Andrey Balan + */ +@SuppressWarnings("serial") +public class GroupInfoFetchException extends Exception { + + public GroupInfoFetchException() { + } + + /** + * @param message + */ + public GroupInfoFetchException(String message) { + super(message); + } + + /** + * @param cause + */ + public GroupInfoFetchException(Throwable cause) { + super(cause); + } + + /** + * @param message + * @param cause + */ + public GroupInfoFetchException(String message, Throwable cause) { + super(message, cause); + } + +} Index: lams_common/src/java/org/lamsfoundation/lams/integrationContext.xml =================================================================== diff -u -r73c1472c62db79b6ec21be7a5cba1d901f193013 -rd27ed028b0e16c263776418b7bce22099fed4eed --- lams_common/src/java/org/lamsfoundation/lams/integrationContext.xml (.../integrationContext.xml) (revision 73c1472c62db79b6ec21be7a5cba1d901f193013) +++ lams_common/src/java/org/lamsfoundation/lams/integrationContext.xml (.../integrationContext.xml) (revision d27ed028b0e16c263776418b7bce22099fed4eed) @@ -3,6 +3,7 @@ <beans> <bean id="integrationServiceTarget" class="org.lamsfoundation.lams.integration.service.IntegrationService"> <property name="service"><ref bean="userManagementService"/></property> + <property name="lessonService"><ref bean="lessonService"/></property> </bean> <bean id="integrationService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">