Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/CloneLessonsServlet.java =================================================================== diff -u -r14810d5d6e140873396126a1103aa3113b257b4a -r67bf7cc2eed9d841593d383cf139d63a65e1a8a6 --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/CloneLessonsServlet.java (.../CloneLessonsServlet.java) (revision 14810d5d6e140873396126a1103aa3113b257b4a) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/CloneLessonsServlet.java (.../CloneLessonsServlet.java) (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -32,6 +32,8 @@ import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; +import org.lamsfoundation.ld.integration.util.LineitemUtil; import blackboard.base.BbList; import blackboard.data.content.Content; @@ -46,6 +48,9 @@ import blackboard.persist.course.CourseDbLoader; import blackboard.persist.course.CourseMembershipDbLoader; import blackboard.persist.navigation.CourseTocDbLoader; +import blackboard.platform.BbServiceManager; +import blackboard.platform.context.Context; +import blackboard.platform.context.ContextManager; import blackboard.portal.data.ExtraInfo; import blackboard.portal.data.PortalExtraInfo; import blackboard.portal.servlet.PortalUtil; @@ -68,6 +73,10 @@ String newLessonIds = ""; try { + // get Blackboard context + ContextManager ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + Context ctx = ctxMgr.setContext(request); + CourseDbLoader courseLoader = CourseDbLoader.Default.getInstance(); Course course = courseLoader.loadByCourseId(courseIdParam); PkId courseId = (PkId) course.getId(); @@ -144,16 +153,10 @@ // persist updated content persister.persist(content); + + //update lineitem details + LineitemUtil.updateLineitemLessonId(content, _course_id, newLessonId, ctx, teacher.getUserName()); - // store internalContentId -> externalContentId. It's used for lineitem removal (delete.jsp) - PortalExtraInfo pei = PortalUtil.loadPortalExtraInfo(null, null, "LamsStorage"); - ExtraInfo ei = pei.getExtraInfo(); - ei.setValue(_content_id, Long.toString(newLessonId)); - PortalUtil.savePortalExtraInfo(pei); - - // Gradebook column will be copied automatically if appropriate option is selected on - // cloning lesson page - logger.debug("Lesson (lessonId=" + urlLessonId + ") was successfully cloned to the one (lessonId=" + newLessonId + ")."); Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GradebookServlet.java =================================================================== diff -u -r8efc33beea4026ba7ed13ac6c2c3cc2dc8ef8221 -r67bf7cc2eed9d841593d383cf139d63a65e1a8a6 --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GradebookServlet.java (.../GradebookServlet.java) (revision 8efc33beea4026ba7ed13ac6c2c3cc2dc8ef8221) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GradebookServlet.java (.../GradebookServlet.java) (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -30,11 +30,7 @@ import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.text.DecimalFormat; -import java.util.List; -import java.util.Set; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -44,41 +40,28 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import org.apache.commons.codec.binary.Hex; import org.apache.log4j.Logger; import org.lamsfoundation.ld.integration.Constants; -import org.lamsfoundation.ld.util.LineitemUtil; +import org.lamsfoundation.ld.integration.util.LamsPluginUtil; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; +import org.lamsfoundation.ld.integration.util.LineitemUtil; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; -import blackboard.data.content.Content; -import blackboard.data.content.CourseDocument; -import blackboard.data.course.Course; import blackboard.data.course.CourseMembership; import blackboard.data.gradebook.Lineitem; import blackboard.data.gradebook.Score; import blackboard.data.user.User; -import blackboard.persist.BbPersistenceManager; -import blackboard.persist.Container; import blackboard.persist.Id; import blackboard.persist.KeyNotFoundException; -import blackboard.persist.PkId; -import blackboard.persist.content.ContentDbLoader; -import blackboard.persist.course.CourseDbLoader; import blackboard.persist.course.CourseMembershipDbLoader; -import blackboard.persist.gradebook.LineitemDbLoader; -import blackboard.persist.gradebook.LineitemDbPersister; import blackboard.persist.gradebook.ScoreDbLoader; import blackboard.persist.gradebook.ScoreDbPersister; import blackboard.persist.user.UserDbLoader; import blackboard.platform.BbServiceManager; -import blackboard.platform.context.Context; import blackboard.platform.context.ContextManager; -import blackboard.portal.data.ExtraInfo; -import blackboard.portal.data.PortalExtraInfo; -import blackboard.portal.servlet.PortalUtil; /** * Deals with Blackboard Grade Center. @@ -98,6 +81,10 @@ try { // get Blackboard context ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + UserDbLoader userLoader = UserDbLoader.Default.getInstance(); + CourseMembershipDbLoader courseMembershipLoader = CourseMembershipDbLoader.Default.getInstance(); + ScoreDbLoader scoreLoader = ScoreDbLoader.Default.getInstance(); + ScoreDbPersister scorePersister = ScoreDbPersister.Default.getInstance(); // get Parameter values String userName = request.getParameter(Constants.PARAM_USER_ID); @@ -120,48 +107,13 @@ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "authentication failed"); return; } - - //check if isGradebookcenter - PortalExtraInfo portalExtraInfo = PortalUtil.loadPortalExtraInfo(null, null, "LamsStorage"); - ExtraInfo extraInfo = portalExtraInfo.getExtraInfo(); - Set bbContentIds = extraInfo.getKeys(); - String bbContentId = null; - for (String bbContentIdIter : bbContentIds) { - String lamsLessonId = extraInfo.getValue(bbContentIdIter); - if (lamsLessonIdParam.equals(lamsLessonId)) { - bbContentId = bbContentIdIter; - break; - } - } - - // exit method as it was created in version prior to 1.2.1 and thus don't have lineitem - if (bbContentId == null) { - response.sendError(HttpServletResponse.SC_CONFLICT, "exit method as it was created in version prior to 1.2.1 and thus don't have lineitem"); - return; - } - - //check isGradecenter option is ON - BbPersistenceManager bbPm = BbServiceManager.getPersistenceService().getDbPersistenceManager(); - Container bbContainer = bbPm.getContainer(); - Id contentId = new PkId( bbContainer, CourseDocument.DATA_TYPE, bbContentId ); - ContentDbLoader contentDbLoader = (ContentDbLoader) bbPm.getLoader( ContentDbLoader.TYPE ); - Content bbContent = (Content)contentDbLoader.loadById( contentId ); - //check isGradecenter option is ON - if (!bbContent.getIsDescribed()) {//(isDescribed field is used for storing isGradecenter parameter) - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "exit method as Gradecenter option is OFF"); - return; - } // get user list, but no role info since there are no course info - UserDbLoader userLoader = (UserDbLoader) bbPm.getLoader(UserDbLoader.TYPE); User user = userLoader.loadByUserName(userName); - if (user == null) { throw new ServletException("User not found with userName:" + userName); } Id userId = user.getId(); - //do not remove the following line: it's required to instantiate the object - logger.info(bbContent.getTitle()); String serviceURL = LamsSecurityUtil.getServerAddress() + "/services/xml/LessonManager?" + LamsSecurityUtil.generateAuthenticateParameters(userName) + "&courseId=" @@ -192,7 +144,6 @@ DocumentBuilder db = dbf.newDocumentBuilder(); Document document = db.parse(is); - NodeList activities = document.getDocumentElement().getFirstChild().getChildNodes(); float maxResult = 0; @@ -218,16 +169,15 @@ } - Lineitem lineitem = LineitemUtil.getLineitem(bbContentId, userId, lamsLessonIdParam); + Lineitem lineitem = LineitemUtil.getLineitem(userId, lamsLessonIdParam); + if (lineitem == null) { + throw new ServletException("Lineitem was not found for userId:" + userId + " and lamsLessonId:" + lamsLessonIdParam); + } //do not remove the following line: it's required to instantiate the object logger.info("Record score for " +lineitem.getName() + " lesson. It now has "+ lineitem.getScores().size() + " scores."); // store new score - CourseMembershipDbLoader memLoader = (CourseMembershipDbLoader) bbPm - .getLoader(CourseMembershipDbLoader.TYPE); - ScoreDbLoader scoreLoader = (ScoreDbLoader) bbPm.getLoader(ScoreDbLoader.TYPE); - ScoreDbPersister scorePersister = (ScoreDbPersister) bbPm.getPersister(ScoreDbPersister.TYPE); - CourseMembership courseMembership = memLoader.loadByCourseAndUserId(lineitem.getCourseId(), userId); + CourseMembership courseMembership = courseMembershipLoader.loadByCourseAndUserId(lineitem.getCourseId(), userId); Score currentScore = null; try { currentScore = scoreLoader.loadByCourseMembershipIdAndLineitemId(courseMembership.getId(), lineitem.getId()); Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GradebookSyncServlet.java =================================================================== diff -u -r7f3b6d94fcbb46ec5cf01163a0217a54ed00051b -r67bf7cc2eed9d841593d383cf139d63a65e1a8a6 --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GradebookSyncServlet.java (.../GradebookSyncServlet.java) (revision 7f3b6d94fcbb46ec5cf01163a0217a54ed00051b) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GradebookSyncServlet.java (.../GradebookSyncServlet.java) (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -33,7 +33,6 @@ import java.net.URL; import java.net.URLConnection; import java.text.DecimalFormat; -import java.util.Set; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -45,35 +44,25 @@ import org.apache.log4j.Logger; import org.lamsfoundation.ld.integration.Constants; -import org.lamsfoundation.ld.util.LineitemUtil; +import org.lamsfoundation.ld.integration.util.LamsBuildingBlockException; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; +import org.lamsfoundation.ld.integration.util.LineitemUtil; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import blackboard.base.BbList; -import blackboard.data.content.Content; -import blackboard.data.content.CourseDocument; -import blackboard.data.course.Course; import blackboard.data.course.CourseMembership; import blackboard.data.gradebook.Lineitem; import blackboard.data.gradebook.Score; -import blackboard.persist.BbPersistenceManager; -import blackboard.persist.Container; import blackboard.persist.Id; -import blackboard.persist.PkId; -import blackboard.persist.content.ContentDbLoader; -import blackboard.persist.course.CourseDbLoader; import blackboard.persist.course.CourseMembershipDbLoader; -import blackboard.persist.gradebook.LineitemDbLoader; import blackboard.persist.gradebook.ScoreDbLoader; import blackboard.persist.gradebook.ScoreDbPersister; import blackboard.platform.BbServiceManager; import blackboard.platform.context.Context; import blackboard.platform.context.ContextManager; -import blackboard.portal.data.ExtraInfo; -import blackboard.portal.data.PortalExtraInfo; -import blackboard.portal.servlet.PortalUtil; import blackboard.util.StringUtil; /** @@ -97,7 +86,9 @@ // get Blackboard context ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); Context ctx = ctxMgr.setContext(request); - BbPersistenceManager bbPm = BbServiceManager.getPersistenceService().getDbPersistenceManager(); + CourseMembershipDbLoader courseMemLoader = CourseMembershipDbLoader.Default.getInstance(); + ScoreDbLoader scoreLoader = ScoreDbLoader.Default.getInstance(); + ScoreDbPersister scorePersister = ScoreDbPersister.Default.getInstance(); // get Parameter values String lamsLessonIdParam = request.getParameter(Constants.PARAM_LESSON_ID); @@ -106,65 +97,11 @@ throw new RuntimeException("Requred parameters missing. lsid=" + lamsLessonIdParam); } - // check if isGradebookcenter - PortalExtraInfo portalExtraInfo = PortalUtil.loadPortalExtraInfo(null, null, "LamsStorage"); - ExtraInfo extraInfo = portalExtraInfo.getExtraInfo(); - Set bbContentIds = extraInfo.getKeys(); - String bbContentId = null; - for (String bbContentIdIter : bbContentIds) { - String lamsLessonId = extraInfo.getValue(bbContentIdIter); - if (lamsLessonIdParam.equals(lamsLessonId)) { - bbContentId = bbContentIdIter; - break; - } + Lineitem lineitem = LineitemUtil.getLineitem(ctx.getUserId(), lamsLessonIdParam); + if (lineitem == null) { + throw new ServletException("Lineitem was not found for userId:" + ctx.getUserId() + " and lamsLessonId:" + lamsLessonIdParam); } - - // check whether the lesson was created by Chen Rui's BB and has gradebook feature on - Lineitem lineitem = null; - if (bbContentId == null) { - CourseDbLoader cLoader = CourseDbLoader.Default.getInstance(); - LineitemDbLoader liLoader = LineitemDbLoader.Default.getInstance(); - - BbList coursesBBList = cLoader.loadByUserId(ctx.getUserId()); - Course[] courses = (Course[]) coursesBBList.toArray(new Course[0]); - boolean isChenRuiGradebookOn = false; - for (int i = 0; i < courses.length; i++) { - BbList lineitemsBBList = liLoader.loadByCourseId(courses[i].getId()); - Lineitem[] lineitems = (Lineitem[]) lineitemsBBList.toArray(new Lineitem[0]); - for (int j = 0; j < lineitems.length; j++) { - if (lineitems[j].getAssessmentId() != null - && lineitems[j].getAssessmentId().equals(lamsLessonIdParam)) { - lineitem = lineitems[j]; - isChenRuiGradebookOn = true; - break; - } - } - } - - // was created in version prior to 1.2.1 OR possibly Chen Rui's BB gradecenter option is OFF - if (!isChenRuiGradebookOn) { - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - response.getWriter() - .write("Can't syncronize lesson as it was created in version prior to 1.2.1 and thus don't have lineitem."); - return; - } - - // check isGradecenter option is ON - } else { - Container bbContainer = bbPm.getContainer(); - Id contentId = new PkId(bbContainer, CourseDocument.DATA_TYPE, bbContentId); - ContentDbLoader contentDbLoader = (ContentDbLoader) bbPm.getLoader(ContentDbLoader.TYPE); - Content bbContent = (Content) contentDbLoader.loadById(contentId); - // check isGradecenter option is ON - if (!bbContent.getIsDescribed()) {// (isDescribed field is used for storing isGradecenter parameter) - response.setStatus(HttpServletResponse.SC_ACCEPTED); - response.getWriter().write("Can't syncronize lesson as it's gradecenter option is OFF."); - return; - } - lineitem = LineitemUtil.getLineitem(bbContentId, ctx.getUserId(), lamsLessonIdParam); - } - String username = ctx.getUser().getUserName(); String serviceURL = LamsSecurityUtil.getServerAddress() + "/services/xml/LessonManager?" + LamsSecurityUtil.generateAuthenticateParameters(username) @@ -181,18 +118,11 @@ if (httpConn.getResponseCode() != HttpURLConnection.HTTP_OK) { String errorMsg = "HTTP Response Code: " + httpConn.getResponseCode() + ", HTTP Response Message: " + httpConn.getResponseMessage(); -// response.sendError(HttpServletResponse.SC_UNAUTHORIZED, errorMsg); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write(errorMsg); return; } - - CourseMembershipDbLoader courseMemLoader = (CourseMembershipDbLoader) bbPm - .getLoader(CourseMembershipDbLoader.TYPE); - ScoreDbLoader scoreLoader = (ScoreDbLoader) bbPm.getLoader(ScoreDbLoader.TYPE); - ScoreDbPersister scorePersister = (ScoreDbPersister) bbPm.getPersister(ScoreDbPersister.TYPE); - // InputStream is = url.openConnection().getInputStream(); InputStream is = conn.getInputStream(); // parse xml response @@ -261,6 +191,13 @@ } } + + } catch (LamsBuildingBlockException e) { + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.write("Exception was thrown: " + e.getMessage()); + return; + } catch (MalformedURLException e) { throw new ServletException("Unable to get LAMS learning designs, bad URL: " + ", please check lams.properties", e); Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GroupDataServlet.java =================================================================== diff -u -r7f3b6d94fcbb46ec5cf01163a0217a54ed00051b -r67bf7cc2eed9d841593d383cf139d63a65e1a8a6 --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GroupDataServlet.java (.../GroupDataServlet.java) (revision 7f3b6d94fcbb46ec5cf01163a0217a54ed00051b) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GroupDataServlet.java (.../GroupDataServlet.java) (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -33,6 +33,8 @@ import org.apache.log4j.Logger; import org.lamsfoundation.ld.integration.Constants; +import org.lamsfoundation.ld.integration.util.LamsPluginUtil; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; import com.google.gson.JsonArray; import com.google.gson.JsonObject; @@ -48,6 +50,7 @@ import blackboard.persist.course.GroupDbLoader; import blackboard.persist.user.UserDbLoader; import blackboard.platform.BbServiceManager; +import blackboard.platform.persistence.PersistenceServiceFactory; /** * Fetch groups of the specified course. Serves 2 different types of calls: 1-initial request for group names; @@ -88,7 +91,7 @@ } // get the persistence manager - BbPersistenceManager bbPm = BbServiceManager.getPersistenceService().getDbPersistenceManager(); + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); CourseDbLoader cLoader = CourseDbLoader.Default.getInstance(); GroupDbLoader groupLoader = (GroupDbLoader) bbPm.getLoader(GroupDbLoader.TYPE); UserDbLoader userDbLoader = (UserDbLoader) bbPm.getLoader(UserDbLoader.TYPE); Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/ImportLessonsServlet.java =================================================================== diff -u -r91e279a802a26c1c977e9c843b2937684fcdda2f -r67bf7cc2eed9d841593d383cf139d63a65e1a8a6 --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/ImportLessonsServlet.java (.../ImportLessonsServlet.java) (revision 91e279a802a26c1c977e9c843b2937684fcdda2f) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/ImportLessonsServlet.java (.../ImportLessonsServlet.java) (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -33,7 +33,9 @@ import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; -import org.lamsfoundation.ld.util.LamsServerException; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; +import org.lamsfoundation.ld.integration.util.LamsServerException; +import org.lamsfoundation.ld.integration.util.LineitemUtil; import blackboard.base.BbList; import blackboard.base.FormattedText; @@ -170,15 +172,9 @@ // persist updated content persister.persist(content); - // store internalContentId -> externalContentId. It's used for lineitem removal (delete.jsp) - PortalExtraInfo pei = PortalUtil.loadPortalExtraInfo(null, null, "LamsStorage"); - ExtraInfo ei = pei.getExtraInfo(); - ei.setValue(_content_id, Long.toString(newLessonId)); - PortalUtil.savePortalExtraInfo(pei); + //update lineitem details + LineitemUtil.updateLineitemLessonId(content, _course_id, newLessonId, ctx, teacher.getUserName()); - // Gradebook column will be copied automatically if appropriate option is selected on - // importing lesson page - logger.debug("Lesson (lessonId=" + urlLessonId + ") was successfully imported to the one (lessonId=" + newLessonId + ")."); Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LamsLearningDesignDeleteServlet.java =================================================================== diff -u -r7f3b6d94fcbb46ec5cf01163a0217a54ed00051b -r67bf7cc2eed9d841593d383cf139d63a65e1a8a6 --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LamsLearningDesignDeleteServlet.java (.../LamsLearningDesignDeleteServlet.java) (revision 7f3b6d94fcbb46ec5cf01163a0217a54ed00051b) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LamsLearningDesignDeleteServlet.java (.../LamsLearningDesignDeleteServlet.java) (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -30,6 +30,7 @@ import javax.servlet.http.HttpServletResponse; import org.lamsfoundation.ld.integration.Constants; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; import blackboard.base.InitializationException; import blackboard.platform.BbServiceException; Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LamsLearningDesignServlet.java =================================================================== diff -u -r7f3b6d94fcbb46ec5cf01163a0217a54ed00051b -r67bf7cc2eed9d841593d383cf139d63a65e1a8a6 --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LamsLearningDesignServlet.java (.../LamsLearningDesignServlet.java) (revision 7f3b6d94fcbb46ec5cf01163a0217a54ed00051b) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LamsLearningDesignServlet.java (.../LamsLearningDesignServlet.java) (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -30,6 +30,7 @@ import javax.servlet.http.HttpServletResponse; import org.lamsfoundation.ld.integration.Constants; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; import blackboard.base.InitializationException; import blackboard.platform.BbServiceException; Fisheye: Tag 67bf7cc2eed9d841593d383cf139d63a65e1a8a6 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LamsPluginUtil.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 67bf7cc2eed9d841593d383cf139d63a65e1a8a6 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LamsSecurityUtil.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/RenderDesignImageServlet.java =================================================================== diff -u -r7f3b6d94fcbb46ec5cf01163a0217a54ed00051b -r67bf7cc2eed9d841593d383cf139d63a65e1a8a6 --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/RenderDesignImageServlet.java (.../RenderDesignImageServlet.java) (revision 7f3b6d94fcbb46ec5cf01163a0217a54ed00051b) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/RenderDesignImageServlet.java (.../RenderDesignImageServlet.java) (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -30,6 +30,7 @@ import javax.servlet.http.HttpServletResponse; import org.lamsfoundation.ld.integration.Constants; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; import blackboard.base.InitializationException; import blackboard.platform.BbServiceException; @@ -79,7 +80,8 @@ ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); ctx = ctxMgr.setContext(request); - String learningDesignImageUrl = LamsSecurityUtil.generateRequestLearningDesignImage(ctx, false) + "&ldId=" + learningDesignId; + String username = ctx.getUser().getUserName(); + String learningDesignImageUrl = LamsSecurityUtil.generateRequestLearningDesignImage(username, false) + "&ldId=" + learningDesignId; response.sendRedirect(learningDesignImageUrl); } catch (InitializationException e) { Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/StartLessonServlet.java =================================================================== diff -u -r94e9c338bd889d88fb004b11cca3cae3de7024c8 -r67bf7cc2eed9d841593d383cf139d63a65e1a8a6 --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/StartLessonServlet.java (.../StartLessonServlet.java) (revision 94e9c338bd889d88fb004b11cca3cae3de7024c8) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/StartLessonServlet.java (.../StartLessonServlet.java) (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -30,7 +30,9 @@ import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; -import org.lamsfoundation.ld.util.LineitemUtil; +import org.lamsfoundation.ld.integration.util.LamsPluginUtil; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; +import org.lamsfoundation.ld.integration.util.LineitemUtil; import blackboard.base.FormattedText; import blackboard.data.content.Content; @@ -43,6 +45,7 @@ import blackboard.platform.BbServiceManager; import blackboard.platform.context.Context; import blackboard.platform.context.ContextManager; +import blackboard.platform.persistence.PersistenceServiceFactory; import blackboard.platform.plugin.PlugInException; import blackboard.platform.plugin.PlugInUtil; import blackboard.portal.data.ExtraInfo; @@ -52,7 +55,9 @@ /** * Starts a lesson, returning the BB Content Id in JSON. Based on start_lesson_proc but uses the username * parameter as a basis for identifying the user. - * Return a server error rather than throw an exception as this will be consumed by AJAX call or the like. + * Return a server error rather than throw an exception as this will be consumed by AJAX call or the like. + * + * TODO create a common methods util class with start_lesson_proc.jsp */ public class StartLessonServlet extends HttpServlet { @@ -80,7 +85,7 @@ CourseDocument bbContent = new blackboard.data.content.CourseDocument(); // Retrieve the Db persistence manager from the persistence service - BbPersistenceManager bbPm = BbServiceManager.getPersistenceService().getDbPersistenceManager(); + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); String courseIdStr = request.getParameter("course_id"); String contentIdStr = request.getParameter("content_id"); @@ -187,15 +192,16 @@ bbContent.setUrl(contentUrl); persister.persist(bbContent); - // store internalContentId -> externalContentId. It's used for lineitem removal (delete.jsp) + // store internalContentId -> externalContentId. It's used for GradebookServlet PortalExtraInfo pei = PortalUtil.loadPortalExtraInfo(null, null, "LamsStorage"); ExtraInfo ei = pei.getExtraInfo(); ei.setValue(bbContentId, lamsLessonId); PortalUtil.savePortalExtraInfo(pei); // Create new Gradebook column for current lesson if (isGradecenter) { - LineitemUtil.createLineitem(ctx, bbContent); + String userName = ctx.getUser().getUserName(); + LineitemUtil.createLineitem(bbContent, ctx, userName); } String strReturnUrl = PlugInUtil.getEditableContentReturnURL(bbContent.getParentId(), courseId); Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/UpdateServerUrlServlet.java =================================================================== diff -u -r8e41f1c9b3682b7dbe5f08ed2f9b361aff2d1f5b -r67bf7cc2eed9d841593d383cf139d63a65e1a8a6 --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/UpdateServerUrlServlet.java (.../UpdateServerUrlServlet.java) (revision 8e41f1c9b3682b7dbe5f08ed2f9b361aff2d1f5b) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/UpdateServerUrlServlet.java (.../UpdateServerUrlServlet.java) (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -46,6 +46,7 @@ import blackboard.persist.navigation.CourseTocDbLoader; import blackboard.platform.BbServiceManager; import blackboard.platform.context.ContextManager; +import blackboard.platform.persistence.PersistenceServiceFactory; import blackboard.platform.plugin.PlugInUtil; /** @@ -80,7 +81,7 @@ // get Blackboard context ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); - BbPersistenceManager bbPm = BbServiceManager.getPersistenceService().getDbPersistenceManager(); + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); Container bbContainer = bbPm.getContainer(); ContentDbLoader contentDbLoader = ContentDbLoader.Default.getInstance(); Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/UserDataServlet.java =================================================================== diff -u -r1568156ccc545849b14864558e2c3c360a3364d2 -r67bf7cc2eed9d841593d383cf139d63a65e1a8a6 --- lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/UserDataServlet.java (.../UserDataServlet.java) (revision 1568156ccc545849b14864558e2c3c360a3364d2) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/UserDataServlet.java (.../UserDataServlet.java) (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -29,15 +29,18 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Hex; -import org.lamsfoundation.ld.util.CSVUtil; import org.lamsfoundation.ld.integration.Constants; import blackboard.persist.BbPersistenceManager; import blackboard.persist.user.UserDbLoader; import blackboard.platform.BbServiceManager; import blackboard.data.user.User; import blackboard.platform.context.ContextManager; +import blackboard.platform.persistence.PersistenceServiceFactory; + import org.apache.log4j.Logger; -import org.lamsfoundation.ld.integration.blackboard.LamsSecurityUtil; +import org.lamsfoundation.ld.integration.util.CSVUtil; +import org.lamsfoundation.ld.integration.util.LamsPluginUtil; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; /** * @author Anthony Xiao @@ -91,7 +94,7 @@ } // get the persistence manager - BbPersistenceManager bbPm = BbServiceManager.getPersistenceService().getDbPersistenceManager(); + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); // get user list, but no role info since there are no course info UserDbLoader userLoader = (UserDbLoader) bbPm.getLoader(UserDbLoader.TYPE); Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/util/CSVUtil.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/util/CSVUtil.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/util/CSVUtil.java (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -0,0 +1,171 @@ +/**************************************************************** + * Copyright (C) 2007 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 + * **************************************************************** + */ +package org.lamsfoundation.ld.integration.util; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.regex.*; + +/** + * CSVUtil Provides "Comma Seperated Value" writing and parsing. + * The two methods write() and parse() will perform writing to and parse from + * the CSV format + * + * @author Luke Foxton + */ +public class CSVUtil { + + private static final char QUOTE = '"'; + private static final char COMMA = ','; + + /* precompile the patterns to speed up the search */ + + // should we put quotes around a value? + private static final Pattern CONTAINS_NEWLINE = Pattern.compile(".*(\\n|\\r)+.*"); + + // should we put quotes around a value? + private static final Pattern CONTAINS_COMMA = Pattern.compile(".*(,)+.*"); + + // should we escape the quotes? + private static final Pattern CONTAINS_QUOTE = Pattern.compile("\""); + + // how should we wrap qoutes around comma or newline? + private static final String WRAP_QOUTE = "\"$0\""; + + // how should we escape the value if it has qoutes? + private static final String ESCAPE_QUOTE = "\"\""; + + // has this value been wrapped with quotes + private static final Pattern WRAPPED_QUOTE = Pattern.compile("^\"(.*((,|\\n|\\r)+).*)\"$"); + + // has this value been escaped by ESCAPE_QUOTE + private static final Pattern ESCAPED_QUOTE = Pattern.compile("\"\""); + + // how should we unescape the the ESCAPED_QUOTE? + private static final String UNWRAP_QOUTE = "$1"; + + // how should we unescape the the ESCAPED_COMMA? + private static final String UNESCAPE_QUOTE = "\""; + + /* + * NOTE: why are we using \\n|\\r in CONTAINS_NEWLINE and WRAPPED_QUOTE? + * javadoc says "." represents "Any character (may or may not match line terminators)" + * and we want to make sure terminiators such as newline (\n) gets matched as well + * if we dont match it then ,\n, will get written as ","\n"," + */ + + /** + * Writes a array of String into CSV format + * @param vals - The array of string to be written into CSV format + * @return + */ + public static String write(String[] vals){ + String str = ""; + int lastIndex = vals.length - 1; + for(int i=0; iLuke Foxton + */ +public class LamsPluginUtil { + + public static final String VENDOR_ID = "lams"; + public static final String PLUGIN_HANDLE = "lamscontent"; + public static final String CONTENT_HANDLE = "resource/x-lams-lamscontent"; + public static final String FILE_PROPERTIES = "lams.properties"; + + public static final String PROP_LAMS_SECRET_KEY = "LAMS_SERVER_SKEY"; + public static final String PROP_LAMS_SERVER_ID = "LAMS_SERVER_ID"; + public static final String PROP_LAMS_URL = "LAMS_SERVER_URL"; + public static final String PROP_REQ_SRC = "BB_REQ_SRC"; + public static final String PROP_LAMS_SERVER_TIME_REFRESH_INTERVAL = "LAMS_SERVER_TIME_REFRESH_INTERVAL"; + public static final String PROP_ALT_LAMS_URL = "LAMS_ALT_SERVER_URL"; + + private static Properties lamsProperties = null; + + /** + * Returns the properties file that contains the server name, key and connection URL + * + * @return The LAMS Properties file + * @throws PlugInException + * @throws FileNotFoundException + * @throws IOException + */ + public static Properties getProperties() { + if (lamsProperties != null) + return lamsProperties; + + // load LAMS Configuration File + try { + File configFile = new File(PlugInUtil.getConfigDirectory(VENDOR_ID, PLUGIN_HANDLE).getPath() + + File.separator + FILE_PROPERTIES); + Properties p = new Properties(); + + if (configFile.exists()) + p.load(new FileInputStream(configFile)); + else { + p.setProperty(PROP_LAMS_URL, ""); + p.setProperty(PROP_LAMS_SECRET_KEY, ""); + p.setProperty(PROP_LAMS_SERVER_ID, ""); + p.setProperty(PROP_REQ_SRC, ""); + p.setProperty(PROP_LAMS_SERVER_TIME_REFRESH_INTERVAL, "24"); + } + + lamsProperties = p; + return p; + } catch (PlugInException e) { + throw new RuntimeException(e); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Save a Properties file as the LAMS properties file + * + * @param p + * @throws PlugInException + * @throws FileNotFoundException + * @throws IOException + */ + public static void setProperties(Properties p) { + try { + lamsProperties = p; + + FileOutputStream configFile = new FileOutputStream(PlugInUtil.getConfigDirectory(VENDOR_ID, PLUGIN_HANDLE) + .getPath() + File.separator + FILE_PROPERTIES); + p.store(configFile, "LAMS configuration"); + configFile.close(); + } catch (PlugInException e) { + throw new RuntimeException(e); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * + * @return the secret key from lams.properties + */ + public static String getSecretKey() { + return getProperties().getProperty(PROP_LAMS_SECRET_KEY); + } + + /** + * + * @return the secret url from lams.properties + */ + public static String getServerUrl() { + return getProperties().getProperty(PROP_LAMS_URL); + } + + /** + * + * @return the server id from lams.properties + */ + public static String getServerId() { + return getProperties().getProperty(PROP_LAMS_SERVER_ID); + } + + /** + * + * @return the request source from lams.properties + */ + public static String getReqSrc() { + return getProperties().getProperty(PROP_REQ_SRC); + } + + /** + * + * @return the LAMS server time refresh interval from lams.properties + */ + public static String getLamsServerTimeRefreshInterval() { + return getProperties().getProperty(PROP_LAMS_SERVER_TIME_REFRESH_INTERVAL); + } + +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/util/LamsSecurityUtil.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/util/LamsSecurityUtil.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/util/LamsSecurityUtil.java (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -0,0 +1,1220 @@ +/**************************************************************** + * Copyright (C) 2007 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 + * **************************************************************** + */ +package org.lamsfoundation.ld.integration.util; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.ConnectException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.rmi.RemoteException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Date; +import java.util.Properties; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.io.IOUtils; +import org.apache.log4j.Logger; +import org.lamsfoundation.ld.integration.dto.LearnerProgressDTO; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.xml.sax.SAXException; + +import blackboard.base.BbList; +import blackboard.data.course.CourseMembership; +import blackboard.data.user.User; +import blackboard.persist.BbPersistenceManager; +import blackboard.persist.Id; +import blackboard.persist.KeyNotFoundException; +import blackboard.persist.PersistenceException; +import blackboard.persist.course.CourseMembershipDbLoader; +import blackboard.persist.user.UserDbLoader; +import blackboard.platform.BbServiceManager; +import blackboard.platform.context.Context; +import blackboard.platform.cx.component.CopyControl; +import blackboard.platform.persistence.PersistenceServiceFactory; +import blackboard.portal.data.ExtraInfo; +import blackboard.portal.data.PortalExtraInfo; +import blackboard.portal.servlet.PortalUtil; + +/** + * This class creates URLs, servlet calls and webservice calls for communication with LAMS + * + * @author Luke Foxton + */ +public class LamsSecurityUtil { + + private static Logger logger = Logger.getLogger(LamsSecurityUtil.class); + private static final String DUMMY_COURSE = "Previews"; + private static final String EXPORT_FOLDER_LAMS_SERVER = "/tmp/lams/"; + + /** + * Generates login requests to LAMS for author, monitor and learner, using the alternative URL. + * + * @param ctx + * the blackboard contect, contains session data + * @param method + * the mehtod to request of LAMS "author", "monitor", "learnerStrictAuth" + * @param lsid + * lesson id. It is expected to be present in case of "monitor" and "learnerStrictAuth" + * @return a url pointing to the LAMS lesson, monitor, author session + * @throws IOException + * @throws PersistenceException + * @throws Exception + */ + public static String generateRequestAltURL(Context ctx, String method, String lsid) throws PersistenceException, IOException { + return generateRequestURLForServer(ctx, method, lsid, getAltServerAddress()); + } + + /** + * Generates login requests to LAMS for author, monitor and learner + * + * @param ctx + * the blackboard contect, contains session data + * @param method + * the mehtod to request of LAMS "author", "monitor", "learnerStrictAuth" + * @param lsid + * lesson id. It is expected to be present in case of "monitor" and "learnerStrictAuth" + * @return a url pointing to the LAMS lesson, monitor, author session + * @throws IOException + * @throws PersistenceException + * @throws Exception + */ + public static String generateRequestURL(Context ctx, String method, String lsid) throws PersistenceException, IOException { + return generateRequestURLForServer(ctx, method, lsid, getServerAddress()); + } + + private static String generateRequestURLForServer(Context ctx, String method, String lsid, String serverAddr) throws PersistenceException, IOException { + String serverId = getServerID(); + String reqSrc = getReqSrc(); + + // If lams.properties could not be read, throw exception + if (serverAddr == null || serverId == null || reqSrc == null) { + throw new RuntimeException("Configuration Exception " + serverAddr + ", " + serverId); + } + + String timestamp = getServerTime(); + String username = ctx.getUser().getUserName(); + String firstName = ctx.getUser().getGivenName(); + String lastName = ctx.getUser().getFamilyName(); + String email = ctx.getUser().getEmailAddress(); + String locale = ctx.getUser().getLocale(); + String country = getCountry(locale); + String lang = getLanguage(locale); + + // Even for authoring calls we still need a 'course' the user, role & organisation are all bound up together + // do to be authorised to use authoring you must be in an organisation. + String courseId = setupCourseId(ctx, null, true); + + String secretkey = LamsPluginUtil.getSecretKey(); + + // in case of learnerStrictAuth we should also include lsid value when creating hash: [ts + uid + method + lsid + // + serverID + serverKey] + // regular case: [ts + uid + method + serverID + serverKey] + String plaintext = "learnerStrictAuth".equals(method) ? timestamp.toLowerCase().trim() + + username.toLowerCase().trim() + method.toLowerCase().trim() + lsid.toLowerCase().trim() + + serverId.toLowerCase().trim() + secretkey.toLowerCase().trim() : timestamp.toLowerCase().trim() + + username.toLowerCase().trim() + method.toLowerCase().trim() + serverId.toLowerCase().trim() + + secretkey.toLowerCase().trim(); + // generate authentication hash code to validate parameters + String hash = sha1(plaintext); + + String url; + try { + String course = courseId != null ? "&courseid=" + URLEncoder.encode(courseId, "UTF8") : ""; + url = serverAddr + "/LoginRequest?" + "&uid=" + URLEncoder.encode(username, "UTF8") + "&method=" + method + + "&ts=" + timestamp + "&sid=" + serverId + "&hash=" + hash + course + + "&country=" + country + "&lang=" + lang + "&requestSrc=" + + URLEncoder.encode(reqSrc, "UTF8") + "&firstName=" + URLEncoder.encode(firstName, "UTF-8") + + "&lastName=" + URLEncoder.encode(lastName, "UTF-8") + + "&email=" + URLEncoder.encode(email, "UTF-8"); + + if ("learnerStrictAuth".equals(method) || "monitor".equals(method)) { + url += "&lsid=" + lsid; + } + + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + + logger.info("LAMS Req: " + url); + // System.out.println(url); + + return url; + } + + /** + * Generates default + * @throws UnsupportedEncodingException + */ + public static String generateAuthenticateParameters(String username) throws UnsupportedEncodingException { + String serverAddr = getServerAddress(); + String serverId = getServerID(); + String reqSrc = getReqSrc(); + + // If lams.properties could not be read, throw exception + if (serverAddr == null || serverId == null || reqSrc == null) { + throw new RuntimeException("Configuration Exception " + serverAddr + ", " + serverId); + } + + String timestamp = new Long(System.currentTimeMillis()).toString(); + String hash = generateAuthenticationHash(timestamp, username, serverId); + + String authenticateParameters = "&serverId=" + serverId + "&datetime=" + timestamp + "&hashValue=" + hash + + "&username=" + URLEncoder.encode(username, "UTF8"); + + return authenticateParameters; + } + + /** + * Generates url request to LAMS for LearningDesignImage. + * + * @param ctx + * the blackboard contect, contains session data + * @return a url pointing to the LAMS lesson, monitor, author session + * @throws UnsupportedEncodingException + */ + public static String generateRequestLearningDesignImage(String username, boolean isSvgImage) throws UnsupportedEncodingException { + String serverAddr = getServerAddress(); + int svgFormat = (isSvgImage) ? 1 : 2; + + //$request = "$CFG->lamslesson_serverurl/services/LearningDesignSVG?serverId=" . $CFG->lamslesson_serverid . "&datetime=" . $datetime_encoded . "&hashValue=" . + //$hashvalue . "&username=" . $username . "&courseId=" . $courseid . "&courseName=" . urlencode($coursename) . "&mode=2&country=" . $country . "&lang=" . $lang . + //"&ldId=" . $ldid . "&svgFormat=" . $format; + String url = serverAddr + "/services/LearningDesignSVG?" + generateAuthenticateParameters(username) + + "&svgFormat=" + svgFormat; + + logger.info("LAMS Req: " + url); + + return url; + } + + /** + * Gets a list of learning designs & workspace folders for the current user from LAMS. + * + * @param ctx + * the blackboard context, contains session data + * @param courseId + * blackboard courseid. We pass it as a parameter as ctx.getCourse().getCourseId() is null when called + * from LamsLearningDesignServlet. + * @param folderId folderId. It can be null and then LAMS returns default workspace folders. + * + * @return a string containing the LAMS workspace tree in tigra format + */ + public static String getLearningDesigns(Context ctx, String courseId, String folderId) { + return getLearningDesigns(ctx, null, courseId, folderId,"getLearningDesignsJSON",null,null,null,null,null,null); + } + + /** + * Gets a list of learning designs & workspace folders for the current user from LAMS or the user "usernameFromParam" + * + * @param ctx the blackboard context, contains session data + * @param usernameFromParam only used if there isn't a user in the context, due to how the servlet is called + * @param courseId blackboard course id. We pass it as a parameter as ctx.getCourse().getCourseId() is null when called + * from LamsLearningDesignServlet. + * @param folderId folderID in LAMS. It can be null and then LAMS returns default workspace folders. + * @param method which method to call on the LAMS end + * @param type used onlu for method = getLearningDesignsJSON, restricts by type + * @param page used only for method = getPagedHomeLearningDesignsJSON + * @param size used only for method = getPagedHomeLearningDesignsJSON + * @return a string containing the LAMS workspace tree in tigra format (method = getLearningDesignsJSON) or + * a string containing the learning designs in JSON (method = getPagedHomeLearningDesignsJSON) + */ + public static String getLearningDesigns(Context ctx, String usernameFromParam, String urlCourseId, String folderId, String method, String type, + String search, String page, String size, String sortName, String sortDate) { + + String serverAddr = getServerAddress(); + + String courseId = setupCourseId(ctx, urlCourseId, true); + String serverId = getServerID(); + + // If lams.properties could not be read, throw exception + if (serverAddr == null || serverId == null) { + throw new RuntimeException("lams.properties file could not be read. serverAddr:" + serverAddr + ", serverId:" + serverId); + } + + String timestamp = new Long(System.currentTimeMillis()).toString(); + + User user = ctx.getUser(); + if ( user == null ) + user = loadUserFromDB(ctx, usernameFromParam); + + String username = user.getUserName(); + String firstName = user.getGivenName(); + String lastName = user.getFamilyName(); + String email = user.getEmailAddress(); + String hash = generateAuthenticationHash(timestamp, username, serverId); + + String locale = user.getLocale(); + String country = getCountry(locale); + String lang = getLanguage(locale); + + // the mode to call upon learning designs + final Integer MODE = 2; + + // TODO: Make locale settings work + String learningDesigns = ""; // empty + try { + + + String serviceURL = serverAddr + + "/services/xml/LearningDesignRepository?method="+method+"&datetime=" + + timestamp + "&username=" + URLEncoder.encode(username, "utf8") + "&serverId=" + + URLEncoder.encode(serverId, "utf8") + "&hashValue=" + hash + "&courseId=" + + URLEncoder.encode(courseId, "UTF8") + "&country=" + country + "&lang=" + lang + "&mode=" + MODE + + "&firstName=" + URLEncoder.encode(firstName, "UTF-8") + "&lastName=" + + URLEncoder.encode(lastName, "UTF-8") + "&email=" + URLEncoder.encode(email, "UTF-8"); + + if (folderId != null ) { + serviceURL += "&folderID=" + ( folderId.equalsIgnoreCase("home") ? "-1" : folderId); + } + + // The following parameter is only used for getLearningDesignsJSON + if ( type != null && type.length() > 0 ) { + serviceURL += "&type=" +type; + } + + // The following parameters are only used for getPagedLearningDesignsJSON + if (page != null ) { + serviceURL += "&page=" + page; + } + if (size != null ) { + serviceURL += "&size=" + size; + } + // sort by name, ascending = 1, descending = 0 + if (sortName != null ) { + serviceURL += "&sortName=" + sortName; + } + // sort by date, ascending = 1, descending = 0 + if (sortDate != null ) { + serviceURL += "&sortDate=" + sortDate; + } + // get all the designs that contain this string + if (search != null ) { + serviceURL += "&search=" + search; + } + + InputStream is = LamsSecurityUtil.callLamsServerPost(serviceURL); + + // Read/convert response to a String + StringWriter writer = new StringWriter(); + IOUtils.copy(is, writer, "UTF-8"); + learningDesigns = writer.toString(); + + } catch (MalformedURLException e) { + throw new RuntimeException("Unable to get LAMS learning designs, bad URL: '" + serverAddr + + "', please check lams.properties", e); + } catch (IllegalStateException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (ConnectException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return learningDesigns; + } + + private static User loadUserFromDB(Context ctx, String username) { + User user = null; + try { + final UserDbLoader userDbLoader = UserDbLoader.Default.getInstance(); + user = userDbLoader.loadByUserName(username); + } catch (KeyNotFoundException e) { + throw new RuntimeException("No user details found in context or via username parameter. Unable access LAMS. "+e.getMessage()+" Username "+username+" Ctx "+ctx,e); + } catch (PersistenceException e) { + throw new RuntimeException("No user details found in context or via username parameter. Unable access LAMS. "+e.getMessage()+" Username "+username+" Ctx "+ctx,e); + } + if ( user == null ) { + throw new RuntimeException("No user details found in context or via username parameter. Unable access LAMS. Username "+username+" Ctx "+ctx); + } + return user; + } + + /** + * Gets a list of learning designs & workspace folders for the current user from LAMS. + * + * @param ctx the blackboard context, contains session data + * @param courseId blackboard courseid. We pass it as a parameter as ctx.getCourse().getCourseId() is null when called + * from LamsLearningDesignServlet. + * @param ldId learning design to delete + * @return JSON response from server + */ + public static String deleteLearningDesigns(Context ctx, String urlCourseId, Long ldId) { + + String courseId = setupCourseId(ctx, urlCourseId, false); + + String serverAddr = getServerAddress(); + String serverId = getServerID(); + + // If lams.properties could not be read, throw exception + if (serverAddr == null || serverId == null) { + throw new RuntimeException("lams.properties file could not be read. serverAddr:" + serverAddr + ", serverId:" + serverId); + } + + String timestamp = new Long(System.currentTimeMillis()).toString(); + String username = ctx.getUser().getUserName(); + String firstName = ctx.getUser().getGivenName(); + String lastName = ctx.getUser().getFamilyName(); + String email = ctx.getUser().getEmailAddress(); + String hash = generateAuthenticationHash(timestamp, username, serverId); + + String locale = ctx.getUser().getLocale(); + String country = getCountry(locale); + String lang = getLanguage(locale); + + try { + + String serviceURL = serverAddr + + "/services/xml/LearningDesignRepository?method=deleteLearningDesignJSON&datetime=" + + timestamp + "&username=" + URLEncoder.encode(username, "utf8") + "&serverId=" + + URLEncoder.encode(serverId, "utf8") + "&hashValue=" + hash + "&courseId=" + + URLEncoder.encode(courseId, "UTF8") + "&country=" + country + "&lang=" + lang + + "&firstName=" + URLEncoder.encode(firstName, "UTF-8") + "&lastName=" + + URLEncoder.encode(lastName, "UTF-8") + "&email=" + URLEncoder.encode(email, "UTF-8") + + "&learningDesignID="+ldId; + + InputStream is = LamsSecurityUtil.callLamsServerPost(serviceURL); + + // Read/convert response to a String + StringWriter writer = new StringWriter(); + IOUtils.copy(is, writer, "UTF-8"); + return writer.toString(); + + } catch (MalformedURLException e) { + throw new RuntimeException("Unable to get LAMS learning designs, bad URL: '" + serverAddr + + "', please check lams.properties", e); + } catch (IllegalStateException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (ConnectException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static String setupCourseId(Context ctx, String urlCourseId, boolean allowUserDummyCourse) { + // can we pull the alphanumeric course id from the context, rather than the on passed in from the URL? If neither exist, use the dummy Preview course. + String courseId = null; + if ( ctx.getCourse()!=null ) + courseId = ctx.getCourse().getCourseId(); + if ( courseId == null && urlCourseId != null && urlCourseId.length() > 0) + courseId = urlCourseId; + if ( courseId == null && allowUserDummyCourse ) + courseId = DUMMY_COURSE; + return courseId; + } + /** + * Starts lessons in lams through a LAMS webservice + * + * @param ctx + * the blackboard contect, contains session data + * @param ldId + * the learning design id for which you wish to start a lesson + * @param title + * the title of the lesson + * @param desc + * the description of the lesson + * + * @return the learning session id + */ + public static Long startLesson(Context ctx, long ldId, String title, String desc, boolean isPreview) { + return startLesson(ctx, null, null, ldId, title, desc, isPreview); + } + + /** + * Starts lessons in lams through a LAMS webservice using the username & courseId parameter, needed + * when there won't be a user / courseId in the context. + * + * @param ctx + * the blackboard contect, contains session data + * @param usernameFromParam + * current user's username as a request parameter + * @param courseIdStr + * courseId as a request parameter + * @param ldId + * the learning design id for which you wish to start a lesson + * @param title + * the title of the lesson + * @param desc + * the description of the lesson + * + * @return the learning session id + */ + public static Long startLesson(Context ctx, String usernameFromParam, String courseIdStr, long ldId, String title, String desc, boolean isPreview) { + + String serverId = getServerID(); + String serverAddr = getServerAddress(); + String serverKey = getServerKey(); + + User user = ctx.getUser(); + if ( user == null ) + user = loadUserFromDB(ctx, usernameFromParam); + + String username = user.getUserName(); + String locale = user.getLocale(); + String country = getCountry(locale); + String lang = getLanguage(locale); + String method = (isPreview) ? "preview" : "start"; + + // courseId always needed to check roles + // if it is preview, then can use the DUMMY_COURSE + String courseId = setupCourseId(ctx, courseIdStr, isPreview); + + if (courseId == null || serverId == null || serverAddr == null || serverKey == null) { + logger.info("Unable to start lesson, one or more lams configuration properties or the course id is null"); + throw new RuntimeException("Unable to start lesson, one or more lams configuration properties or the course id is null. courseId="+courseId); + } + + try { + + String timestamp = new Long(System.currentTimeMillis()).toString(); + String hash = generateAuthenticationHash(timestamp, username, serverId); + String course = courseId != null ? "&courseId=" + URLEncoder.encode(courseId, "UTF8") : ""; + + String serviceURL = serverAddr + "/services/xml/LessonManager?" + "serverId=" + + URLEncoder.encode(serverId, "utf8") + "&datetime=" + timestamp + "&username=" + + URLEncoder.encode(username, "utf8") + "&hashValue=" + hash + course + + "&ldId=" + new Long(ldId).toString() + "&country=" + + country + "&lang=" + lang + "&method=" + method + "&title=" + + URLEncoder.encode(title, "utf8").trim() + "&desc=" + URLEncoder.encode(desc, "utf8").trim() + + "&enableNotifications=true"; + + logger.info("LAMS START LESSON Req: " + serviceURL); + + // parse xml response and get the lesson id + InputStream is = LamsSecurityUtil.callLamsServerPost(serviceURL); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document document = db.parse(is); + return Long.parseLong(document.getElementsByTagName("Lesson").item(0).getAttributes() + .getNamedItem("lessonId").getNodeValue()); + + } catch (MalformedURLException e) { + throw new RuntimeException("Unable to start LAMS lesson, bad URL: '" + serverAddr + + "', please check lams.properties", e); + } catch (IllegalStateException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (RemoteException e) { + throw new RuntimeException("Unable to start LAMS lesson, RMI Remote Exception", e); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unable to start LAMS lesson, Unsupported Encoding Exception", e); + } catch (ConnectException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (IOException e) { + throw new RuntimeException("Unable to start LAMS lesson. " + e.getMessage() + + " Please contact your system administrator.", e); + } catch (Exception e) { + throw new RuntimeException("Unable to start LAMS lesson. Please contact your system administrator.", e); + } + + } + + /** + * Clones lessons in lams through a LAMS webservice using the lsID & courseId parameter. + * + * @param courseId + * courseId as a request parameter + * @param ldId + * the learning design id for which you wish to start a lesson + * + * @return lesson id of a cloned lesson + */ + public static Long cloneLesson(User teacher, String courseId, String lsId) { + + String serverId = getServerID(); + String serverAddr = getServerAddress(); + String serverKey = getServerKey(); + String username = teacher.getUserName(); + String locale = teacher.getLocale(); + String country = getCountry(locale); + String lang = getLanguage(locale); + + if (courseId == null || serverId == null || serverAddr == null || serverKey == null) { + logger.info("Unable to clone lesson, one or more lams configuration properties or the course id is null"); + throw new RuntimeException("Unable to clone lesson, one or more lams configuration properties or the course id is null. courseId="+courseId); + } + + try { + String method = "clone"; + String timestamp = new Long(System.currentTimeMillis()).toString(); + String hash = generateAuthenticationHash(timestamp, username, serverId); + String serviceURL = serverAddr + "/services/xml/LessonManager?" + "serverId=" + + URLEncoder.encode(serverId, "utf8") + "&datetime=" + timestamp + "&username=" + + URLEncoder.encode(username, "utf8") + "&hashValue=" + hash + "&courseId=" + + URLEncoder.encode(courseId, "UTF8") + "&country=" + + country + "&lang=" + lang + "&lsId=" + lsId + "&method=" + method; + + logger.info("LAMS clone lesson request: " + serviceURL); + + // parse xml response and get the lesson id + InputStream is = LamsSecurityUtil.callLamsServerPost(serviceURL); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document document = db.parse(is); + return Long.parseLong(document.getElementsByTagName("Lesson").item(0).getAttributes() + .getNamedItem("lessonId").getNodeValue()); + + } catch (MalformedURLException e) { + throw new RuntimeException("Unable to clone LAMS lesson, bad URL: '" + serverAddr + + "', please check lams.properties", e); + } catch (IllegalStateException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (RemoteException e) { + throw new RuntimeException("Unable to clone LAMS lesson, RMI Remote Exception", e); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unable to clone LAMS lesson, Unsupported Encoding Exception", e); + } catch (ConnectException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (IOException e) { + throw new RuntimeException( + "Unable to clone LAMS lesson. " + e.getMessage() + " Please contact your system administrator.", e); + } catch (ParserConfigurationException e) { + throw new RuntimeException( + "Unable to clone LAMS lesson. " + e.getMessage() + " Can't instantiate DocumentBuilder.", e); + } catch (SAXException e) { + throw new RuntimeException( + "Unable to clone LAMS lesson. " + e.getMessage() + " Can't parse LAMS results.", e); + } catch (Exception e) { + throw new RuntimeException("Unable to clone LAMS lesson. Please contact your system administrator.", e); + } + + } + + /** + * Import learning design in LAMS from its temp folder. Then starting a lesson using this learning design. + * + * @param courseId + * courseId as a request parameter + * @param ldId + * the learning design id for which you wish to start a lesson + * + * @return lesson id of a cloned lesson + * @throws LamsServerException + */ + public static Long importLearningDesign(User teacher, String courseId, String lsId, String ldId) throws LamsServerException { + + String serverId = getServerID(); + String serverAddr = getServerAddress(); + String serverKey = getServerKey(); + String username = teacher.getUserName(); + String locale = teacher.getLocale(); + String country = getCountry(locale); + String lang = getLanguage(locale); + + if (courseId == null || serverId == null || serverAddr == null || serverKey == null) { + logger.info("Unable to import lesson, one or more lams configuration properties or the course id is null"); + throw new RuntimeException("Unable to import lesson, one or more lams configuration properties or the course id is null. courseId="+courseId); + } + + //import a learning design + String filePath = EXPORT_FOLDER_LAMS_SERVER + lsId + "_" + ldId + ".zip"; + + try { + String filePathParam = URLEncoder.encode(filePath, "UTF-8"); + String timestamp = new Long(System.currentTimeMillis()).toString(); + String hash = generateAuthenticationHash(timestamp, username, serverId); + String serviceURL = serverAddr + "/services/xml/LessonManager?" + "serverId=" + + URLEncoder.encode(serverId, "utf8") + "&datetime=" + timestamp + "&username=" + + URLEncoder.encode(username, "utf8") + "&hashValue=" + hash + "&courseId=" + + URLEncoder.encode(courseId, "UTF8") + "&country=" + + country + "&lang=" + lang + "&method=import&customCSV=&filePath=" + filePathParam; + + logger.info("LAMS import lesson request: " + serviceURL); + + // parse xml response and get the ldid + InputStream is = LamsSecurityUtil.callLamsServerPost(serviceURL); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document document = db.parse(is); + return Long.parseLong(document.getElementsByTagName("Lesson").item(0).getAttributes() + .getNamedItem("ldId").getNodeValue()); + + } catch (MalformedURLException e) { + throw new LamsServerException("Unable to import LAMS lesson, bad URL: '" + serverAddr + + "', please check lams.properties. Tried to import file " + filePath, e); + } catch (IllegalStateException e) { + throw new LamsServerException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator. Tried to import file " + + filePath, + e); + } catch (RemoteException e) { + throw new LamsServerException( + "Unable to import LAMS lesson, RMI Remote Exception. Tried to import file " + filePath, e); + } catch (UnsupportedEncodingException e) { + throw new LamsServerException( + "Unable to import LAMS lesson, Unsupported Encoding Exception. Tried to import file " + filePath, + e); + } catch (ConnectException e) { + throw new LamsServerException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator. Tried to import file " + + filePath, + e); + } catch (IOException e) { + throw new LamsServerException("Unable to import LAMS lesson. " + e.getMessage() + + " Please contact your system administrator. Tried to import file " + filePath, e); + } catch (ParserConfigurationException e) { + throw new LamsServerException("Unable to import LAMS lesson. " + e.getMessage() + + " Can't instantiate DocumentBuilder. Tried to import file " + filePath, e); + } catch (SAXException e) { + throw new LamsServerException("Unable to import LAMS lesson. " + e.getMessage() + + " Can't parse LAMS results. Tried to import file " + filePath, e); + } catch (Exception e) { + throw new LamsServerException( + "Unable to import LAMS lesson. Please contact your system administrator. Tried to import file " + + filePath, + e); + } + + } + + /** + * Pre-adding students and monitors to a lesson + * + * @param ctx + * the blackboard contect, contains session data + * @param lessonId + * the lesoon id that was just started + */ + public static void preaddLearnersMonitorsToLesson(Context ctx, long lessonId) { + String serverId = getServerID(); + String serverAddr = getServerAddress(); + String serverKey = getServerKey(); + String courseIdStr = ctx.getCourse().getCourseId(); + String username = ctx.getUser().getUserName(); + String locale = ctx.getUser().getLocale(); + String country = getCountry(locale); + String lang = getLanguage(locale); + + if (serverId == null || serverAddr == null || serverKey == null) { + throw new RuntimeException("Unable to start lesson, one or more lams configuration properties is null"); + } + + try { + + /* + * Returns a list of learners and monitors in the given course or group. + */ + + String learnerIds = ""; + String firstNames = ""; + String lastNames = ""; + String emails = ""; + String monitorIds = ""; + final String DUMMY_VALUE = "-"; + + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + Id courseId = ctx.getCourse().getId(); + + CourseMembershipDbLoader courseMemLoader = (CourseMembershipDbLoader) bbPm + .getLoader(CourseMembershipDbLoader.TYPE); + BbList studentCourseMemberships = courseMemLoader.loadByCourseIdAndRole(courseId, + CourseMembership.Role.STUDENT, null, true); + for (CourseMembership courseMembership : studentCourseMemberships) { + learnerIds += URLEncoder.encode(courseMembership.getUser().getUserName(), "utf8") + ","; + + String firstName = courseMembership.getUser().getGivenName().isEmpty() ? DUMMY_VALUE : courseMembership + .getUser().getGivenName(); + firstNames += URLEncoder.encode(firstName, "utf8") + ","; + + String lastName = courseMembership.getUser().getFamilyName().isEmpty() ? DUMMY_VALUE : courseMembership + .getUser().getFamilyName(); + lastNames += URLEncoder.encode(lastName, "utf8") + ","; + + String email = courseMembership.getUser().getEmailAddress().isEmpty() ? DUMMY_VALUE : courseMembership + .getUser().getEmailAddress(); + emails += URLEncoder.encode(email, "utf8") + ","; + } + + BbList monitorCourseMemberships = courseMemLoader.loadByCourseIdAndRole(courseId, + CourseMembership.Role.INSTRUCTOR, null, true); + BbList teachingAssistantCourseMemberships = courseMemLoader.loadByCourseIdAndRole(courseId, + CourseMembership.Role.TEACHING_ASSISTANT, null, true); + monitorCourseMemberships.addAll(teachingAssistantCourseMemberships); + BbList courseBuilderCourseMemberships = courseMemLoader.loadByCourseIdAndRole(courseId, + CourseMembership.Role.COURSE_BUILDER, null, true); + monitorCourseMemberships.addAll(courseBuilderCourseMemberships); + for (CourseMembership courseMembership : monitorCourseMemberships) { + monitorIds += URLEncoder.encode(courseMembership.getUser().getUserName(), "utf8") + ","; + + String firstName = courseMembership.getUser().getGivenName().isEmpty() ? DUMMY_VALUE : courseMembership + .getUser().getGivenName(); + firstNames += URLEncoder.encode(firstName, "utf8") + ","; + + String lastName = courseMembership.getUser().getFamilyName().isEmpty() ? DUMMY_VALUE : courseMembership + .getUser().getFamilyName(); + lastNames += URLEncoder.encode(lastName, "utf8") + ","; + + String email = courseMembership.getUser().getEmailAddress().isEmpty() ? DUMMY_VALUE : courseMembership + .getUser().getEmailAddress(); + emails += URLEncoder.encode(email, "utf8") + ","; + } + + //no learners & no monitors - do nothing + if (learnerIds.isEmpty() && monitorIds.isEmpty()) { + return; + } + + // remove trailing comma + learnerIds = learnerIds.isEmpty() ? "" : learnerIds.substring(0, learnerIds.length() - 1); + firstNames = firstNames.isEmpty() ? "" : firstNames.substring(0, firstNames.length() - 1); + lastNames = lastNames.isEmpty() ? "" : lastNames.substring(0, lastNames.length() - 1); + emails = emails.isEmpty() ? "" : emails.substring(0, emails.length() - 1); + monitorIds = monitorIds.isEmpty() ? "" : monitorIds.substring(0, monitorIds.length() - 1); + + String timestamp = new Long(System.currentTimeMillis()).toString(); + String hash = generateAuthenticationHash(timestamp, username, serverId); + + String serviceURL = serverAddr + "/services/xml/LessonManager?" + "&serverId=" + + URLEncoder.encode(serverId, "utf8") + "&datetime=" + timestamp + "&username=" + + URLEncoder.encode(username, "utf8") + "&hashValue=" + hash + "&courseId=" + + URLEncoder.encode(courseIdStr, "utf8") + "&lsId=" + lessonId + "&country=" + country + "&lang=" + + lang + "&method=join" + "&firstNames=" + + firstNames + "&lastNames=" + lastNames + "&emails=" + emails; + if (!monitorIds.isEmpty()) { + serviceURL += "&monitorIds=" + monitorIds; + } + if (!learnerIds.isEmpty()) { + serviceURL += "&learnerIds=" + learnerIds; + } + + logger.info("LAMS Preadd users Req: " + serviceURL); + System.out.println("LAMS Preadd users Req: " + serviceURL); + + InputStream is = LamsSecurityUtil.callLamsServerPost(serviceURL); + + } catch (MalformedURLException e) { + throw new RuntimeException("Unable to preadd users to the lesson, bad URL: '" + serverAddr + + "', please check lams.properties", e); + } catch (IllegalStateException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (RemoteException e) { + throw new RuntimeException("Unable to preadd users to the lesson, RMI Remote Exception", e); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unable to preadd users to the lesson, Unsupported Encoding Exception", e); + } catch (ConnectException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (IOException e) { + throw new RuntimeException("Unable to preadd users to the lesson. " + e.getMessage() + + " Please contact your system administrator.", e); + } catch (Exception e) { + throw new RuntimeException( + "Unable to preadd users to the lesson. Please contact your system administrator.", e); + } + + } + + /** + * getLearnerProgress in current lesson through a LAMS webservice + * + * @param ctx + * the blackboard contect, contains session data + * @param lsId + * the lesson id for which you wish to retrieve progress + * + * @return the learning session id + */ + public static LearnerProgressDTO getLearnerProgress(Context ctx, long lsId) { + String serverId = getServerID(); + String serverAddr = getServerAddress(); + String serverKey = getServerKey(); + String courseId = ctx.getCourse().getCourseId(); + + String username = ctx.getUser().getUserName(); + String firstName = ctx.getUser().getGivenName(); + String lastName = ctx.getUser().getFamilyName(); + String email = ctx.getUser().getEmailAddress(); + String locale = ctx.getUser().getLocale(); + String country = getCountry(locale); + String lang = getLanguage(locale); + + if (serverId == null || serverAddr == null || serverKey == null) { + throw new RuntimeException("Unable to start lesson, one or more lams configuration properties is null"); + } + + try { + + String timestamp = new Long(System.currentTimeMillis()).toString(); + String hash = generateAuthenticationHash(timestamp, username, serverId); + + String serviceURL = serverAddr + "/services/xml/LessonManager?method=singleStudentProgress" + "&serverId=" + + URLEncoder.encode(serverId, "utf8") + "&datetime=" + timestamp + "&username=" + + URLEncoder.encode(username, "utf8") + "&hashValue=" + hash + "&courseId=" + + URLEncoder.encode(courseId, "utf8") + "&country=" + country + "&lang=" + lang + "&firstName=" + + URLEncoder.encode(firstName, "UTF-8") + "&lastName=" + URLEncoder.encode(lastName, "UTF-8") + + "&email=" + URLEncoder.encode(email, "UTF-8") + "&lsId=" + new Long(lsId).toString(); + + logger.info("Retirieving learner progress: " + serviceURL); + + // InputStream is = url.openConnection().getInputStream(); + InputStream is = LamsSecurityUtil.callLamsServerPost(serviceURL); + + // parse xml response + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document document = db.parse(is); + + // get the lesson id from the response + NamedNodeMap learnerProgress = document.getElementsByTagName("LearnerProgress").item(0).getAttributes(); + boolean lessonComplete = Boolean.parseBoolean(learnerProgress.getNamedItem("lessonComplete").getNodeValue()); + int activitiesCompleted = Integer.parseInt(learnerProgress.getNamedItem("activitiesCompleted").getNodeValue()); + int attemptedActivities = Integer.parseInt(learnerProgress.getNamedItem("attemptedActivities").getNodeValue()); + int activityCount = Integer.parseInt(learnerProgress.getNamedItem("activityCount").getNodeValue()); + + LearnerProgressDTO learnerProgressDto = new LearnerProgressDTO(activityCount, attemptedActivities, + activitiesCompleted, lessonComplete); + + return learnerProgressDto; + + } catch (MalformedURLException e) { + throw new RuntimeException("Unable to get LearnerProgress, bad URL: '" + serverAddr + + "', please check lams.properties", e); + } catch (IllegalStateException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (RemoteException e) { + throw new RuntimeException("Unable to get LearnerProgress, RMI Remote Exception", e); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unable to get LearnerProgress, Unsupported Encoding Exception", e); + } catch (ConnectException e) { + throw new RuntimeException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (IOException e) { + throw new RuntimeException("Unable to get LearnerProgress. " + e.getMessage() + + " Please contact your system administrator.", e); + } catch (Exception e) { + throw new RuntimeException("Unable to get LearnerProgress. Please contact your system administrator.", e); + } + + } + + /** + * Make a call to LAMS server. + * + * @param serviceURL + * @return resulted InputStream + * @throws IOException + */ + private static InputStream callLamsServer(String serviceURL) throws IOException { + URL url = new URL(serviceURL); + URLConnection conn = url.openConnection(); + if (!(conn instanceof HttpURLConnection)) { + throw new IOException("Unable to open connection to: " + serviceURL); + } + + HttpURLConnection httpConn = (HttpURLConnection) conn; + + if (httpConn.getResponseCode() != HttpURLConnection.HTTP_OK) { + throw new IOException("LAMS server responded with HTTP response code: " + httpConn.getResponseCode() + + ", HTTP response message: " + httpConn.getResponseMessage()); + } + + // InputStream is = url.openConnection().getInputStream(); + InputStream is = conn.getInputStream(); + + return is; + } + + + /** + * Make a call to LAMS server. + * + * @param serviceURL + * @return resulted InputStream + * @throws IOException + */ + private static InputStream callLamsServerPost(String serviceURL) throws IOException { + + String path; + String body; + + int bodyStart = serviceURL.indexOf('?'); + if ( bodyStart < 0 ) { + path = serviceURL; + body = ""; + } else { + path = serviceURL.substring(0,bodyStart); + body = serviceURL.substring(bodyStart+1); + } + + byte[] postData = body.getBytes("UTF-8"); + int postDataLength = postData.length; + + URL url = new URL(path); + URLConnection conn = url.openConnection(); + if (!(conn instanceof HttpURLConnection)) { + throw new IOException("Unable to open connection to: " + serviceURL); + } + + HttpURLConnection httpConn = (HttpURLConnection) conn; + conn.setDoOutput( true ); + httpConn.setRequestMethod("POST"); + conn.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded"); + conn.setRequestProperty( "charset", "utf-8"); + conn.setRequestProperty( "Content-Length", Integer.toString( postDataLength )); + conn.setUseCaches( false ); + + conn.getOutputStream().write(postData); + + if (httpConn.getResponseCode() != HttpURLConnection.HTTP_OK) { + throw new IOException("LAMS server responded with HTTP response code: " + httpConn.getResponseCode() + + ", HTTP response message: " + httpConn.getResponseMessage()); + } + + InputStream is = conn.getInputStream(); + return is; + } + + public static String getServerTime() throws IOException, PersistenceException { + long now = (new Date()).getTime(); + + // get LamsServerTime from the storage + PortalExtraInfo pei = PortalUtil.loadPortalExtraInfo(null, null, "LamsServerTimeStorage"); + ExtraInfo ei = pei.getExtraInfo(); + + String lamsServerTimeDeltaStr = ei.getValue("LAMSServerTimeDelta"); + long lamsServerTimeDelta = (lamsServerTimeDeltaStr == null) ? -1 : Long.parseLong(lamsServerTimeDeltaStr); + + //check if it's time to update + String lastUpdateTimeStr = ei.getValue("lastUpdateTime"); + long lastUpdateTime = (lastUpdateTimeStr == null) ? -1 : Long.parseLong(lastUpdateTimeStr); + long lamsServerTime; + + long lamsServerTimeRefreshInterval = getLamsServerTimeRefreshInterval() * 60 * 60 * 1000; + if ((lamsServerTimeDeltaStr == null) || (lastUpdateTime + lamsServerTimeRefreshInterval < now)) { + + // refresh time from LAMS server + String serverAddr = getServerAddress(); + String serviceURL = serverAddr + "/services/getServerTime"; + logger.info("LAMS Get Server Time request: " + serviceURL); + InputStream is = LamsSecurityUtil.callLamsServer(serviceURL); + + StringWriter writer = new StringWriter(); + IOUtils.copy(is, writer, "UTF-8"); + + try { + lamsServerTime = Long.parseLong(writer.toString().trim()); + } catch (NumberFormatException e) { + throw new RuntimeException("LAMS server returned wrong time format on call to /service/getServerTime", + e); + } + lamsServerTimeDelta = now - lamsServerTime; + + // Store LAMSServerTime and lastUpdateTime to the storage + ei.setValue("LAMSServerTimeDelta", "" + lamsServerTimeDelta); + ei.setValue("lastUpdateTime", "" + now); + PortalUtil.savePortalExtraInfo(pei); + } else { + + //no need to refresh - use stored value + lamsServerTime = now - lamsServerTimeDelta; + } + + return "" + lamsServerTime; + } + + /** + * Gets the app.version property value from + * the ./main.properties file of the base folder + * + * @return app.version string + * @throws IOException + */ + public static String getAppVersion() throws IOException{ + + String versionString = null; + + //to load application's properties, we use this class + Properties mainProperties = new Properties(); + + FileInputStream file; + + //the base folder is ./, the root of the main.properties file + String path = "./main.properties"; + + //load the file handle for main.properties + file = new FileInputStream(path); + + //load all the properties from this file + mainProperties.load(file); + + //we have loaded the properties, so close the file handle + file.close(); + + //retrieve the property we are intrested, the app.version + versionString = mainProperties.getProperty("app.version"); + + return versionString; + } + + /** + * @return gets server address from the lams.properties file + */ + public static String getServerAddress() { + return LamsPluginUtil.getProperties().getProperty(LamsPluginUtil.PROP_LAMS_URL); + } + + /** + * @return gets alternative server address from the lams.properties file + */ + public static String getAltServerAddress() { + return LamsPluginUtil.getProperties().getProperty(LamsPluginUtil.PROP_ALT_LAMS_URL); + } + + + /** + * @return gets server id from the lams.properties file + */ + public static String getServerID() { + return LamsPluginUtil.getProperties().getProperty(LamsPluginUtil.PROP_LAMS_SERVER_ID); + } + + /** + * @return gets server key from the lams.properties file + */ + public static String getServerKey() { + return LamsPluginUtil.getProperties().getProperty(LamsPluginUtil.PROP_LAMS_SECRET_KEY); + } + + /** + * @return gets request source from the lams.properties file + */ + public static String getReqSrc() { + return LamsPluginUtil.getProperties().getProperty(LamsPluginUtil.PROP_REQ_SRC); + } + + /** + * + * @return the LAMS server time refresh interval from lams.properties + */ + public static long getLamsServerTimeRefreshInterval() { + //set default value + long lamsServerTimeRefreshInterval = 24; + + try { + String lamsServerTimeRefreshIntervalStr = LamsPluginUtil.getProperties().getProperty( + LamsPluginUtil.PROP_LAMS_SERVER_TIME_REFRESH_INTERVAL); + lamsServerTimeRefreshInterval = Long.parseLong(lamsServerTimeRefreshIntervalStr); + } catch (NumberFormatException e) { + logger.warn("Wrong format of PROP_LAMS_SERVER_TIME_REFRESH_INTERVAL from lams.properties"); + } + + return lamsServerTimeRefreshInterval; + } + + // generate authentication hash code to validate parameters + public static String generateAuthenticationHash(String datetime, String login, String serverId) { + String secretkey = getServerKey(); + + String plaintext = datetime.toLowerCase().trim() + login.toLowerCase().trim() + serverId.toLowerCase().trim() + + secretkey.toLowerCase().trim(); + + String hash = sha1(plaintext); + + return hash; + } + + /** + * The parameters are: uid - the username on the external system method - either author, monitor or learner ts - + * timestamp sid - serverID str is [ts + uid + method + serverID + serverKey] (Note: all lower case) + * + * @param str + * The string to be hashed + * @return The hased string + */ + public static String sha1(String str) { + try { + MessageDigest md = MessageDigest.getInstance("SHA1"); + return new String(Hex.encodeHex(md.digest(str.getBytes()))); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + /** + * + * @param localeStr + * the full balckboard locale string + * @return the language + */ + public static String getLanguage(String localeStr) { + if (localeStr == null) + return "xx"; + String[] split = localeStr.split("_"); + return split[0]; + } + + /** + * + * @param localeStr + * the full balckboard locale string + * @return the country + */ + public static String getCountry(String localeStr) { + if (localeStr == null) + return "XX"; + String[] split = localeStr.split("_"); + + //default country set to AU + String country = split.length > 1 ? split[1] : "AU"; + return country; + } + +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/util/LamsServerException.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/util/LamsServerException.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/util/LamsServerException.java (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -0,0 +1,34 @@ +package org.lamsfoundation.ld.integration.util; + +/** + * Exception that originated at LAMS server. + */ +public class LamsServerException extends Exception { + + public LamsServerException() { + super(); + } + + /** + * @param message + */ + public LamsServerException(String message) { + super(message); + } + + /** + * @param cause + */ + public LamsServerException(Throwable cause) { + super(cause); + } + + /** + * @param message + * @param cause + */ + public LamsServerException(String message, Throwable cause) { + super(message, cause); + } + +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/util/LineitemUtil.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/util/LineitemUtil.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/util/LineitemUtil.java (revision 67bf7cc2eed9d841593d383cf139d63a65e1a8a6) @@ -0,0 +1,442 @@ +/**************************************************************** + * 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 + * **************************************************************** + */ + +package org.lamsfoundation.ld.integration.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.List; +import java.util.Set; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.apache.log4j.Logger; +import org.lamsfoundation.ld.integration.Constants; + +import blackboard.data.ValidationException; +import blackboard.data.content.Content; +import blackboard.data.content.CourseDocument; +import blackboard.data.course.Course; +import blackboard.data.gradebook.Lineitem; +import blackboard.data.gradebook.impl.OutcomeDefinition; +import blackboard.data.gradebook.impl.OutcomeDefinitionScale; +import blackboard.persist.BbPersistenceManager; +import blackboard.persist.Container; +import blackboard.persist.Id; +import blackboard.persist.KeyNotFoundException; +import blackboard.persist.PersistenceException; +import blackboard.persist.PkId; +import blackboard.persist.content.ContentDbLoader; +import blackboard.persist.course.CourseDbLoader; +import blackboard.persist.gradebook.LineitemDbLoader; +import blackboard.persist.gradebook.LineitemDbPersister; +import blackboard.persist.gradebook.ext.OutcomeDefinitionScaleDbLoader; +import blackboard.persist.gradebook.ext.OutcomeDefinitionScaleDbPersister; +import blackboard.persist.gradebook.impl.OutcomeDefinitionDbPersister; +import blackboard.platform.BbServiceManager; +import blackboard.platform.context.Context; +import blackboard.platform.persistence.PersistenceServiceFactory; +import blackboard.portal.data.ExtraInfo; +import blackboard.portal.data.PortalExtraInfo; +import blackboard.portal.servlet.PortalUtil; + +public class LineitemUtil { + + private final static String LAMS_LINEITEM_STORAGE = "LamsLineitemStorage"; + + private static Logger logger = Logger.getLogger(LineitemUtil.class); + + public static void createLineitem(Content bbContent, Context ctx, String userName) + throws ValidationException, PersistenceException, IOException { + LineitemDbPersister linePersister = LineitemDbPersister.Default.getInstance(); + OutcomeDefinitionDbPersister outcomeDefinitionPersister = OutcomeDefinitionDbPersister.Default.getInstance(); + OutcomeDefinitionScaleDbLoader outcomeDefinitionScaleLoader = OutcomeDefinitionScaleDbLoader.Default + .getInstance(); + OutcomeDefinitionScaleDbPersister uutcomeDefinitionScaleDbPersister = OutcomeDefinitionScaleDbPersister.Default + .getInstance(); + + String title = bbContent.getTitle(); + Id courseId = bbContent.getCourseId(); + String lamsLessonId = bbContent.getLinkRef(); + + // Create new Gradebook column for current bbContent + Lineitem lineitem = new Lineitem(); + lineitem.setCourseId(courseId); + lineitem.setName(title); + lineitem.setPointsPossible(Constants.GRADEBOOK_POINTS_POSSIBLE); + lineitem.setType(Constants.GRADEBOOK_LINEITEM_TYPE); + lineitem.setIsAvailable(true); + lineitem.setDateAdded(); + lineitem.setAssessmentLocation(Lineitem.AssessmentLocation.EXTERNAL); + lineitem.setAssessmentId(lamsLessonId, Lineitem.AssessmentLocation.EXTERNAL); + lineitem.validate(); + linePersister.persist(lineitem); + + OutcomeDefinition outcomeDefinition = lineitem.getOutcomeDefinition(); + outcomeDefinition.setCourseId(courseId); + outcomeDefinition.setPosition(1); + + boolean hasLessonScoreOutputs = LineitemUtil.hasLessonScoreOutputs(bbContent, ctx, userName); + OutcomeDefinitionScale outcomeDefinitionScale; + if (hasLessonScoreOutputs) { + outcomeDefinitionScale = outcomeDefinitionScaleLoader.loadByCourseIdAndTitle(courseId, + OutcomeDefinitionScale.SCORE); + outcomeDefinitionScale.setNumericScale(true); + outcomeDefinitionScale.setPercentageScale(true); + outcomeDefinition.setScorable(true); + } else { + outcomeDefinitionScale = outcomeDefinitionScaleLoader.loadByCourseIdAndTitle(courseId, + OutcomeDefinitionScale.COMPLETE_INCOMPLETE); + outcomeDefinitionScale.setNumericScale(false); + outcomeDefinition.setScorable(false); + } + uutcomeDefinitionScaleDbPersister.persist(outcomeDefinitionScale); + outcomeDefinition.setScale(outcomeDefinitionScale); + outcomeDefinitionPersister.persist(outcomeDefinition); + + updateLamsLineitemStorage(bbContent, lineitem); + } + + /* + * Checks whether lesson has scorable outputs (i.e. MCQ or Assessment activity). + * + * @param ctx + * the blackboard context, contains session data + * + * @return an url pointing to the LAMS lesson, monitor, author session + * + * @throws IOException + */ + private static boolean hasLessonScoreOutputs(Content bbContent, Context ctx, String username) throws IOException { + String ldId = ctx.getRequestParameter("sequence_id"); + + //sequence_id parameter is null in case we come from modify_proc + if (ldId == null) { + //get sequence_id from bbcontent URL set in start_lesson_proc + String bbContentUrl = bbContent.getUrl(); + String[] params = bbContentUrl.split("&"); + for (String param : params) { + String paramName = param.split("=")[0]; + String paramValue = param.split("=")[1]; + + if ("ldid".equals(paramName)) { + ldId = paramValue; + break; + } + } + } + + String learningDesignSvgUrl = LamsSecurityUtil.generateRequestLearningDesignImage(username, true) + "&ldId=" + + ldId.trim(); + + URL url = new URL(learningDesignSvgUrl); + URLConnection conn = url.openConnection(); + if (!(conn instanceof HttpURLConnection)) { + throw new RuntimeException("Unable to open connection to: " + learningDesignSvgUrl); + } + + HttpURLConnection httpConn = (HttpURLConnection) conn; + + if (httpConn.getResponseCode() != HttpURLConnection.HTTP_OK) { + throw new RuntimeException("HTTP Response Code: " + httpConn.getResponseCode() + ", HTTP Response Message: " + + httpConn.getResponseMessage()); + } + + // InputStream is = url.openConnection().getInputStream(); + InputStream is = conn.getInputStream(); + + // parse xml response + String learningDesignSvg = IOUtils.toString(is, "UTF-8"); + boolean hasLessonScoreOutputs = (learningDesignSvg.indexOf("icon_mcq.png") != -1) + || (learningDesignSvg.indexOf("icon_assessment.png") != -1); + + return hasLessonScoreOutputs; + } + + /** + * Removes lineitem. Throws exception if lineitem is not found. + * + * @param bbContentId + * @param courseIdStr + * @throws PersistenceException + * @throws ServletException + */ + public static void removeLineitem(String bbContentId, String courseIdStr) + throws PersistenceException, ServletException { + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + Container bbContainer = bbPm.getContainer(); + ContentDbLoader courseDocumentLoader = ContentDbLoader.Default.getInstance(); + LineitemDbPersister linePersister = LineitemDbPersister.Default.getInstance(); + + Id contentId = new PkId(bbContainer, CourseDocument.DATA_TYPE, bbContentId); + Content bbContent = courseDocumentLoader.loadById(contentId); + //check isGradecenter option is ON (isDescribed field is used for storing isGradecenter parameter) + if (!bbContent.getIsDescribed()) { + return; + } + + Id lineitemId = getLineitem(bbContentId, courseIdStr, true); + linePersister.deleteById(lineitemId); + } + + /** + * Changes lineitem's name. Throws exception if lineitem is not found. + * + * @param bbContentId + * @param courseIdStr + * @param newLineitemName + * @throws PersistenceException + * @throws ServletException + * @throws ValidationException + */ + public static void changeLineitemName(String bbContentId, String courseIdStr, String newLineitemName) + throws PersistenceException, ServletException, ValidationException { + LineitemDbLoader lineitemLoader = LineitemDbLoader.Default.getInstance(); + LineitemDbPersister linePersister = LineitemDbPersister.Default.getInstance(); + + Id lineitemId = getLineitem(bbContentId, courseIdStr, true); + Lineitem lineitem = lineitemLoader.loadById(lineitemId); + lineitem.setName(newLineitemName); + linePersister.persist(lineitem); + } + + /** + * Changes lineitem's lamsLessonId. Throws exception if lineitem is not found. + * + * @param bbContentId + * @param courseIdStr + * @param newLineitemName + * @throws PersistenceException + * @throws ServletException + * @throws ValidationException + * @throws IOException + */ + public static void updateLineitemLessonId(Content bbContent, String courseIdStr, Long newLamsLessonId, Context ctx, String userName) + throws PersistenceException, ServletException, ValidationException, IOException { + LineitemDbLoader lineitemLoader = LineitemDbLoader.Default.getInstance(); + LineitemDbPersister linePersister = LineitemDbPersister.Default.getInstance(); + + PkId contentId = (PkId) bbContent.getId(); + String _content_id = "_" + contentId.getPk1() + "_" + contentId.getPk2(); + + //update only in case grade center is ON + if (bbContent.getIsDescribed()) { + + Id lineitemId = getLineitem(_content_id, courseIdStr, false); + //in case admin forgot to check "Grade Center Columns and Settings" option on doing course copy/import + if (lineitemId == null) { + createLineitem(bbContent, ctx, userName); + + //in case he checked it and BB created Lineitem object, then just need to update it + } else { + Lineitem lineitem = lineitemLoader.loadById(lineitemId); + lineitem.setAssessmentId(Long.toString(newLamsLessonId), Lineitem.AssessmentLocation.EXTERNAL); + linePersister.persist(lineitem); + + updateLamsLineitemStorage(bbContent, lineitem); + } + + } + + // store internalContentId -> externalContentId. It's used for GradebookServlet. Store it just in case + PortalExtraInfo pei = PortalUtil.loadPortalExtraInfo(null, null, "LamsStorage"); + ExtraInfo ei = pei.getExtraInfo(); + ei.setValue(_content_id, Long.toString(newLamsLessonId)); + PortalUtil.savePortalExtraInfo(pei); + } + + /* Updates "LamsLineitemStorage" storage used for storing bbContentId->lineitem + * @param bbContent + * @param lineitem + * @throws PersistenceException + */ + private static void updateLamsLineitemStorage(Content bbContent, Lineitem lineitem) throws PersistenceException { + //Construct bbContent id + PkId bbContentPkId = (PkId) bbContent.getId(); + String bbContentId = "_" + bbContentPkId.getPk1() + "_" + bbContentPkId.getPk2(); + //Construct lineitem id + PkId lineitemPkId = (PkId) lineitem.getId(); + String lineitemId = "_" + lineitemPkId.getPk1() + "_" + lineitemPkId.getPk2(); + + //Store lineitemid to the storage bbContentId -> lineitemid (this storage is available since 1.2.3 version) + PortalExtraInfo pei = PortalUtil.loadPortalExtraInfo(null, null, LAMS_LINEITEM_STORAGE); + ExtraInfo ei = pei.getExtraInfo(); + ei.setValue(bbContentId, lineitemId); + PortalUtil.savePortalExtraInfo(pei); + } + + /* + * Finds lineitem by bbContentId and courseId. + * + * @throws ServletException + * + * @throws PersistenceException + */ + private static Id getLineitem(String bbContentId, String courseIdStr, boolean isThrowException) + throws ServletException, PersistenceException, LamsBuildingBlockException { + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + LineitemDbLoader lineitemLoader = LineitemDbLoader.Default.getInstance(); + + //get lineitemId from the storage (bbContentId -> lineitemId) + PortalExtraInfo pei = PortalUtil.loadPortalExtraInfo(null, null, LAMS_LINEITEM_STORAGE); + ExtraInfo ei = pei.getExtraInfo(); + String lineitemIdStr = ei.getValue(bbContentId); + + // try to get lineitem from any course that user is participating in (for lineitems created in versions after 1.2 and before 1.2.3) + if (lineitemIdStr == null) { + // get stored bbContentId -> lamsLessonId + PortalExtraInfo portalExtraInfo = PortalUtil.loadPortalExtraInfo(null, null, "LamsStorage"); + ExtraInfo extraInfo = portalExtraInfo.getExtraInfo(); + String lamsLessonId = extraInfo.getValue(bbContentId); + + if (lamsLessonId == null || "".equals(lamsLessonId)) { + if (isThrowException) { + throw new LamsBuildingBlockException("lamsLessonId corresponding to bbContentId= " + bbContentId + + " was not found in LamsStorage"); + } else { + return null; + } + } + + // search for appropriate lineitem + Id courseId = bbPm.generateId(Course.DATA_TYPE, courseIdStr); + List lineitems = lineitemLoader.loadByCourseId(courseId); + + Lineitem lineitem = null; + for (Lineitem lineitemIter : lineitems) { + if (lineitemIter.getAssessmentId() != null && lineitemIter.getAssessmentId().equals(lamsLessonId)) { + lineitem = lineitemIter; + break; + } + } + + if (lineitem == null) { + if (isThrowException) { + throw new LamsBuildingBlockException( + "Lineitem that corresponds to bbContentId: " + bbContentId + " was not found"); + } else { + return null; + } + } + + // delete lineitem (can't delete it simply doing linePersister.deleteById(lineitem.getId()) due to BB9 bug) + PkId lineitemPkId = (PkId) lineitem.getId(); + lineitemIdStr = "_" + lineitemPkId.getPk1() + "_" + lineitemPkId.getPk2(); + } + + if (lineitemIdStr == null) { + throw new LamsBuildingBlockException("Lineitem was not found for contentId:" + bbContentId + + ". This is despite the fact that isGradecenter option is ON."); + } + + Id lineitemId = bbPm.generateId(Lineitem.LINEITEM_DATA_TYPE, lineitemIdStr.trim()); + return lineitemId; + } + + /** + * Finds lineitem by bbContentId, userId and lamsLessonId. + * + * @throws ServletException + * @throws PersistenceException + */ + public static Lineitem getLineitem(String bbContentId, Id userId, String lamsLessonIdParam) + throws ServletException, PersistenceException { + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + CourseDbLoader courseLoader = CourseDbLoader.Default.getInstance(); + LineitemDbLoader lineitemLoader = LineitemDbLoader.Default.getInstance(); + + // get lineitemId from the storage (bbContentId -> lineitemid) + PortalExtraInfo pei = PortalUtil.loadPortalExtraInfo(null, null, LAMS_LINEITEM_STORAGE); + ExtraInfo ei = pei.getExtraInfo(); + String lineitemIdStr = ei.getValue(bbContentId); + + if (lineitemIdStr != null) { + Id lineitemId = bbPm.generateId(Lineitem.LINEITEM_DATA_TYPE, lineitemIdStr.trim()); + return lineitemLoader.loadById(lineitemId); + + // try to get lineitem from any course that user is participating in (for lineitems created in versions after 1.2 and before 1.2.3) + } else { + + // search for appropriate lineitem + List userCourses = courseLoader.loadByUserId(userId); + for (Course userCourse : userCourses) { + List lineitems = lineitemLoader.loadByCourseId(userCourse.getId()); + + for (Lineitem lineitem : lineitems) { + if (lineitem.getAssessmentId() != null && lineitem.getAssessmentId().equals(lamsLessonIdParam)) { + return lineitem; + } + } + } + } + + return null; + } + + /** + * Finds lineitem by userId and lamsLessonId. The same as above method but first finds bbContentId and also checks + * Grade center option is ON. + * + * @throws ServletException + * @throws PersistenceException + */ + public static Lineitem getLineitem(Id userId, String lamsLessonIdParam) + throws ServletException, PersistenceException { + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + Container bbContainer = bbPm.getContainer(); + ContentDbLoader contentDbLoader = ContentDbLoader.Default.getInstance(); + + //Finds bbContentId from "LamsStorage" corresponding to specified lamsLessonId. + //Note: bbContentId can be null in case it's Chen Rui's BB, which doesn't have "LamsStorage". + PortalExtraInfo portalExtraInfo = PortalUtil.loadPortalExtraInfo(null, null, "LamsStorage"); + ExtraInfo extraInfo = portalExtraInfo.getExtraInfo(); + Set bbContentIds = extraInfo.getKeys(); + String bbContentId = null; + for (String bbContentIdIter : bbContentIds) { + String lamsLessonId = extraInfo.getValue(bbContentIdIter); + if (lamsLessonIdParam.equals(lamsLessonId)) { + bbContentId = bbContentIdIter; + break; + } + } + + if (bbContentId != null) { + Id contentId = new PkId(bbContainer, CourseDocument.DATA_TYPE, bbContentId); + Content bbContent = contentDbLoader.loadById(contentId); + // check isGradecenter option is ON (bbContent.isDescribed field is used for storing isGradecenter parameter) + if (!bbContent.getIsDescribed()) { + throw new LamsBuildingBlockException("Operation failed due to lesson (lessonId=" + lamsLessonIdParam + + ") has gradecenter option switched OFF."); + } + } + + //get lineitem + return LineitemUtil.getLineitem(bbContentId == null ? "" : bbContentId, userId, lamsLessonIdParam); + } +} Fisheye: Tag 67bf7cc2eed9d841593d383cf139d63a65e1a8a6 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/util/CSVUtil.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 67bf7cc2eed9d841593d383cf139d63a65e1a8a6 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/util/LamsServerException.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 67bf7cc2eed9d841593d383cf139d63a65e1a8a6 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/util/LineitemUtil.java'. Fisheye: No comparison available. Pass `N' to diff?