getAllToolConsumers();
void saveExtServerOrgMap(ExtServerOrgMap map);
@@ -143,6 +172,24 @@
void saveExtServerToolAdapterMap(ExtServerToolAdapterMap map);
void deleteExtServerToolAdapterMap(ExtServerToolAdapterMap map);
+
+ /**
+ * Creates new ExtServerLessonMap object. Method is suitable for creating lessons via integration servers.
+ *
+ * @param lessonId
+ * @param extServer
+ */
+ void createExtServerLessonMap(Long lessonId, ExtServerOrgMap extServer);
+
+ /**
+ * Creates new ExtServerLessonMap object. Method is suitable for creating lessons via LTI tool consumers as long as
+ * they provide resourceLinkId as a parameter and not lessonId.
+ *
+ * @param lessonId
+ * @param resourceLinkId resource_link_id parameter sent by LTI tool consumer
+ * @param extServer
+ */
+ void createExtServerLessonMap(Long lessonId, String resourceLinkId, ExtServerOrgMap extServer);
/**
* Checks whether the lesson was created from extServer and returns lessonFinishCallbackUrl if it's not blank.
Index: lams_common/src/java/org/lamsfoundation/lams/integration/service/IntegrationService.java
===================================================================
diff -u -r445a4dce899e983de37328df3219fbed8ab78f9d -r700058382334580a4a183013cfaad01e0b0ca51a
--- lams_common/src/java/org/lamsfoundation/lams/integration/service/IntegrationService.java (.../IntegrationService.java) (revision 445a4dce899e983de37328df3219fbed8ab78f9d)
+++ lams_common/src/java/org/lamsfoundation/lams/integration/service/IntegrationService.java (.../IntegrationService.java) (revision 700058382334580a4a183013cfaad01e0b0ca51a)
@@ -33,6 +33,7 @@
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
+import java.security.GeneralSecurityException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
@@ -44,6 +45,9 @@
import org.apache.log4j.Logger;
import org.apache.tomcat.util.json.JSONArray;
import org.apache.tomcat.util.json.JSONObject;
+import org.imsglobal.pox.IMSPOXRequest;
+import org.lamsfoundation.lams.gradebook.GradebookUserLesson;
+import org.lamsfoundation.lams.gradebook.service.IGradebookService;
import org.lamsfoundation.lams.integration.ExtCourseClassMap;
import org.lamsfoundation.lams.integration.ExtServerLessonMap;
import org.lamsfoundation.lams.integration.ExtServerOrgMap;
@@ -57,6 +61,7 @@
import org.lamsfoundation.lams.integration.util.LoginRequestDispatcher;
import org.lamsfoundation.lams.lesson.Lesson;
import org.lamsfoundation.lams.lesson.service.ILessonService;
+import org.lamsfoundation.lams.tool.service.ILamsCoreToolService;
import org.lamsfoundation.lams.usermanagement.AuthenticationMethod;
import org.lamsfoundation.lams.usermanagement.Organisation;
import org.lamsfoundation.lams.usermanagement.OrganisationState;
@@ -72,6 +77,8 @@
import org.lamsfoundation.lams.util.ValidationUtil;
import org.lamsfoundation.lams.util.WebUtil;
+import oauth.signpost.exception.OAuthException;
+
/**
*
* View Source
@@ -83,24 +90,35 @@
private static Logger log = Logger.getLogger(IntegrationService.class);
+ private IGradebookService gradebookService;
private IUserManagementService service;
private ILessonService lessonService;
+ private ILamsCoreToolService toolService;
+ /**
+ * Returns integration server or LTI tool consumer by its human-entered server key/server id.
+ *
+ * @param serverId server key/server id
+ * @param isIntegrationServer true in case this is an integration server, false - LTI tool consumer
+ * @return
+ */
@Override
public ExtServerOrgMap getExtServerOrgMap(String serverId) {
- List list = service.findByProperty(ExtServerOrgMap.class, "serverid", serverId);
+ Map properties = new HashMap();
+ properties.put("serverid", serverId);
+ List list = service.findByProperties(ExtServerOrgMap.class, properties);
if (list == null || list.size() == 0) {
return null;
} else {
- return (ExtServerOrgMap) list.get(0);
+ return list.get(0);
}
}
@Override
- public ExtCourseClassMap getExtCourseClassMap(Integer extServerOrgMapId, String extCourseId) {
+ public ExtCourseClassMap getExtCourseClassMap(Integer sid, String extCourseId) {
Map properties = new HashMap();
properties.put("courseid", extCourseId);
- properties.put("extServerOrgMap.sid", extServerOrgMapId);
+ properties.put("extServerOrgMap.sid", sid);
List list = service.findByProperties(ExtCourseClassMap.class, properties);
if (list == null || list.size() == 0) {
@@ -500,9 +518,18 @@
}
@Override
- public List getAllExtServerOrgMaps() {
- return service.findAll(ExtServerOrgMap.class);
+ public List getAllExtServerOrgMaps() {
+ Map properties = new HashMap();
+ properties.put("serverTypeId", ExtServerOrgMap.INTEGRATION_SERVER_TYPE);
+ return service.findByProperties(ExtServerOrgMap.class, properties);
}
+
+ @Override
+ public List getAllToolConsumers() {
+ Map properties = new HashMap();
+ properties.put("serverTypeId", ExtServerOrgMap.LTI_CONSUMER_SERVER_TYPE);
+ return service.findByProperties(ExtServerOrgMap.class, properties);
+ }
@Override
@SuppressWarnings("unchecked")
@@ -547,9 +574,16 @@
return (ExtServerOrgMap) service.findById(ExtServerOrgMap.class, sid);
}
+ @Override
public void createExtServerLessonMap(Long lessonId, ExtServerOrgMap extServer) {
+ createExtServerLessonMap(lessonId, null, extServer);;
+ }
+
+ @Override
+ public void createExtServerLessonMap(Long lessonId, String resourceLinkId, ExtServerOrgMap extServer) {
ExtServerLessonMap map = new ExtServerLessonMap();
map.setLessonId(lessonId);
+ map.setResourceLinkId(resourceLinkId);
map.setExtServer(extServer);
service.save(map);
}
@@ -567,23 +601,71 @@
// checks whether the lesson was created from extServer and whether it has lessonFinishCallbackUrl setting
if (extServerLesson != null
&& StringUtils.isNotBlank(extServerLesson.getExtServer().getLessonFinishUrl())) {
- ExtServerOrgMap serverMap = extServerLesson.getExtServer();
+ ExtServerOrgMap server = extServerLesson.getExtServer();
- ExtUserUseridMap extUserUseridMap = getExtUserUseridMapByUserId(serverMap, user.getUserId());
- if (extUserUseridMap != null) {
- String extUsername = extUserUseridMap.getExtUsername();
+ ExtUserUseridMap extUser = getExtUserUseridMapByUserId(server, user.getUserId());
+ if (extUser != null) {
+ String extUsername = extUser.getExtUsername();
- // construct real lessonFinishCallbackUrl
- lessonFinishCallbackUrl = serverMap.getLessonFinishUrl();
- String timestamp = Long.toString(new Date().getTime());
- String hash = hash(serverMap, extUsername, timestamp);
- String encodedExtUsername = URLEncoder.encode(extUsername, "UTF8");
+ //return URL in case of integration server
+ if (server.getServerTypeId().equals(ExtServerOrgMap.INTEGRATION_SERVER_TYPE)) {
+ // construct real lessonFinishCallbackUrl
+ lessonFinishCallbackUrl = server.getLessonFinishUrl();
+ String timestamp = Long.toString(new Date().getTime());
+ String hash = hash(server, extUsername, timestamp);
+ String encodedExtUsername = URLEncoder.encode(extUsername, "UTF8");
- // set the values for the parameters
- lessonFinishCallbackUrl = lessonFinishCallbackUrl.replaceAll("%username%", encodedExtUsername)
- .replaceAll("%lessonid%", lessonId.toString()).replaceAll("%timestamp%", timestamp)
- .replaceAll("%hash%", hash);
- log.debug(lessonFinishCallbackUrl);
+ // set the values for the parameters
+ lessonFinishCallbackUrl = lessonFinishCallbackUrl.replaceAll("%username%", encodedExtUsername)
+ .replaceAll("%lessonid%", lessonId.toString()).replaceAll("%timestamp%", timestamp)
+ .replaceAll("%hash%", hash);
+ log.debug(lessonFinishCallbackUrl);
+
+ // in case of LTI Tool Consumer - create a new thread to report score back to LMS (in order to do this task in parallel not to slow down later work)
+ } else {
+
+ // calculate lesson's MaxPossibleMark
+ Long lessonMaxPossibleMark = toolService.getLessonMaxPossibleMark(lesson);
+ GradebookUserLesson gradebookUserLesson = gradebookService.getGradebookUserLesson(lessonId,
+ user.getUserId());
+ Double userTotalMark = (gradebookUserLesson == null) || (gradebookUserLesson.getMark() == null)
+ ? null : gradebookUserLesson.getMark();
+
+ final String lessonFinishUrl = server.getLessonFinishUrl();
+ if (userTotalMark != null && StringUtils.isNotBlank(lessonFinishUrl)) {
+
+ Double score = lessonMaxPossibleMark.equals(0L) ? 0 : userTotalMark / lessonMaxPossibleMark;
+ final String scoreStr = (userTotalMark == null) || lessonMaxPossibleMark.equals(0L) ? ""
+ : score.toString();
+
+ final String serverKey = server.getServerid();
+ final String serverSecret = server.getServerkey();
+ final String tcGradebookId = extUser.getTcGradebookId();
+ final ExtUserUseridMap extUserFinal = extUser;
+
+ Thread preaddLearnersMonitorsThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ //send Request directly
+ try {
+ IMSPOXRequest.sendReplaceResult(lessonFinishUrl, serverKey, serverSecret,
+ tcGradebookId, scoreStr);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } catch (OAuthException e) {
+ throw new RuntimeException(e);
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
+ log.debug("Score (" + scoreStr + ") posted to Tool Consumer (serverKey:" + serverKey
+ + "). extUsername:" + extUserFinal.getExtUsername());
+ }
+ }, "LAMS_sendScoresLTI_thread");
+ preaddLearnersMonitorsThread.start();
+
+ }
+ }
+
}
}
}
@@ -743,8 +825,19 @@
properties.put("extServerOrgMap.sid", sid);
properties.put("organisation.organisationId", lesson.getOrganisation().getOrganisationId());
List list = service.findByProperties(ExtCourseClassMap.class, properties);
- return list == null || list.isEmpty() ? null : list.get(0);
+
+ return (list == null || list.isEmpty()) ? null : list.get(0);
}
+
+ @Override
+ public ExtServerLessonMap getLtiConsumerLesson(String serverId, String resourceLinkId) {
+ Map properties = new HashMap();
+ properties.put("extServer.serverid", serverId);
+ properties.put("resourceLinkId", resourceLinkId);
+ List list = service.findByProperties(ExtServerLessonMap.class, properties);
+
+ return (list == null || list.isEmpty()) ? null : list.get(0);
+ }
private ExtServerLessonMap getExtServerLessonMap(Long lessonId) {
List list = service.findByProperty(ExtServerLessonMap.class, "lessonId", lessonId);
@@ -787,4 +880,13 @@
public ILessonService getLessonService() {
return lessonService;
}
+
+ public void setGradebookService(IGradebookService gradebookService) {
+ this.gradebookService = gradebookService;
+ }
+
+ public void setToolService(ILamsCoreToolService toolService) {
+ this.toolService = toolService;
+ }
+
}
\ No newline at end of file
Index: lams_common/src/java/org/lamsfoundation/lams/integration/util/LtiUtils.java
===================================================================
diff -u
--- lams_common/src/java/org/lamsfoundation/lams/integration/util/LtiUtils.java (revision 0)
+++ lams_common/src/java/org/lamsfoundation/lams/integration/util/LtiUtils.java (revision 700058382334580a4a183013cfaad01e0b0ca51a)
@@ -0,0 +1,79 @@
+package org.lamsfoundation.lams.integration.util;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class LtiUtils {
+
+ public static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key";
+
+ /**
+ * Return true
if the user is an administrator.
+ * {Method added by LAMS}
+ *
+ * @return true
if the user has a role of administrator
+ */
+ public static boolean isAdmin(String roles) {
+ List rolesToSearchFor = new LinkedList();
+ rolesToSearchFor.add("urn:lti:role:ims/lis/Administrator");
+ rolesToSearchFor.add("urn:lti:sysrole:ims/lis/SysAdmin");
+ rolesToSearchFor.add("urn:lti:sysrole:ims/lis/Administrator");
+ rolesToSearchFor.add("urn:lti:instrole:ims/lis/Administrator");
+
+ return hasRole(roles, rolesToSearchFor);
+ }
+
+ /**
+ * Return true
if the user is staff.
+ * {Method added by LAMS}
+ *
+ * @return true
if the user has a role of instructor, contentdeveloper or teachingassistant
+ */
+ public static boolean isStaff(String roles) {
+ List rolesToSearchFor = new LinkedList();
+ rolesToSearchFor.add("urn:lti:role:ims/lis/Instructor");
+ rolesToSearchFor.add("urn:lti:role:ims/lis/ContentDeveloper");
+ rolesToSearchFor.add("urn:lti:role:ims/lis/TeachingAssistant");
+
+ return hasRole(roles, rolesToSearchFor);
+ }
+
+ /**
+ * Return true
if the user is a learner.
+ * {Method added by LAMS}
+ *
+ * @return true
if the user has a role of learner
+ */
+ public static boolean isLearner(String roles) {
+ List rolesToSearchFor = new LinkedList();
+ rolesToSearchFor.add("urn:lti:role:ims/lis/Learner");
+
+ return hasRole(roles, rolesToSearchFor);
+ }
+
+ /*
+ * Check whether the user has a specified role name.
+ * {Method added by LAMS}
+ *
+ * @param role
+ * Name of role
+ *
+ * @return true
if the user has the specified role
+ */
+ private static boolean hasRole(String roles, List rolesToSearchFor) {
+ String[] roleArray = roles.split(",");
+
+ boolean hasRole = false;
+ for (String role : roleArray) {
+ for (String roleToSearchFor : rolesToSearchFor) {
+ if (role.equals(roleToSearchFor)) {
+ hasRole = true;
+ break;
+ }
+ }
+ }
+
+ return hasRole;
+ }
+
+}
Index: lams_common/src/java/org/lamsfoundation/lams/integrationContext.xml
===================================================================
diff -u -rd27ed028b0e16c263776418b7bce22099fed4eed -r700058382334580a4a183013cfaad01e0b0ca51a
--- lams_common/src/java/org/lamsfoundation/lams/integrationContext.xml (.../integrationContext.xml) (revision d27ed028b0e16c263776418b7bce22099fed4eed)
+++ lams_common/src/java/org/lamsfoundation/lams/integrationContext.xml (.../integrationContext.xml) (revision 700058382334580a4a183013cfaad01e0b0ca51a)
@@ -2,8 +2,10 @@
-
+
+
+
Index: lams_common/src/java/org/lamsfoundation/lams/tool/service/ILamsCoreToolService.java
===================================================================
diff -u -rde44e2e970afac102177634d2e9106919f65f773 -r700058382334580a4a183013cfaad01e0b0ca51a
--- lams_common/src/java/org/lamsfoundation/lams/tool/service/ILamsCoreToolService.java (.../ILamsCoreToolService.java) (revision de44e2e970afac102177634d2e9106919f65f773)
+++ lams_common/src/java/org/lamsfoundation/lams/tool/service/ILamsCoreToolService.java (.../ILamsCoreToolService.java) (revision 700058382334580a4a183013cfaad01e0b0ca51a)
@@ -281,6 +281,14 @@
* @return activity's max possible mark if available, null otherwise
*/
Long getActivityMaxPossibleMark(ToolActivity activity);
+
+ /**
+ * Calculates lesson's maximum possible mark by adding up all activities max marks.
+ *
+ * @param lesson
+ * @return
+ */
+ Long getLessonMaxPossibleMark(Lesson lesson);
/**
* Update the tool session data.
Index: lams_common/src/java/org/lamsfoundation/lams/tool/service/LamsCoreToolService.java
===================================================================
diff -u -rde44e2e970afac102177634d2e9106919f65f773 -r700058382334580a4a183013cfaad01e0b0ca51a
--- lams_common/src/java/org/lamsfoundation/lams/tool/service/LamsCoreToolService.java (.../LamsCoreToolService.java) (revision de44e2e970afac102177634d2e9106919f65f773)
+++ lams_common/src/java/org/lamsfoundation/lams/tool/service/LamsCoreToolService.java (.../LamsCoreToolService.java) (revision 700058382334580a4a183013cfaad01e0b0ca51a)
@@ -32,6 +32,7 @@
import org.lamsfoundation.lams.learningdesign.Activity;
import org.lamsfoundation.lams.learningdesign.ActivityEvaluation;
import org.lamsfoundation.lams.learningdesign.ToolActivity;
+import org.lamsfoundation.lams.learningdesign.dao.IActivityDAO;
import org.lamsfoundation.lams.lesson.Lesson;
import org.lamsfoundation.lams.tool.SystemTool;
import org.lamsfoundation.lams.tool.Tool;
@@ -74,6 +75,7 @@
// Instance variables
// ---------------------------------------------------------------------
private ApplicationContext context;
+ private IActivityDAO activityDAO;
private IToolSessionDAO toolSessionDAO;
private ISystemToolDAO systemToolDAO;
private ToolContentIDGenerator contentIDGenerator;
@@ -87,6 +89,10 @@
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
+
+ public void setActivityDAO(IActivityDAO activityDAO) {
+ this.activityDAO = activityDAO;
+ }
/**
* @param toolSessionDAO
@@ -559,7 +565,52 @@
}
return null;
}
+
+ @Override
+ public Long getLessonMaxPossibleMark(Lesson lesson) {
+ // calculate lesson's MaxPossibleMark
+ Set activities = getLessonActivities(lesson);
+ Long lessonMaxPossibleMark = 0L;
+ for (ToolActivity activity : activities) {
+ Long activityMaxPossibleMark = getActivityMaxPossibleMark(activity);
+ if (activityMaxPossibleMark != null) {
+ lessonMaxPossibleMark += activityMaxPossibleMark;
+ }
+ }
+ return lessonMaxPossibleMark;
+ }
+
+ /**
+ * Returns lesson tool activities. It works almost the same as lesson.getLearningDesign().getActivities() except it
+ * solves problem with first activity unable to cast to ToolActivity.
+ */
+ @SuppressWarnings("unchecked")
+ private Set getLessonActivities(Lesson lesson) {
+ Set activities = new TreeSet();
+ Set toolActivities = new TreeSet();
+ /*
+ * Hibernate CGLIB is failing to load the first activity in the sequence as a ToolActivity for some mysterious
+ * reason Causes a ClassCastException when you try to cast it, even if it is a ToolActivity.
+ *
+ * THIS IS A HACK to retrieve the first tool activity manually so it can be cast as a ToolActivity - if it is
+ * one
+ */
+ Activity firstActivity = activityDAO
+ .getActivityByActivityId(lesson.getLearningDesign().getFirstActivity().getActivityId());
+ activities.add(firstActivity);
+ activities.addAll(lesson.getLearningDesign().getActivities());
+
+ for (Activity activity : activities) {
+ if (activity instanceof ToolActivity) {
+ ToolActivity toolActivity = (ToolActivity) activity;
+ toolActivities.add(toolActivity);
+ }
+ }
+
+ return toolActivities;
+ }
+
@Override
public void updateToolSession(ToolSession toolSession) {
toolSessionDAO.updateToolSession(toolSession);
Index: lams_common/src/java/org/lamsfoundation/lams/toolApplicationContext.xml
===================================================================
diff -u -rde44e2e970afac102177634d2e9106919f65f773 -r700058382334580a4a183013cfaad01e0b0ca51a
--- lams_common/src/java/org/lamsfoundation/lams/toolApplicationContext.xml (.../toolApplicationContext.xml) (revision de44e2e970afac102177634d2e9106919f65f773)
+++ lams_common/src/java/org/lamsfoundation/lams/toolApplicationContext.xml (.../toolApplicationContext.xml) (revision 700058382334580a4a183013cfaad01e0b0ca51a)
@@ -45,6 +45,7 @@
+