Index: 3rdParty_sources/basiclti-util/src/main/java/org/imsglobal/lti/BasicLTIConstants.java =================================================================== diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r5462a5e4f4762e4de1db63099864a5c0301a5b2e --- 3rdParty_sources/basiclti-util/src/main/java/org/imsglobal/lti/BasicLTIConstants.java (.../BasicLTIConstants.java) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80) +++ 3rdParty_sources/basiclti-util/src/main/java/org/imsglobal/lti/BasicLTIConstants.java (.../BasicLTIConstants.java) (revision 5462a5e4f4762e4de1db63099864a5c0301a5b2e) @@ -411,10 +411,11 @@ */ public static final String EXT_SAKAI_PROVIDER_DISPLAYID = "ext_sakai_provider_displayid"; - //**LAMS** added the next 3 lines + //**LAMS** added the next 4 lines public static final String CONTENT_ITEMS = "content_items"; public static final String LTI_MESSAGE = "lti_msg"; public static final String LTI_ERROR_MESSAGE = "lti_errormsg"; + public static final String DATA = "data"; /** * Utility array useful for validating property names when building launch @@ -437,7 +438,7 @@ TC_PROFILE_URL, TOOL_CONSUMER_INSTANCE_CONTACT_EMAIL, TOOL_CONSUMER_INSTANCE_DESCRIPTION, TOOL_CONSUMER_INSTANCE_GUID, TOOL_CONSUMER_INSTANCE_NAME, - TOOL_CONSUMER_INSTANCE_URL, USER_ID, USER_IMAGE, CONTENT_ITEMS, LTI_MESSAGE, LTI_ERROR_MESSAGE }; //**LAMS** added CONTENT_ITEMS, LTI_MESSAGE, LTI_ERROR_MESSAGE constants + TOOL_CONSUMER_INSTANCE_URL, USER_ID, USER_IMAGE, CONTENT_ITEMS, LTI_MESSAGE, LTI_ERROR_MESSAGE, DATA }; //**LAMS** added CONTENT_ITEMS, LTI_MESSAGE, LTI_ERROR_MESSAGE, DATA constants /** * The default site type to use if a site needs to be created. Can be overriden in sakai.properties or as part of the launch. Index: lams_build/lib/basiclti-util/basiclti-util-1.2.0.jar =================================================================== diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r5462a5e4f4762e4de1db63099864a5c0301a5b2e Binary files differ Index: lams_central/src/java/org/lamsfoundation/lams/web/controller/LtiController.java =================================================================== diff -u -r0f9ee91abfb169ea992d5cbfdab6e86f47445c92 -r5462a5e4f4762e4de1db63099864a5c0301a5b2e --- lams_central/src/java/org/lamsfoundation/lams/web/controller/LtiController.java (.../LtiController.java) (revision 0f9ee91abfb169ea992d5cbfdab6e86f47445c92) +++ lams_central/src/java/org/lamsfoundation/lams/web/controller/LtiController.java (.../LtiController.java) (revision 5462a5e4f4762e4de1db63099864a5c0301a5b2e) @@ -6,12 +6,16 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.TreeMap; import java.util.Vector; +import java.util.Map.Entry; +import java.util.regex.Pattern; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; @@ -97,36 +101,40 @@ String extUserId = request.getParameter(BasicLTIConstants.USER_ID); Long customLessonId = WebUtil.readLongParam(request, "custom_lessonid", true); boolean isContentItemSelection = WebUtil.readBooleanParam(request, "custom_iscontentitemselection", false); + //parameter containing original resource_link_id, available after course copy (BB only) + String resourceLinkIdHistory = request.getParameter("resource_link_id_history"); ExtServerLessonMap extLessonMap = integrationService.getLtiConsumerLesson(consumerKey, resourceLinkId); ExtServer extServer = integrationService.getExtServer(consumerKey); -// log.info("" request.getParameter(name)); - Map params = request.getParameterMap(); - Iterator i = params.keySet().iterator(); - while (i.hasNext()) { - String key = (String) i.next(); - String value = ((String[]) params.get(key))[0]; - log.info("#### Parameter " + key + ": " + value); - } - - //it's the case of ContentItemSelection request or course-copy. (Currently we support course-copy only for links created using ContentItemSelection) - if (extLessonMap == null && (customLessonId != null) && isContentItemSelection) { - Lesson lesson = lessonService.getLesson(customLessonId); + //it's the case of ContentItemSelection request or course-copy. (Currently we support course-copy only for links created using ContentItemSelection or from Blackboard server) + if (extLessonMap == null && (isContentItemSelection || StringUtils.isNotBlank(resourceLinkIdHistory))) { + //figure out original lesson + Lesson lesson; + if (isContentItemSelection) { + lesson = lessonService.getLesson(customLessonId); + + } else { + ExtServerLessonMap oldExtLessonMap = integrationService.getLtiConsumerLesson(consumerKey, + resourceLinkIdHistory); + lesson = lessonService.getLesson(oldExtLessonMap.getLessonId()); + } + String extCourseId = request.getParameter(BasicLTIConstants.CONTEXT_ID); - ExtCourseClassMap orgMap = integrationService.getExtCourseClassMap(extServer.getSid(), extCourseId); - Integer organisationId = orgMap.getOrganisation().getOrganisationId(); + ExtCourseClassMap currentOrgMap = integrationService.getExtCourseClassMap(extServer.getSid(), extCourseId); + Integer currentOrganisationId = currentOrgMap.getOrganisation().getOrganisationId(); - //check if the new lesson should be created after course copy, that potentially has happened on LMS side. - //(we can detect it by comparing orgId of the custom_lessonid's organisation and CONTEXT_ID's one) + //check if the new lesson should be created after course copy, that potentially has happened on LMS side; + //(we can detect it by comparing orgId of the custom_lessonid's organisation and CONTEXT_ID's one). + //Otherwise this is the case of a first call after deep linking was used, and thus we need to update resourceLinkId, which was empty previously. boolean isLessonCopyRequired = lesson.getOrganisation() != null - && !lesson.getOrganisation().getOrganisationId().equals(organisationId); + && !lesson.getOrganisation().getOrganisationId().equals(currentOrganisationId); if (isLessonCopyRequired) { // clone lesson Integer creatorId = lesson.getUser().getUserId(); - Long newLessonId = monitoringService.cloneLesson(customLessonId, creatorId, true, true, null, null, - orgMap.getOrganisation()); + Long newLessonId = monitoringService.cloneLesson(lesson.getLessonId(), creatorId, true, true, null, null, + currentOrgMap.getOrganisation()); // store information which extServer has started the lesson extLessonMap = integrationService.createExtServerLessonMap(newLessonId, extServer); @@ -226,7 +234,10 @@ request.setAttribute(BasicLTIConstants.LTI_MESSAGE_TYPE, ltiMessageType); request.setAttribute(LtiUtils.CONTENT_ITEM_RETURN_URL, contentItemReturnUrl); request.setAttribute("title", request.getParameter("title")); - request.setAttribute("desc", request.getParameter("text").replaceAll("\\<[^>]*>", "")); + //text parameter can be null in case of BB server + String description = request.getParameter("text") == null ? "" + : request.getParameter("text").replaceAll("\\<[^>]*>", ""); + request.setAttribute("desc", description); request.setAttribute("data", request.getParameter("data")); } @@ -374,7 +385,20 @@ // "custom" : { // "chapter" : "12", // "section" : "3" -// } +// }, +// "lineItem":{ +// "reportingMethod":"http://purl.imsglobal.org/ctx/lis/v2p1/Result#totalScore", +// "@type":"LineItem", +// "assignedActivity":{ +// "activityId":"a-9334df-33" +// }, +// "scoreConstraints":{ +// "@type":"NumericLimits", +// "normalMaximum":45, +// "extraCreditMaximum":5, +// "totalMaximum":50 +// } +// }, // } // ] // } @@ -388,14 +412,28 @@ customJSON.put("iscontentitemselection", "true"); contentItemJSON.set("custom", customJSON); + ObjectNode lineItem = JsonNodeFactory.instance.objectNode(); + lineItem.put("reportingMethod", "http://purl.imsglobal.org/ctx/lis/v2p1/Result#totalScore"); + lineItem.put("@type", "LineItem"); + ObjectNode assignedActivity = JsonNodeFactory.instance.objectNode(); + assignedActivity.put("activityId", key + "_" + lesson.getLessonId().toString()); + lineItem.set("assignedActivity", assignedActivity); + ObjectNode scoreConstraints = JsonNodeFactory.instance.objectNode(); + scoreConstraints.put("@type", "NumericLimits"); + scoreConstraints.put("normalMaximum", 10); + scoreConstraints.put("extraCreditMaximum", 1); + scoreConstraints.put("totalMaximum", 100); + lineItem.set("scoreConstraints", scoreConstraints); + contentItemJSON.set("lineItem", lineItem); + ArrayNode graph = JsonNodeFactory.instance.arrayNode(); graph.add(contentItemJSON); ObjectNode responseJSON = JsonNodeFactory.instance.objectNode(); responseJSON.set("@graph", graph); responseJSON.put("@context", "http://purl.imsglobal.org/ctx/lti/v1/ContentItem"); - String content_items = URLEncoder.encode(responseJSON.toString(), "UTF-8"); + String content_items = responseJSON.toString();//URLEncoder.encode(responseJSON.toString(), "UTF-8"); // content_items = content_items.replace("\"", "%22"); // content_items = content_items.replace("'", "%27"); properties.put("content_items", content_items);