Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/ConfigServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GradebookServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GradebookSyncFixServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GradebookSyncServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/GroupDataServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LamsLearningDesignDeleteServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LamsLearningDesignServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LearnerMonitorServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LessonManagerServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/LinkToolsServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/OpenLamsPageServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/RenderDesignImageServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/StartLessonAjaxServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/UpdateServerUrlServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 7b88f06d088a1787ab489b521c8d20480096a9b0 refers to a dead (removed) revision in file `lams_bb_integration/src/org/lamsfoundation/ld/integration/blackboard/UserDataServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/ConfigServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/ConfigServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/ConfigServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,125 @@ +package org.lamsfoundation.ld.integration.servlet; + +import java.io.IOException; +import java.text.ParseException; +import java.util.Properties; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.ParserConfigurationException; + +import org.lamsfoundation.ld.integration.util.LamsPluginUtil; +import org.xml.sax.SAXException; + +import blackboard.base.InitializationException; +import blackboard.data.ValidationException; +import blackboard.persist.PersistenceException; +import blackboard.platform.BbServiceException; +import blackboard.platform.plugin.PlugInException; +import blackboard.platform.plugin.PlugInUtil; + +/** + * Handles displaying and modification of the LAMS BB plugin's config settings. + */ +@SuppressWarnings("serial") +public class ConfigServlet extends HttpServlet{ + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + process(request, response); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + process(request, response); + } + + private void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + // SECURITY! + // Authorise current user for System Admin (automatic redirect) + try { + if (!PlugInUtil.authorizeForSystemAdmin(request, response)) + return; + } catch (PlugInException e) { + throw new RuntimeException(e); + } + + try { + String method = request.getParameter("method"); + if (method.equals("showConfigSettings")) { + showConfigSettings(request, response); + + } else if (method.equals("saveConfigSettings")) { + saveConfigSettings(request, response); + } + + } catch (InitializationException e) { + throw new ServletException(e); + } catch (BbServiceException e) { + throw new ServletException(e); + } catch (PersistenceException e) { + throw new ServletException(e); + } catch (ParseException e) { + throw new ServletException(e); + } catch (ValidationException e) { + throw new ServletException(e); + } catch (ParserConfigurationException e) { + throw new ServletException(e); + } catch (SAXException e) { + throw new ServletException(e); + } + } + + /** + * Show /admin/config.jsp page. + */ + private void showConfigSettings(HttpServletRequest request, HttpServletResponse response) + throws InitializationException, BbServiceException, PersistenceException, IOException, ServletException { + + // Get the LAMS2 Building Block properties from Blackboard (if set) + Properties properties = LamsPluginUtil.getProperties(); + + String lamsServerUrl = properties.getProperty(LamsPluginUtil.PROP_LAMS_URL, "http://"); + request.setAttribute("lamsServerUrl", lamsServerUrl); + + String lamsServerId = properties.getProperty(LamsPluginUtil.PROP_LAMS_SERVER_ID, ""); + request.setAttribute("lamsServerId", lamsServerId); + + String secretKey = properties.getProperty(LamsPluginUtil.PROP_LAMS_SECRET_KEY, ""); + request.setAttribute("secretKey", secretKey); + + String lamsServerTimeRefreshInterval = properties.getProperty(LamsPluginUtil.PROP_LAMS_SERVER_TIME_REFRESH_INTERVAL); + request.setAttribute("lamsServerTimeRefreshInterval", lamsServerTimeRefreshInterval); + + request.getRequestDispatcher("/admin/config.jsp").forward(request, response); + } + + /** + * Saves modified configuration settings. + */ + private void saveConfigSettings(HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException, PersistenceException, ParseException, ValidationException, + ParserConfigurationException, SAXException { + + // Get the properties object + Properties properties = LamsPluginUtil.getProperties(); + + // Get the LAMS2 Building Block properties from the request + String lamsServerUrl = request.getParameter("lamsServerUrl"); + String lamsServerId = request.getParameter("lamsServerId"); + String lamsSecretKey = request.getParameter("lamsSecretKey"); + String lamsServerTimeRefreshInterval = request.getParameter("lamsServerTimeRefreshInterval"); + + // Save the properties to Blackboard + properties.setProperty(LamsPluginUtil.PROP_LAMS_URL, lamsServerUrl); + properties.setProperty(LamsPluginUtil.PROP_LAMS_SECRET_KEY, lamsSecretKey); + properties.setProperty(LamsPluginUtil.PROP_LAMS_SERVER_ID, lamsServerId); + properties.setProperty(LamsPluginUtil.PROP_LAMS_SERVER_TIME_REFRESH_INTERVAL, lamsServerTimeRefreshInterval); + + // Persist the properties object + LamsPluginUtil.setProperties(properties); + + request.getRequestDispatcher("/admin/configSuccess.jsp").forward(request, response); + } +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GradebookServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GradebookServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GradebookServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,260 @@ +/** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + */ +package org.lamsfoundation.ld.integration.servlet; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.ConnectException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lamsfoundation.ld.integration.Constants; +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.xml.sax.SAXException; + +import blackboard.base.InitializationException; +import blackboard.data.ValidationException; +import blackboard.data.course.CourseMembership; +import blackboard.data.gradebook.Lineitem; +import blackboard.data.gradebook.Score; +import blackboard.data.user.User; +import blackboard.persist.Id; +import blackboard.persist.KeyNotFoundException; +import blackboard.persist.PersistenceException; +import blackboard.persist.course.CourseMembershipDbLoader; +import blackboard.persist.gradebook.ScoreDbLoader; +import blackboard.persist.user.UserDbLoader; +import blackboard.platform.BbServiceException; +import blackboard.platform.BbServiceManager; +import blackboard.platform.context.ContextManager; + +/** + * Deals with Blackboard Grade Center. + */ +public class GradebookServlet extends HttpServlet { + + private static final long serialVersionUID = -3587062723412672084L; + private static Logger logger = LoggerFactory.getLogger(GradebookServlet.class); + + /** + * Receives call from Lams ab lesson completion. After that get the latest marks for this user in this lesson and stores it in DB. + */ + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + ContextManager ctxMgr = null; + + 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(); + + // get Parameter values + String userName = request.getParameter(Constants.PARAM_USER_ID); + String timeStamp = request.getParameter(Constants.PARAM_TIMESTAMP); + String hash = request.getParameter(Constants.PARAM_HASH); + String lamsLessonIdParam = request.getParameter(Constants.PARAM_LESSON_ID); + + // check parameters + if (userName == null || timeStamp == null || hash == null || lamsLessonIdParam == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "missing expected parameters"); + return; + } + + //check user rights + String secretKey = LamsPluginUtil.getServerSecretKey(); + String serverId = LamsPluginUtil.getServerId(); + if (!LamsSecurityUtil.sha1( + timeStamp.toLowerCase() + userName.toLowerCase() + serverId.toLowerCase() + + secretKey.toLowerCase()).equals(hash)) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "authentication failed"); + return; + } + + // get user list, but no role info since there are no course info + User user = userLoader.loadByUserName(userName); + if (user == null) { + throw new ServletException("User not found with userName:" + userName); + } + Id userId = user.getId(); + + //allow lessonComplete.jsp on LAMS side to make an Ajax call to this servlet + String serverUrlWithLamsWord = LamsPluginUtil.getServerUrl(); + URI uri = new URI(serverUrlWithLamsWord); + //strip out '/lams/' from the end of the URL + String serverUrl = serverUrlWithLamsWord.lastIndexOf(uri.getPath()) == -1 ? serverUrlWithLamsWord + : serverUrlWithLamsWord.substring(0, serverUrlWithLamsWord.lastIndexOf(uri.getPath())); + response.addHeader("Access-Control-Allow-Origin", serverUrl); + + Lineitem lineitem = LineitemUtil.getLineitem(userId, lamsLessonIdParam, false); + //notifying LAMS that servlet finished its work without exceptions but the score wasn't saved + if (lineitem == null) { + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.write("No Lineitem object found"); + return; + } + // 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."); + + String getLamsMarkURL = LamsPluginUtil.getServerUrl() + "/services/xml/LessonManager?" + + LamsSecurityUtil.generateAuthenticateParameters(userName) + + "&method=gradebookMarksUser" + + "&lsId=" + lamsLessonIdParam + + "&outputsUser=" + URLEncoder.encode(userName, "UTF8"); + URL url = new URL(getLamsMarkURL); + URLConnection conn = url.openConnection(); + if (!(conn instanceof HttpURLConnection)) { + throw new RuntimeException("Unable to open connection to: " + getLamsMarkURL); + } + HttpURLConnection httpConn = (HttpURLConnection) conn; + if (httpConn.getResponseCode() != HttpURLConnection.HTTP_OK) { + String errorMsg = "HTTP Response Code: " + httpConn.getResponseCode() + ", HTTP Response Message: " + + httpConn.getResponseMessage(); + logger.error(errorMsg); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, errorMsg); + return; + } + + // InputStream is = url.openConnection().getInputStream(); + InputStream is = conn.getInputStream(); + // parse xml response + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document document = db.parse(is); + + Node lesson = document.getDocumentElement().getFirstChild(); + Node learnerResult = lesson.getFirstChild(); + + // store new score + CourseMembership courseMembership = courseMembershipLoader.loadByCourseAndUserId(lineitem.getCourseId(), + userId); + Score currentScore = null; + try { + currentScore = scoreLoader.loadByCourseMembershipIdAndLineitemId(courseMembership.getId(), + lineitem.getId()); + } catch (KeyNotFoundException c) { + currentScore = new Score(); + currentScore.setLineitemId(lineitem.getId()); + currentScore.setCourseMembershipId(courseMembership.getId()); + } + + //updates and persists currentScore in the DB + LineitemUtil.updateScoreBasedOnLamsResponse(lesson, learnerResult, currentScore); + + //notifying LAMS that score has been stored successfully + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.write("OK"); + + //the following paragraph is kept due to the Ernie's request to keep it for potential usage in the future +// NodeList activities = document.getDocumentElement().getFirstChild().getChildNodes(); +// +// float maxResult = 0; +// float userResult = 0; +// for (int i = 0; i < activities.getLength(); i++) { +// Node activity = activities.item(i); +// +// for (int j = 0; j < activity.getChildNodes().getLength(); j++) { +// +// Node toolOutput = activity.getChildNodes().item(j); +// String toolOutputName = toolOutput.getAttributes().getNamedItem("name").getNodeValue(); +// // The only numeric outputs we get from LAMS are for the MCQ and Assessment activities +// // learner.total.score = Assessment +// // learner.mark = MCQ +// if ("learner.mark".equals(toolOutputName) || "learner.total.score".equals(toolOutputName)) { +// String userResultStr = toolOutput.getAttributes().getNamedItem("output").getNodeValue(); +// String maxResultStr = toolOutput.getAttributes().getNamedItem("marksPossible").getNodeValue(); +// +// userResult += Float.parseFloat(userResultStr); +// maxResult += Float.parseFloat(maxResultStr); +// } +// } +// +// } + + } catch (MalformedURLException e) { + throw new ServletException("Unable to get LAMS learning designs, bad URL: " + + ", please check lams.properties", e); + } catch (IllegalStateException e) { + throw new ServletException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (ConnectException e) { + throw new ServletException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (UnsupportedEncodingException e) { + throw new ServletException(e); + } catch (IOException e) { + throw new ServletException(e); + } catch (ParserConfigurationException e) { + throw new ServletException(e); + } catch (SAXException e) { + throw new ServletException(e); + } catch (URISyntaxException e) { + throw new ServletException(e); + } catch (PersistenceException e) { + throw new ServletException(e); + } catch (ValidationException e) { + throw new ServletException(e); + } catch (InitializationException e) { + throw new ServletException(e); + } catch (BbServiceException e) { + throw new ServletException(e); + } finally { + // make sure context is released + if (ctxMgr != null) { + ctxMgr.releaseContext(); + } + } + + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + doGet(req, resp); + } + +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GradebookSyncFixServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GradebookSyncFixServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GradebookSyncFixServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,220 @@ +/**************************************************************** + * 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.servlet; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +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.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.lamsfoundation.ld.integration.Constants; +import org.lamsfoundation.ld.integration.util.LamsBuildingBlockException; +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.base.InitializationException; +import blackboard.data.course.CourseMembership; +import blackboard.data.gradebook.Lineitem; +import blackboard.data.gradebook.Score; +import blackboard.persist.Id; +import blackboard.persist.PersistenceException; +import blackboard.persist.course.CourseMembershipDbLoader; +import blackboard.persist.gradebook.ScoreDbLoader; +import blackboard.persist.gradebook.ScoreDbPersister; +import blackboard.platform.BbServiceException; +import blackboard.platform.BbServiceManager; +import blackboard.platform.context.Context; +import blackboard.platform.context.ContextManager; + +/** + * Fixes issues created by GradebookSyncServlet, namely, removes marks of users that had not completed the lesson. + */ +@SuppressWarnings("serial") +public class GradebookSyncFixServlet extends HttpServlet { + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + ContextManager ctxMgr = null; + int numberDeletedScores = 0; + + try { + // get Blackboard context + ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + Context ctx = ctxMgr.setContext(request); + 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); + // validate method parameter + if (lamsLessonIdParam == null) { + throw new RuntimeException("Requred parameters missing. lsid=" + lamsLessonIdParam); + } + + Lineitem lineitem = LineitemUtil.getLineitem(ctx.getUserId(), lamsLessonIdParam, true); + if (lineitem == null) { + throw new ServletException("Lineitem was not found for userId:" + ctx.getUserId() + " and lamsLessonId:" + lamsLessonIdParam); + } + + String username = ctx.getUser().getUserName(); + String serviceURL = LamsPluginUtil.getServerUrl() + "/services/xml/LessonManager?" + + LamsSecurityUtil.generateAuthenticateParameters(username) + + "&method=gradebookMarksLesson&lsId=" + lamsLessonIdParam; + + URL url = new URL(serviceURL); + URLConnection conn = url.openConnection(); + if (!(conn instanceof HttpURLConnection)) { + throw new RuntimeException("Unable to open connection to: " + serviceURL); + } + + HttpURLConnection httpConn = (HttpURLConnection) conn; + + if (httpConn.getResponseCode() != HttpURLConnection.HTTP_OK) { + String errorMsg = "HTTP Response Code: " + httpConn.getResponseCode() + ", HTTP Response Message: " + + httpConn.getResponseMessage(); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.getWriter().write(errorMsg); + return; + } + + InputStream is = conn.getInputStream(); + + // parse xml response + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document document = db.parse(is); + Node lesson = document.getDocumentElement().getFirstChild(); + NodeList learnerResults = lesson.getChildNodes(); + + //in order to reduce DB queries we get scores and courseMemberships all at once + List dbScores = scoreLoader.loadByLineitemId(lineitem.getId()); + List courseMemberships = courseMemLoader.loadByCourseId(lineitem.getCourseId(), null, true); + + //removes marks of users that had not completed the lesson + for (CourseMembership courseMembership: courseMemberships) { + Id courseMembershipId = courseMembership.getId(); + String userName = courseMembership.getUser().getUserName(); + + // find old score + Score currentScore = null; + for (Score dbScore : dbScores) { + if (dbScore.getCourseMembershipId().equals(courseMembershipId)) { + currentScore = dbScore; + break; + } + } + + // determine whether user had finished the lesson + boolean isUserHadFinishedLesson = false; + for (int i = 0; i < learnerResults.getLength(); i++) { + Node learnerResult = learnerResults.item(i); + + String extUsername = learnerResult.getAttributes().getNamedItem("extUsername").getNodeValue(); + + if (userName.equals(extUsername)) { + isUserHadFinishedLesson = true; + break; + } + } + + //remove marks of the user that had not completed the lesson. + if ((currentScore != null) && !isUserHadFinishedLesson) { + scorePersister.deleteById(currentScore.getId()); + + //calculate how many marks are removed + numberDeletedScores++; + } + } + + + } 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); + } catch (IllegalStateException e) { + throw new ServletException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (ConnectException e) { + throw new ServletException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (UnsupportedEncodingException e) { + throw new ServletException(e); + } catch (IOException e) { + throw new ServletException(e); + } catch (ParserConfigurationException e) { + throw new ServletException(e); + } catch (SAXException e) { + throw new ServletException(e); + } catch (PersistenceException e) { + throw new ServletException(e); + } catch (InitializationException e) { + throw new ServletException(e); + } catch (BbServiceException e) { + throw new ServletException(e); + } finally { + // make sure context is released + if (ctxMgr != null) { + ctxMgr.releaseContext(); + } + } + + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.write("Complete! " + numberDeletedScores + " marks have been removed from Blackboard Gradecenter."); + + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + doGet(req, resp); + } + +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GradebookSyncServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GradebookSyncServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GradebookSyncServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,238 @@ +/**************************************************************** + * 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.servlet; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +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.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lamsfoundation.ld.integration.Constants; +import org.lamsfoundation.ld.integration.util.LamsBuildingBlockException; +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.base.InitializationException; +import blackboard.data.ValidationException; +import blackboard.data.course.CourseMembership; +import blackboard.data.gradebook.Lineitem; +import blackboard.data.gradebook.Score; +import blackboard.persist.Id; +import blackboard.persist.PersistenceException; +import blackboard.persist.course.CourseMembershipDbLoader; +import blackboard.persist.gradebook.ScoreDbLoader; +import blackboard.platform.BbServiceException; +import blackboard.platform.BbServiceManager; +import blackboard.platform.context.Context; +import blackboard.platform.context.ContextManager; +import blackboard.util.StringUtil; + +/** + * Monitor on BB side calls this servlet to syncronize lesson marks with LAMS Gradebook. + */ +public class GradebookSyncServlet extends HttpServlet { + + private static final long serialVersionUID = -3587062723412672084L; + private static Logger logger = LoggerFactory.getLogger(GradebookSyncServlet.class); + + /** + * Monitor on BB side calls this servlet to syncronize lesson marks with LAMS Gradebook. After that get the latest + * marks for this user in this lesson and stores it in DB. + */ + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + ContextManager ctxMgr = null; + int numberUpdatedScores = 0; + + try { + // get Blackboard context + ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + Context ctx = ctxMgr.setContext(request); + CourseMembershipDbLoader courseMemLoader = CourseMembershipDbLoader.Default.getInstance(); + ScoreDbLoader scoreLoader = ScoreDbLoader.Default.getInstance(); + + // get Parameter values + String lamsLessonIdParam = request.getParameter(Constants.PARAM_LESSON_ID); + // validate method parameter + if (lamsLessonIdParam == null) { + throw new RuntimeException("Requred parameters missing. lsid=" + lamsLessonIdParam); + } + + Lineitem lineitem = LineitemUtil.getLineitem(ctx.getUserId(), lamsLessonIdParam, true); + if (lineitem == null) { + throw new ServletException("Lineitem was not found for userId:" + ctx.getUserId() + " and lamsLessonId:" + lamsLessonIdParam); + } + + String username = ctx.getUser().getUserName(); + String serviceURL = LamsPluginUtil.getServerUrl() + "/services/xml/LessonManager?" + + LamsSecurityUtil.generateAuthenticateParameters(username) + + "&method=gradebookMarksLesson&lsId=" + lamsLessonIdParam; + + URL url = new URL(serviceURL); + URLConnection conn = url.openConnection(); + if (!(conn instanceof HttpURLConnection)) { + throw new RuntimeException("Unable to open connection to: " + serviceURL); + } + + HttpURLConnection httpConn = (HttpURLConnection) conn; + + if (httpConn.getResponseCode() != HttpURLConnection.HTTP_OK) { + String errorMsg = "HTTP Response Code: " + httpConn.getResponseCode() + ", HTTP Response Message: " + + httpConn.getResponseMessage(); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.getWriter().write(errorMsg); + return; + } + + InputStream is = conn.getInputStream(); + + // parse xml response + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document document = db.parse(is); + Node lesson = document.getDocumentElement().getFirstChild(); + NodeList learnerResults = lesson.getChildNodes(); + + //in order to reduce DB queries we get scores and courseMemberships all at once + List dbScores = scoreLoader.loadByLineitemId(lineitem.getId()); + List courseMemberships = courseMemLoader.loadByCourseId(lineitem.getCourseId(), null, true); + + //update all marks + for (CourseMembership courseMembership: courseMemberships) { + Id courseMembershipId = courseMembership.getId(); + String userName = courseMembership.getUser().getUserName(); + + // find old score + Score currentScore = null; + for (Score dbScore : dbScores) { + if (dbScore.getCourseMembershipId().equals(courseMembershipId)) { + currentScore = dbScore; + break; + } + } + + //find new score + for (int i = 0; i < learnerResults.getLength(); i++) { + Node learnerResult = learnerResults.item(i); + + String extUsername = learnerResult.getAttributes().getNamedItem("extUsername").getNodeValue(); + + if (userName.equals(extUsername)) { + + //update old score + if (currentScore == null) { + currentScore = new Score(); + currentScore.setLineitemId(lineitem.getId()); + currentScore.setCourseMembershipId(courseMembershipId); + } + + //updates and persists currentScore in the DB + double gradebookMark = LineitemUtil.updateScoreBasedOnLamsResponse(lesson, learnerResult, + currentScore); + + //calculate how many marks updated + if (StringUtil.isEmpty(currentScore.getGrade()) + || !new Double(currentScore.getGrade()).equals(new Double(gradebookMark))) { + numberUpdatedScores++; + } + + break; + } + } + } + + + } 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); + } catch (IllegalStateException e) { + throw new ServletException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (ConnectException e) { + throw new ServletException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (UnsupportedEncodingException e) { + throw new ServletException(e); + } catch (IOException e) { + throw new ServletException(e); + } catch (ParserConfigurationException e) { + throw new ServletException(e); + } catch (SAXException e) { + throw new ServletException(e); + } catch (PersistenceException e) { + throw new ServletException(e); + } catch (ValidationException e) { + throw new ServletException(e); + } catch (InitializationException e) { + throw new ServletException(e); + } catch (BbServiceException e) { + throw new ServletException(e); + } finally { + // make sure context is released + if (ctxMgr != null) { + ctxMgr.releaseContext(); + } + } + + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.write("Complete! " + numberUpdatedScores + " marks have been updated/added to Blackboard Gradecenter."); + + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + doGet(req, resp); + } + +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GroupDataServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GroupDataServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/GroupDataServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,174 @@ +/**************************************************************** + * 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.servlet; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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; + +import blackboard.data.course.Course; +import blackboard.data.course.Group; +import blackboard.data.user.User; +import blackboard.persist.BbPersistenceManager; +import blackboard.persist.Id; +import blackboard.persist.PersistenceException; +import blackboard.persist.course.CourseDbLoader; +import blackboard.persist.course.GroupDbLoader; +import blackboard.persist.user.UserDbLoader; +import blackboard.platform.persistence.PersistenceServiceFactory; + +/** + * Fetch groups of the specified course. Serves 2 different types of calls: 1-initial request for group names; + * 2-sequential request for selected group users. + * + * @author Andrey Balan + */ +public class GroupDataServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + private static Logger logger = LoggerFactory.getLogger(GroupDataServlet.class); + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + try { + + // get Parameter values + String usernameParam = request.getParameter(Constants.PARAM_USER_ID); + String tsParam = request.getParameter(Constants.PARAM_TIMESTAMP); + String hashParam = request.getParameter(Constants.PARAM_HASH); + String courseIdParam = request.getParameter(Constants.PARAM_COURSE_ID); + String[] groupIdsParam = request.getParameterValues("extGroupIds"); + + // check paramaeters + if (usernameParam == null || tsParam == null || hashParam == null || courseIdParam == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "missing expected parameters"); + return; + } + + String secretKey = LamsPluginUtil.getServerSecretKey(); + String serverId = LamsPluginUtil.getServerId(); + + if (!LamsSecurityUtil.sha1( + tsParam.toLowerCase() + usernameParam.toLowerCase() + serverId.toLowerCase() + + secretKey.toLowerCase()).equals(hashParam)) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "authentication failed"); + return; + } + + // get the persistence manager + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + CourseDbLoader cLoader = CourseDbLoader.Default.getInstance(); + GroupDbLoader groupLoader = (GroupDbLoader) bbPm.getLoader(GroupDbLoader.TYPE); + UserDbLoader userDbLoader = (UserDbLoader) bbPm.getLoader(UserDbLoader.TYPE); +// Id courseId = bbPm.generateId(Course.DATA_TYPE, courseIdParam); + + //create JSON objects to return + Course course = cLoader.loadByCourseId(courseIdParam); + List groups = groupLoader.loadAvailableByCourseId(course.getId()); + JsonArray jsonGroups = new JsonArray(); + + //in case groupIds is supplied - it means we need to provide groups along with all users + if (groupIdsParam != null && groupIdsParam.length > 0) { + + for (String groupIdParam : groupIdsParam) { + for (Group group : groups) { + //only groups with ids requested by LAMS should be processed + Id groupId = group.getId(); + if (!groupId.toExternalString().equals(groupIdParam)) { + continue; + } + + JsonObject jsonGroup = new JsonObject(); + jsonGroup.addProperty("groupId", groupId.toExternalString()); + jsonGroup.addProperty("groupName", group.getTitle()); + jsonGroups.add(jsonGroup); + + JsonArray jsonUsers = new JsonArray(); + jsonGroup.add("users", jsonUsers); + + List users = userDbLoader.loadByGroupId(groupId, null, true); + for (User user : users) { + JsonObject jsonUser = new JsonObject(); + jsonUsers.add(jsonUser); + + jsonUser.addProperty("userName", user.getUserName()); + + // The CSV list should be the format below + // ,<First name>,<Last name>,<Address>,<City>,<State>, + // <Postcode>,<Country>,<Day time number>,<Mobile number>, + // <Fax number>,<Email>,<Locale language>,<Locale country> + jsonUser.addProperty("1", user.getTitle()); + jsonUser.addProperty("2", user.getGivenName()); + jsonUser.addProperty("3", user.getFamilyName()); + jsonUser.addProperty("4", user.getStreet1() + user.getStreet2()); + jsonUser.addProperty("5", user.getCity()); + jsonUser.addProperty("6", user.getState()); + jsonUser.addProperty("7", user.getZipCode()); + jsonUser.addProperty("8", user.getCountry()); + jsonUser.addProperty("9", user.getHomePhone1()); + jsonUser.addProperty("10", user.getMobilePhone()); + jsonUser.addProperty("11", user.getBusinessFax()); + jsonUser.addProperty("12", user.getEmailAddress()); + String locale = user.getLocale(); + String localeLang = LamsSecurityUtil.getLanguage(locale); + String localeCountry = LamsSecurityUtil.getCountry(locale); + jsonUser.addProperty("13", localeLang); + jsonUser.addProperty("14", localeCountry); + } + } + } + } else { + for (Group group : groups) { + Id groupId = group.getId(); + JsonObject jsonGroup = new JsonObject(); + jsonGroup.addProperty("groupId", groupId.toExternalString()); + jsonGroup.addProperty("groupName", group.getTitle()); + jsonGroup.addProperty("groupSize", group.getGroupMemberships().size()); + jsonGroups.add(jsonGroup); + + } + } + + response.getWriter().write(jsonGroups.toString()); + + } catch (PersistenceException e) { + throw new ServletException("Failed to fetch course's groups", e); + } + } + +} + Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LamsLearningDesignDeleteServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LamsLearningDesignDeleteServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LamsLearningDesignDeleteServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,111 @@ +/**************************************************************** + * 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.servlet; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.lamsfoundation.ld.integration.util.LamsPluginUtil; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; + +import blackboard.base.InitializationException; +import blackboard.platform.BbServiceException; +import blackboard.platform.BbServiceManager; +import blackboard.platform.context.Context; +import blackboard.platform.context.ContextManager; + +/** + * Makes a call to LAMS server to delete a learning design. + */ +public class LamsLearningDesignDeleteServlet extends HttpServlet { + + private static final long serialVersionUID = -351131323404991332L; + + public void doGet(HttpServletRequest request, HttpServletResponse response) { + process(request, response); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) { + process(request, response); + } + + protected void process(HttpServletRequest request, HttpServletResponse response) { + String serverAddr = LamsPluginUtil.getServerUrl(); + String serverId = LamsPluginUtil.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); + } + + // get request parameters + String courseId = request.getParameter("course_id"); + + String strLearningDesignId = request.getParameter("sequence_id"); + if ( strLearningDesignId != null ) { + strLearningDesignId.trim(); + } + + // validate method parameter and associated parameters + if ( strLearningDesignId == null || strLearningDesignId.length() == 0 ) { + throw new RuntimeException("Required parameters missing. Add sequence_id for the id of the learning design to be deleted"); + } + + long learningDesignId = 0; + try { + learningDesignId = Long.parseLong(strLearningDesignId); + } catch ( Exception e ) { + throw new RuntimeException("Required parameters missing. Add sequence_id for the id of the learning design to be deleted",e); + } + + ContextManager ctxMgr = null; + Context ctx = null; + try { + // get Blackboard context + ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + ctx = ctxMgr.setContext(request); + + String serverResponse = LamsSecurityUtil.deleteLearningDesigns(ctx, courseId, learningDesignId); + + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().print(serverResponse); + + } catch (InitializationException e) { + throw new RuntimeException(e); + } catch (BbServiceException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + // make sure context is released + if (ctxMgr != null) { + ctxMgr.releaseContext(); + } + } + } +} + Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LamsLearningDesignServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LamsLearningDesignServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LamsLearningDesignServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,112 @@ +/**************************************************************** + * 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.servlet; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.lamsfoundation.ld.integration.Constants; +import org.lamsfoundation.ld.integration.util.LamsPluginUtil; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; + +import blackboard.base.InitializationException; +import blackboard.platform.BbServiceException; +import blackboard.platform.BbServiceManager; +import blackboard.platform.context.Context; +import blackboard.platform.context.ContextManager; + +/** + * Makes a call to LAMS server to get learning designs and returns it. + */ +public class LamsLearningDesignServlet extends HttpServlet { + + private static final long serialVersionUID = -351131323404991332L; + + public void doGet(HttpServletRequest request, HttpServletResponse response) { + process(request, response); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) { + process(request, response); + } + + protected void process(HttpServletRequest request, HttpServletResponse response) { + String serverAddr = LamsPluginUtil.getServerUrl(); + String serverId = LamsPluginUtil.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); + } + + // get request parameters + String folderId = request.getParameter(Constants.PARAM_FOLDER_ID); + + //paging parameters of tablesorter - used in the LAMS Template Wizard + boolean usePaging = false; + String page = request.getParameter("page"); + String size = request.getParameter("size"); + if ( page != null && page.length()>0) { + usePaging = true; + if ( size == null || size.length()==0) + size="10"; + } + String sortName = request.getParameter("sortName"); + String sortDate = request.getParameter("sortDate"); + String search = request.getParameter("search"); + String type = request.getParameter("type"); + String username = request.getParameter("username"); // backup method to get user, when the Blackboard context does not have the user + + ContextManager ctxMgr = null; + Context ctx = null; + try { + // get Blackboard context + ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + ctx = ctxMgr.setContext(request); + + String courseId = ctx.getCourse().getCourseId(); + String method = usePaging ? "getPagedHomeLearningDesignsJSON" : "getLearningDesignsJSON"; + String learningDesigns = LamsSecurityUtil.getLearningDesigns(ctx, username, courseId, folderId, method, type, search, page, size, sortName, sortDate); + + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().print(learningDesigns); + + } catch (InitializationException e) { + throw new RuntimeException(e); + } catch (BbServiceException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + // make sure context is released + if (ctxMgr != null) { + ctxMgr.releaseContext(); + } + } + } +} + Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LearnerMonitorServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LearnerMonitorServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LearnerMonitorServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,161 @@ +package org.lamsfoundation.ld.integration.servlet; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lamsfoundation.ld.integration.dto.LearnerProgressDTO; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; + +import blackboard.base.InitializationException; +import blackboard.data.content.Content; +import blackboard.data.content.CourseDocument; +import blackboard.data.course.Course; +import blackboard.data.course.CourseMembership; +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.CourseMembershipDbLoader; +import blackboard.platform.BbServiceException; +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; + +/** + * + */ +public class LearnerMonitorServlet extends HttpServlet { + + private static final long serialVersionUID = -351131323404991332L; + private static Logger logger = LoggerFactory.getLogger(LearnerMonitorServlet.class); + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + + // Authorise current user for Course Access (automatic redirect) + try { + if (!PlugInUtil.authorizeForCourse(request, response)) + return; + } catch (PlugInException e) { + throw new RuntimeException(e); + } + + try { + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + CourseMembershipDbLoader sessionCourseMembershipLoader = CourseMembershipDbLoader.Default.getInstance(); + + String strLessonId = request.getParameter("lsid").trim(); + long lessonId = Long.parseLong(strLessonId); + String courseIdParam = request.getParameter("course_id"); + + // Get Course ID and User ID + Id course_id = bbPm.generateId(Course.DATA_TYPE, courseIdParam); + + // get Blackboard context + ContextManager ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + Context ctx = ctxMgr.getContext(); + + Id userId = ctx.getUser().getId(); + + // Get the membership data to determine the User's Role + CourseMembership courseMembership = null; + CourseMembership.Role courseRole = null; + boolean isActive = false; + + try { + courseMembership = sessionCourseMembershipLoader.loadByCourseAndUserId(course_id, userId); + courseRole = courseMembership.getRole(); + isActive = courseMembership.getIsAvailable(); + } catch (KeyNotFoundException e) { + // There is no membership record. + e.printStackTrace(); + } catch (PersistenceException pe) { + // There is no membership record. + pe.printStackTrace(); + } + + // Is the User an Instructor of Teaching Assistant + boolean isInstructor = false; + if (CourseMembership.Role.INSTRUCTOR.equals(courseRole) + || CourseMembership.Role.TEACHING_ASSISTANT.equals(courseRole) + || CourseMembership.Role.COURSE_BUILDER.equals(courseRole)) { + isInstructor = true; + + } else if (!CourseMembership.Role.STUDENT.equals(courseRole)) { + // The user is not an Instructor, Teaching Assistant or Student - Access Denied + response.sendRedirect("notAllowed.jsp"); + } + + // Are they active in the course? If not let Blackboard handle the redirect + if (!isActive) { + PlugInUtil.sendAccessDeniedRedirect(request, response); + } + + //Get lessson's title and description + String title = ""; + String description = ""; + //contentId is available in versions after 1.2.3 + String contentIdParam = request.getParameter("content_id"); + if (contentIdParam != null) { + + Container bbContainer = bbPm.getContainer(); + Id contentId = new PkId(bbContainer, CourseDocument.DATA_TYPE, contentIdParam); + ContentDbLoader courseDocumentLoader = (ContentDbLoader) bbPm.getLoader(ContentDbLoader.TYPE); + Content courseDoc = (Content) courseDocumentLoader.loadById(contentId); + title = courseDoc.getTitle(); + description = courseDoc.getBody().getFormattedText(); + + } else { + + title = (request.getParameter("title") != null) ? request.getParameter("title") : "LAMS Options"; + description = request.getParameter("description"); + } + + //display learning design image + String strIsDisplayDesignImage = request.getParameter("isDisplayDesignImage"); + boolean isDisplayDesignImage = "true".equals(strIsDisplayDesignImage) ? true : false; + String learningDesignImageUrl = ""; + if (isDisplayDesignImage) { + String username = ctx.getUser().getUserName(); + learningDesignImageUrl = LamsSecurityUtil.generateRequestLearningDesignImage(username) + "&lsId=" + + lessonId; + } + + //prepare learnerProgressDto for displaying on jsp + LearnerProgressDTO learnerProgressDto = LamsSecurityUtil.getLearnerProgress(ctx, lessonId); + + request.setAttribute("title", title); + request.setAttribute("isInstructor", isInstructor); + request.setAttribute("description", description); + request.setAttribute("isDisplayDesignImage", isDisplayDesignImage); + request.setAttribute("learningDesignImageUrl", learningDesignImageUrl); + request.setAttribute("isLessonCompleted", learnerProgressDto.getLessonComplete()); + request.setAttribute("attemptedActivitiesCount", learnerProgressDto.getAttemptedActivities()); + request.setAttribute("activitiesCompletedCount", learnerProgressDto.getActivitiesCompleted()); + request.setAttribute("activitiesCount", learnerProgressDto.getActivityCount()); + + } catch (InitializationException e) { + throw new ServletException(e); + } catch (BbServiceException e) { + throw new ServletException(e); + } catch (PersistenceException e) { + throw new ServletException(e); + } + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + doGet(request, response); + } + +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LessonManagerServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LessonManagerServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LessonManagerServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,321 @@ +package org.lamsfoundation.ld.integration.servlet; +/**************************************************************** + * 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 + * **************************************************************** + */ + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.ParserConfigurationException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lamsfoundation.ld.integration.util.BlackboardUtil; +import org.lamsfoundation.ld.integration.util.LamsPluginUtil; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; +import org.lamsfoundation.ld.integration.util.LineitemUtil; +import org.xml.sax.SAXException; + +import blackboard.base.FormattedText; +import blackboard.base.InitializationException; +import blackboard.data.ValidationException; +import blackboard.data.content.Content; +import blackboard.data.content.CourseDocument; +import blackboard.data.course.Course; +import blackboard.data.user.User; +import blackboard.persist.BbPersistenceManager; +import blackboard.persist.Container; +import blackboard.persist.Id; +import blackboard.persist.PersistenceException; +import blackboard.persist.PkId; +import blackboard.persist.content.ContentDbLoader; +import blackboard.persist.content.ContentDbPersister; +import blackboard.platform.BbServiceException; +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; +import blackboard.portal.data.PortalExtraInfo; +import blackboard.portal.servlet.PortalUtil; + +/** + * Shows startLesson page and modifyLesson pages, also handles subsequent start and modification of LAMS lessons. + */ +public class LessonManagerServlet extends HttpServlet { + + private static final long serialVersionUID = -351131323404991332L; + private static Logger logger = LoggerFactory.getLogger(LessonManagerServlet.class); + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + process(request, response); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + process(request, response); + } + + private void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + // Authorise current user for Course Control Panel (automatic redirect) + try { + if (!PlugInUtil.authorizeForCourseControlPanel(request, response)) + return; + } catch (PlugInException e) { + throw new RuntimeException(e); + } + + ContextManager ctxMgr = null; + try { + // get Blackboard context + ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + Context ctx = ctxMgr.getContext(); + + String method = request.getParameter("method"); + // -----------------------Start lesson functions --------------------------- + if (method.equals("showStartLessonPage")) { + showStartLessonPage(request, response, ctx); + + } else if (method.equals("start")) { + start(request, response, ctx); + + // -----------------------Modify lesson functions --------------------------- + } else if (method.equals("showModifyLessonPage")) { + showModifyLessonPage(request, response, ctx); + + } else if (method.equals("modify")) { + modify(request, response, ctx); + + // -----------------------Delete lesson functions --------------------------- + } else if (method.equals("delete")) { + delete(request, response, ctx); + } + + } catch (InitializationException e) { + throw new ServletException(e); + } catch (BbServiceException e) { + throw new ServletException(e); + } catch (PersistenceException e) { + throw new ServletException(e); + } catch (ParseException e) { + throw new ServletException(e); + } catch (ValidationException e) { + throw new ServletException(e); + } catch (ParserConfigurationException e) { + throw new ServletException(e); + } catch (SAXException e) { + throw new ServletException(e); + } + +// // important. make sure context is not released as otherwise <bbNG:genericPage> tag will throw an exception +// finally { +// if (ctxMgr != null) { +// ctxMgr.releaseContext(); +// } +// } + } + + /** + * Shows preparation page with available learning designs. Preview is available. + */ + private void showStartLessonPage(HttpServletRequest request, HttpServletResponse response, Context ctx) + throws InitializationException, BbServiceException, PersistenceException, IOException, ServletException { + + String lamsServerUrl = LamsPluginUtil.getServerUrl(); + request.setAttribute("lamsServerUrl", lamsServerUrl); + + // get all user accessible folders and LD descriptions as JSON + String learningDesigns = LamsSecurityUtil.getLearningDesigns(ctx, ctx.getCourse().getCourseId(), null); + request.setAttribute("learningDesigns", learningDesigns); + + request.getRequestDispatcher("/modules/create.jsp").forward(request, response); + } + + /** + * Starts preview lesson on LAMS server. Launches it. + */ + private void start(HttpServletRequest request, HttpServletResponse response, Context ctx) throws IOException, ServletException, PersistenceException, ParseException, ValidationException, ParserConfigurationException, SAXException { + + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + + //store newly created LAMS lesson + User user = ctx.getUser(); + BlackboardUtil.storeBlackboardContent(request, response, user); + + // constuct strReturnUrl + String courseIdStr = request.getParameter("course_id"); + String contentIdStr = request.getParameter("content_id"); + // internal Blackboard IDs for the course and parent content item + Id courseId = bbPm.generateId(Course.DATA_TYPE, courseIdStr); + Id folderId = bbPm.generateId(CourseDocument.DATA_TYPE, contentIdStr); + String returnUrl = PlugInUtil.getEditableContentReturnURL(folderId, courseId); + request.setAttribute("returnUrl", returnUrl); + + request.getRequestDispatcher("/modules/startLessonSuccess.jsp").forward(request, response); + } + + private void showModifyLessonPage(HttpServletRequest request, HttpServletResponse response, Context ctx) + throws InitializationException, BbServiceException, PersistenceException, IOException, ServletException { + + // retrive the LAMS lesson + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + Container bbContainer = bbPm.getContainer(); + Id contentId = new PkId(bbContainer, CourseDocument.DATA_TYPE, request.getParameter("content_id")); + ContentDbLoader courseDocumentLoader = (ContentDbLoader) bbPm.getLoader(ContentDbLoader.TYPE); + Content bbContent = (Content) courseDocumentLoader.loadById(contentId); + + // get LAMS lessons's properties + Calendar startDate = bbContent.getStartDate(); + Calendar endDate = bbContent.getEndDate(); + FormattedText description = bbContent.getBody(); + + request.setAttribute("bbContent", bbContent); + request.setAttribute("startDate", startDate); + request.setAttribute("endDate", endDate); + request.setAttribute("description", description); + + request.getRequestDispatcher("/modules/modify.jsp").forward(request, response); + } + + private void modify(HttpServletRequest request, HttpServletResponse response, Context ctx) + throws InitializationException, BbServiceException, PersistenceException, IOException, ValidationException, + ServletException, ParserConfigurationException, SAXException, ParseException { + + String _course_id = request.getParameter("course_id"); + String _content_id = request.getParameter("content_id"); + + // Retrieve the Db persistence manager from the persistence service + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + Container bbContainer = bbPm.getContainer(); + + // Internal Blackboard IDs for the course and parent content item + Id courseId = bbPm.generateId(Course.DATA_TYPE, _course_id); + Id contentId = new PkId( bbContainer, CourseDocument.DATA_TYPE, _content_id); + + // Load the content item + ContentDbLoader courseDocumentLoader = (ContentDbLoader) bbPm.getLoader( ContentDbLoader.TYPE ); + Content bbContent = (Content)courseDocumentLoader.loadById( contentId ); + + // Get the form parameters and convert into correct data types + // TODO: Use bb text area instead + String strTitle = request.getParameter("title").trim(); + String strDescription = request.getParameter("descriptiontext").trim(); + FormattedText description = new FormattedText(strDescription, FormattedText.Type.HTML); + + String strIsAvailable = request.getParameter("isAvailable"); + String strIsGradecenter = request.getParameter("isGradecenter"); + String strIsTracked = request.getParameter("isTracked"); + boolean isAvailable = strIsAvailable.equals("true")?true:false; + boolean isGradecenter = strIsGradecenter.equals("true")?true:false; + boolean isTracked = strIsTracked.equals("true")?true:false; + + String strStartDate = request.getParameter("lessonAvailability_start_datetime"); + String strEndDate = request.getParameter("lessonAvailability_end_datetime"); + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String strStartDateCheckbox = request.getParameter("lessonAvailability_start_checkbox"); + String strEndDateCheckbox = request.getParameter("lessonAvailability_end_checkbox"); + + //if teacher turned Gradecenter option ON (and it was OFF previously) - create lineitem + if (!bbContent.getIsDescribed() && isGradecenter) { + String username = ctx.getUser().getUserName(); + LineitemUtil.createLineitem(bbContent, username); + + //if teacher turned Gradecenter option OFF (and it was ON previously) - remove lineitem + } else if (bbContent.getIsDescribed() && !isGradecenter) { + LineitemUtil.removeLineitem(_content_id, _course_id); + + //change existing lineitem's name if lesson name has been changed + } else if (isGradecenter && !strTitle.equals(bbContent.getTitle())) { + LineitemUtil.changeLineitemName(_content_id, _course_id, strTitle); + } + + // Set LAMS content data in Blackboard + bbContent.setTitle(strTitle); + bbContent.setIsAvailable(isAvailable); + bbContent.setIsDescribed(isGradecenter);//isDescribed field is used for storing isGradecenter parameter + bbContent.setIsTracked(isTracked); + bbContent.setBody(description); + + // Set Availability Dates + // Clear the date (set to null) if the checkbox is unchecked + // Start Date + Calendar startDate = null; + if (strStartDateCheckbox != null && strStartDateCheckbox.equals("1")) { + startDate = Calendar.getInstance(); + startDate.setTime(formatter.parse(strStartDate)); + } + bbContent.setStartDate(startDate); + // End Date + Calendar endDate = null; + if (strEndDateCheckbox != null && (strEndDateCheckbox.equals("1"))) { + endDate = Calendar.getInstance(); + endDate.setTime(formatter.parse(strEndDate)); + } + bbContent.setEndDate(endDate); + + //Persist the Modified Lesson Object in Blackboard + ContentDbPersister persister= (ContentDbPersister) bbPm.getPersister( ContentDbPersister.TYPE ); + persister.persist( bbContent ); + + String strReturnUrl = PlugInUtil.getEditableContentReturnURL(bbContent.getParentId(), courseId); + request.setAttribute("strReturnUrl", strReturnUrl); + + request.getRequestDispatcher("/modules/modifyLessonSuccess.jsp").forward(request, response); + } + + private void delete(HttpServletRequest request, HttpServletResponse response, Context ctx) + throws InitializationException, BbServiceException, PersistenceException, IOException, ServletException, ParserConfigurationException, SAXException { + + //remove Lineitem object from Blackboard DB + String _content_id = request.getParameter("content_id"); + String _course_id = request.getParameter("course_id"); + LineitemUtil.removeLineitem(_content_id, _course_id); + + // remove internalContentId -> externalContentId key->value pair (it's used for GradebookServlet) + PortalExtraInfo pei = PortalUtil.loadPortalExtraInfo(null, null, "LamsStorage"); + ExtraInfo ei = pei.getExtraInfo(); + ei.clearEntry(_content_id); + PortalUtil.savePortalExtraInfo(pei); + + //remove lesson from LAMS server + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + Container bbContainer = bbPm.getContainer(); + ContentDbLoader courseDocumentLoader = ContentDbLoader.Default.getInstance(); + Id contentId = new PkId(bbContainer, CourseDocument.DATA_TYPE, _content_id); + Content bbContent = courseDocumentLoader.loadById(contentId); + String lsId = bbContent.getLinkRef(); + String userName = ctx.getUser().getUserName(); + Boolean isDeletedSuccessfully = LamsSecurityUtil.deleteLesson(userName, lsId); + + System.out.println("Lesson (bbContentId:" + _content_id + ") successfully deleted by userName:" + userName); + } + + + +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LinkToolsServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LinkToolsServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/LinkToolsServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,603 @@ +package org.lamsfoundation.ld.integration.servlet; +/**************************************************************** + * 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 + * **************************************************************** + */ + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URLEncoder; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.ParserConfigurationException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lamsfoundation.ld.integration.util.BlackboardUtil; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; +import org.lamsfoundation.ld.integration.util.LamsServerException; +import org.lamsfoundation.ld.integration.util.LineitemUtil; +import org.xml.sax.SAXException; + +import blackboard.base.FormattedText; +import blackboard.base.InitializationException; +import blackboard.data.ValidationException; +import blackboard.data.content.Content; +import blackboard.data.course.Course; +import blackboard.data.navigation.CourseToc; +import blackboard.data.user.User; +import blackboard.persist.BbPersistenceManager; +import blackboard.persist.Container; +import blackboard.persist.Id; +import blackboard.persist.PersistenceException; +import blackboard.persist.PkId; +import blackboard.persist.content.ContentDbLoader; +import blackboard.persist.content.ContentDbPersister; +import blackboard.persist.course.CourseDbLoader; +import blackboard.persist.navigation.CourseTocDbLoader; +import blackboard.platform.BbServiceException; +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.util.StringUtil; + +/** + * + */ +public class LinkToolsServlet extends HttpServlet { + + private static final long serialVersionUID = -351131323404991332L; + private static Logger logger = LoggerFactory.getLogger(LinkToolsServlet.class); + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + process(request, response); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + process(request, response); + } + + private void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + // Authorise current user for Course Control Panel (automatic redirect) + try { + if (!PlugInUtil.authorizeForCourseControlPanel(request, response)) + return; + } catch (PlugInException e) { + throw new RuntimeException(e); + } + + String method = request.getParameter("method"); + if (method.equals("openAdminLinkTool")) { + openAdminLinkTool(request, response); + + } else if (method.equals("openAuthorLinkTool")) { + openAuthorLinkTool(request, response); + + } else if (method.equals("openMonitorLinkTool")) { + openMonitorLinkTool(request, response); + + //Admin on BB side calls this servlet to clone old lesson that were copied to the new course. + } else if (method.equals("cloneLessons")) { + cloneLessons(request, response); + + //Admin on BB side calls this servlet to import old lesson that were copied to the new course. + } else if (method.equals("importLessons")) { + importLessons(request, response); + + //Admin on BB side calls this servlet to correct lineitems that have been screwed up while copying/importing courses. + } else if (method.equals("correctLineitems")) { + correctLineitems(request, response); + } + } + + private void openAdminLinkTool(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + request.getRequestDispatcher("/links/admin.jsp").forward(request, response); + } + + private void openAuthorLinkTool(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + request.getRequestDispatcher("/links/author.jsp").forward(request, response); + } + + private void openMonitorLinkTool(HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + Container bbContainer = bbPm.getContainer(); + Id courseId = new PkId(bbContainer, Course.DATA_TYPE, request.getParameter("course_id")); + + ContextManager ctxMgr = null; + String strOut = "[[]]"; + try { + // get Blackboard context + ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + Context ctx = ctxMgr.getContext(); + ContentDbLoader cLoader = (ContentDbLoader) bbPm.getLoader(ContentDbLoader.TYPE); + CourseTocDbLoader ctLoader = (CourseTocDbLoader) bbPm.getLoader(CourseTocDbLoader.TYPE); + + Course course = ctx.getCourse(); + List<CourseToc> ctList = ctLoader.loadByCourseId(courseId); + CourseToc[] courseTocs = (CourseToc[]) ctList.toArray(new CourseToc[0]); + + int idx = 0; + StringBuilder strB = new StringBuilder(); + strB.append("[{type:'Text', label:'" + course.getTitle().replace("'", "\\'") + "', id:0, children:["); + for (int i = 0; i < courseTocs.length; i++) { + if (courseTocs[i].getTargetType().compareTo(CourseToc.Target.CONTENT) == 0) { + Content cont = cLoader.loadByTocId(courseTocs[i].getId()); + strB.append(getChild(cont, cLoader)); + idx = i; + break; + } + } + for (int i = idx + 1; i < courseTocs.length; i++) { + if (courseTocs[i].getTargetType().compareTo(CourseToc.Target.CONTENT) == 0) { + Content cont = cLoader.loadByTocId(courseTocs[i].getId()); + strB.append(", ").append(getChild(cont, cLoader)); + } + } + strB.append("]}]"); + strOut = strB.toString(); + } catch (InitializationException e) { + throw new ServletException(e); + } catch (BbServiceException e) { + throw new ServletException(e); + } catch (PersistenceException e) { + throw new ServletException(e); + } + + request.setAttribute("treeView", strOut); + request.getRequestDispatcher("/links/monitor.jsp").forward(request, response); + } + + private static String extractParameterValue(String url, String param) { + if (url != null && param != null) { + int quotationMarkIndex = url.indexOf("?"); + String queryPart = quotationMarkIndex > -1 ? url.substring(quotationMarkIndex + 1) : url; + String[] paramEntries = queryPart.split("&"); + for (String paramEntry : paramEntries) { + String[] paramEntrySplitted = paramEntry.split("="); + if ((paramEntrySplitted.length > 1) && param.equalsIgnoreCase(paramEntrySplitted[0])) { + return paramEntrySplitted[1]; + } + } + } + return null; + } + + private String getChild(Content f, ContentDbLoader cLoader) { + StringBuilder sb = new StringBuilder(); + try { + + if (f.getIsFolder()) { + + List<Content> cList = cLoader.loadChildren(f.getId()); + Content[] cArray = cList.toArray(new Content[0]); + //sort content by title + Arrays.sort(cArray, new Comparator<Content>() { + @Override + public int compare(Content o1, Content o2) { + if (o1 != null && o2 != null) { + return o1.getTitle().compareToIgnoreCase(o2.getTitle()); + } else if (o1 != null) + return 1; + else + return -1; + } + }); + + String title = f.getTitle(); + if (title.indexOf("'") != -1) { + title = title.replace("'", "\\'"); + } + sb.append("{type:'Text', label:'" + title + "', id:0"); + + if (cArray.length == 0) { + sb.append(", expanded:0, children:[{type:'HTML', html:'<i>null</i>', id:0}]}"); + return sb.toString(); + + } else { + sb.append(", children:["); + sb.append(getChild(cArray[0], cLoader)); + for (int i = 1; i < cArray.length; i++) { + sb.append(", ").append(getChild(cArray[i], cLoader)); + } + sb.append("]}"); + } + return sb.toString(); + + } else { + + if (f.getContentHandler().equals("resource/x-lams-lamscontent")) { + String strUrl = f.getUrl(); + String strId = extractParameterValue(strUrl, "lsid"); + String strTitle = f.getTitle().replace("'", "\\'"); + sb.append("{type:'Text', label:'" + strTitle + "', id:'" + strId + "'}"); + // return sb.toString(); + + } else if (f.getContentHandler().equals("resource/x-ntu-hdllams")) { + String strUrl = f.getUrl(); + String strId = "0"; + if (strUrl.indexOf("&seq_id=") != -1) { + int pos1 = strUrl.indexOf("&seq_id=") + 8; +// int pos2 = strUrl.indexOf("&", pos1); + strId = strUrl.substring(pos1); + } + String strTitle = f.getTitle().replace("'", "\\'"); + sb.append("{type:'Text', label:'" + strTitle + "', id:'" + strId + "'}"); + + } else { + // sb.append("{type:'HTML', html:'<i>null</i>', id:0}"); + } + return sb.toString(); + } + + } catch (Exception e) { + return sb.toString(); + } + } + + /** + * Admin on BB side calls this servlet to clone old lesson that were copied to the new course. + * + * @param request + * @param response + * @throws IOException + * @throws ServletException + */ + private void cloneLessons(HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + String _course_id = request.getParameter("course_id"); + if (StringUtil.isEmpty(_course_id)) { + throw new RuntimeException("Required parameters are missing. courseId: " + _course_id); + } + + String newLessonIds = ""; + try { + newLessonIds = recreateLessonsAfterCourseCopy(_course_id); + } catch (IllegalStateException e) { + throw new ServletException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (PersistenceException e) { + throw new ServletException(e); + } catch (ValidationException e) { + throw new ServletException(e); + } catch (ParserConfigurationException e) { + throw new ServletException(e); + } catch (SAXException e) { + throw new ServletException(e); + } + + //prepare string to write out + int newLessonsCounts = newLessonIds.length() - newLessonIds.replace(",", "").length(); + String resultStr = "Complete! " + newLessonsCounts + " lessons have been cloned."; + //add all lessonIds (without the last comma) + if (newLessonsCounts > 0) { + resultStr += " Their updated lessonIds: " + newLessonIds.substring(0, newLessonIds.length() - 2); + } + + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.write(resultStr); + out.flush(); + out.close(); + } + + /** + * Recreates lessons after course has been copied. I.e. asks LAMS server to clone old lesson and then updates BB + * link with the newly created lesson Id. + * + * @param courseIdParam + * id of the course that has been copied + * @return + * @throws PersistenceException + * @throws ValidationException + * @throws IOException + * @throws ServletException + * @throws SAXException + * @throws ParserConfigurationException + */ + private static String recreateLessonsAfterCourseCopy(String _course_id) + throws PersistenceException, ValidationException, ServletException, IOException, ParserConfigurationException, SAXException { + String newLessonIds = ""; + + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + ContentDbPersister persister = ContentDbPersister.Default.getInstance(); + CourseDbLoader courseLoader = CourseDbLoader.Default.getInstance(); + + PkId courseId = (PkId) bbPm.generateId(Course.DATA_TYPE, _course_id); + Course course = courseLoader.loadById(courseId); + String courseIdStr = course.getCourseId(); + + logger.debug("Starting clonning course lessons (courseId=" + courseId + ")."); + + // find a teacher that will be assigned as lesson's author on LAMS side + User teacher = BlackboardUtil.getCourseTeacher(courseId); + + //find all lessons that should be updated + List<Content> lamsContents = BlackboardUtil.getLamsLessonsByCourse(courseId); + for (Content content : lamsContents) { + + String _content_id = content.getId().toExternalString(); + + String url = content.getUrl(); + String urlLessonId = getParameterValue(url, "lsid"); + String urlCourseId = getParameterValue(url, "course_id"); + String urlContentId = getParameterValue(url, "content_id"); + + //in case when both courseId and contentId don't coincide with the ones from URL - means lesson needs to be cloned + if (!urlCourseId.equals(_course_id) && !urlContentId.equals(_content_id)) { + + final Long newLessonId = LamsSecurityUtil.cloneLesson(teacher, courseIdStr, urlLessonId); + + // update lesson id + content.setLinkRef(Long.toString(newLessonId)); + + // update URL + url = replaceParameterValue(url, "lsid", Long.toString(newLessonId)); + url = replaceParameterValue(url, "course_id", _course_id); + url = replaceParameterValue(url, "content_id", _content_id); + content.setUrl(url); + + // persist updated content + persister.persist(content); + + //update lineitem details + LineitemUtil.updateLineitemLessonId(content, _course_id, newLessonId, teacher.getUserName()); + + logger.debug("Lesson (lessonId=" + urlLessonId + ") was successfully cloned to the one (lessonId=" + + newLessonId + ")."); + + newLessonIds += newLessonId + ", "; + } + + } + + return newLessonIds; + } + + /** + * Admin on BB side calls this servlet to import old lesson that were copied to the new course. + */ + private void importLessons(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String _course_id = request.getParameter("course_id"); + if (StringUtil.isEmpty(_course_id)) { + throw new RuntimeException("Required parameters are missing. courseId: " + _course_id); + } + + String newLessonIds = ""; + try { + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + ContentDbPersister persister = ContentDbPersister.Default.getInstance(); + CourseDbLoader courseLoader = CourseDbLoader.Default.getInstance(); + + PkId courseId = (PkId) bbPm.generateId(Course.DATA_TYPE, _course_id); + Course course = courseLoader.loadById(courseId); + String courseIdStr = course.getCourseId(); + + logger.debug("Starting importing course lessons (courseId=" + courseId + ")."); + + // find a teacher that will be assigned as lesson's author on LAMS side + User teacher = BlackboardUtil.getCourseTeacher(courseId); + + //find all lessons that should be updated + List<Content> lamsContents = BlackboardUtil.getLamsLessonsByCourse(courseId); + for (Content content : lamsContents) { + + String _content_id = content.getId().toExternalString(); + + String url = content.getUrl(); + String urlLessonId = getParameterValue(url, "lsid"); + String urlCourseId = getParameterValue(url, "course_id"); + String urlContentId = getParameterValue(url, "content_id"); + String urlLdId = getParameterValue(url, "ldid"); + + //in case when both courseId and contentId don't coincide with the ones from URL - means lesson needs to be imported + if (!urlCourseId.equals(_course_id) && !urlContentId.equals(_content_id)) { + + final Long newLdId = LamsSecurityUtil.importLearningDesign(teacher, courseIdStr, urlLessonId, + urlLdId); + + logger.debug("Lesson (lessonId=" + urlLessonId + + ") was successfully imported to the one (learningDesignId=" + newLdId + ")."); + + // Start the Lesson in LAMS (via Webservices) and get back the lesson ID + String title = content.getTitle(); + FormattedText descriptionFormatted = content.getBody(); + String description = URLEncoder.encode(descriptionFormatted.getText(), "UTF-8"); + final long newLessonId = LamsSecurityUtil.startLesson(teacher, courseIdStr, newLdId, title, + description, false); + + // update lesson id + content.setLinkRef(Long.toString(newLessonId)); + + // update URL + url = replaceParameterValue(url, "ldid", Long.toString(newLdId)); + url = replaceParameterValue(url, "lsid", Long.toString(newLessonId)); + url = replaceParameterValue(url, "course_id", _course_id); + url = replaceParameterValue(url, "content_id", _content_id); + content.setUrl(url); + + // persist updated content + persister.persist(content); + + //update lineitem details + LineitemUtil.updateLineitemLessonId(content, _course_id, newLessonId, teacher.getUserName()); + + logger.debug("Lesson (lessonId=" + urlLessonId + ") was successfully imported to the one (lessonId=" + + newLessonId + ")."); + + newLessonIds += newLessonId + ", "; + } + } + + } catch (LamsServerException e) { + //printing out error cause + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.write("Failed! " + e.getMessage()); + out.flush(); + out.close(); + return; + } catch (IllegalStateException e) { + throw new ServletException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (PersistenceException e) { + throw new ServletException(e); + } catch (ValidationException e) { + throw new ServletException(e); + } catch (ParserConfigurationException e) { + throw new ServletException(e); + } catch (SAXException e) { + throw new ServletException(e); + } + + //prepare string to write out + int newLessonsCounts = newLessonIds.length() - newLessonIds.replace(",", "").length(); + String resultStr = "Complete! " + newLessonsCounts + " lessons have been imported."; + //add all lessonIds (without the last comma) + if (newLessonsCounts > 0) { + resultStr += " Their updated lessonIds: " + newLessonIds.substring(0, newLessonIds.length()-2); + } + logger.debug(resultStr); + + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.write(resultStr); + out.flush(); + out.close(); + } + + /** + * Admin on BB side calls this servlet to correct lineitems that have been screwed up while copying/importing courses. + * + * @param request + * @param response + * @throws ServletException + * @throws IOException + */ + private void correctLineitems(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String _course_id = request.getParameter("course_id"); + if (StringUtil.isEmpty(_course_id)) { + throw new RuntimeException("Required parameters are missing. courseId: " + _course_id); + } + + try { + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + PkId courseId = (PkId) bbPm.generateId(Course.DATA_TYPE, _course_id); + + logger.debug("Starting clonning course lessons (courseId=" + courseId + ")."); + + // find a teacher that will be assigned as lesson's author on LAMS side + User teacher = BlackboardUtil.getCourseTeacher(courseId); + + //find all lessons that should be updated + List<Content> lamsContents = BlackboardUtil.getLamsLessonsByCourse(courseId); + for (Content content : lamsContents) { + + // update lesson id + String lessonId = content.getLinkRef(); + + //update lineitem details + LineitemUtil.updateLineitemLessonId(content, _course_id, Long.parseLong(lessonId), + teacher.getUserName()); + } + + } catch (IllegalStateException e) { + throw new ServletException( + "LAMS Server timeout, did not get a response from the LAMS server. Please contact your systems administrator", + e); + } catch (NumberFormatException e) { + throw new ServletException(e); + } catch (PersistenceException e) { + throw new ServletException(e); + } catch (ValidationException e) { + throw new ServletException(e); + } catch (ParserConfigurationException e) { + throw new ServletException(e); + } catch (SAXException e) { + throw new ServletException(e); + } + + //prepare string to write out + String resultStr = "Complete! All lineiems have been corrected."; + logger.debug(resultStr); + + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.write(resultStr); + out.flush(); + out.close(); + } + + /* + * Returns param value, and empty string in case of there is no such param available + * + * @param url + * @param paramName + * @return + */ + private static String getParameterValue(String url, String paramName) { + String paramValue = ""; + + int quotationMarkIndex = url.indexOf("?"); + String queryPart = quotationMarkIndex > -1 ? url.substring(quotationMarkIndex + 1) : url; + String[] paramEntries = queryPart.split("&"); + for (String paramEntry : paramEntries) { + String[] paramEntrySplitted = paramEntry.split("="); + if ((paramEntrySplitted.length > 1) && paramName.equalsIgnoreCase(paramEntrySplitted[0])) { + paramValue = paramEntrySplitted[1]; + break; + } + } + + return paramValue; + } + + private static String replaceParameterValue(String url, String paramName, String newParamValue) { + String oldParamValue = ""; + + int quotationMarkIndex = url.indexOf("?"); + String queryPart = quotationMarkIndex > -1 ? url.substring(quotationMarkIndex + 1) : url; + String[] paramEntries = queryPart.split("&"); + for (String paramEntry : paramEntries) { + String[] paramEntrySplitted = paramEntry.split("="); + if ((paramEntrySplitted.length > 1) && paramName.equalsIgnoreCase(paramEntrySplitted[0])) { + oldParamValue = paramEntrySplitted[1]; + + return url.replaceFirst(paramName + "=" + oldParamValue, paramName + "=" + newParamValue); + } + } + + return url; + } + +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/OpenLamsPageServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/OpenLamsPageServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/OpenLamsPageServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,246 @@ +package org.lamsfoundation.ld.integration.servlet; +/**************************************************************** + * 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 + * **************************************************************** + */ + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; + +import blackboard.base.InitializationException; +import blackboard.data.course.Course; +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.platform.BbServiceException; +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; + +/** + * Launches LAMS pages: author, learner and monitor one. + */ +public class OpenLamsPageServlet extends HttpServlet { + + private static final long serialVersionUID = -351131323404991332L; + private static Logger logger = LoggerFactory.getLogger(OpenLamsPageServlet.class); + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + process(request, response); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + process(request, response); + } + + private void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + ContextManager ctxMgr = null; + try { + // get Blackboard context + ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + Context ctx = ctxMgr.getContext(); + + String method = request.getParameter("method"); + // -----------------------Assessment Author functions --------------------------- + if (method.equals("openAuthor")) { + openAuthor(request, response, ctx); + + } else if (method.equals("openPreview")) { + openPreview(request, response, ctx); + + } else if (method.equals("openLearner")) { + openLearner(request, response, ctx); + + } else if (method.equals("openMonitor")) { + openMonitor(request, response, ctx); + } + + } catch (InitializationException e) { + throw new ServletException(e); + } catch (BbServiceException e) { + throw new ServletException(e); + } catch (PersistenceException e) { + throw new ServletException(e); + } finally { + // make sure context is released + if (ctxMgr != null) { + ctxMgr.releaseContext(); + } + } + + } + + private void openAuthor(HttpServletRequest request, HttpServletResponse response, Context ctx) + throws InitializationException, BbServiceException, PersistenceException, IOException { + // Authorise current user for Course Control Panel (automatic redirect) + try { + if (!PlugInUtil.authorizeForCourseControlPanel(request, response)) + return; + } catch (PlugInException e) { + throw new RuntimeException(e); + } + + // construct Login Request URL for authoring LAMS Lessons + String authorUrl = LamsSecurityUtil.generateRequestURL(ctx, "author", null); + response.sendRedirect(authorUrl); + } + + /** + * Starts preview lesson on LAMS server. Launches it. + */ + private void openPreview(HttpServletRequest request, HttpServletResponse response, Context ctx) + throws InitializationException, BbServiceException, PersistenceException, IOException { + // Authorize current user for Course Control Panel (automatic redirect) + try { + if (!PlugInUtil.authorizeForCourseControlPanel(request, response)) + return; + } catch (PlugInException e) { + throw new RuntimeException(e); + } + + // Get the form parameters and convert into correct data types + String strTitle = request.getParameter("title").trim(); + String strLdId = request.getParameter("ldId").trim(); + long ldId = Long.parseLong(strLdId); + + // start lesson-preview in LAMS and get back the lesson ID + User user = ctx.getUser(); + Long lsId = LamsSecurityUtil.startLesson(user, "Previews", ldId, strTitle, "", true); + // error checking + if (lsId == -1) { + response.sendRedirect("lamsServerDown.jsp"); + System.exit(1); + } + + // redirect to preview lesson + String previewUrl = LamsSecurityUtil.generateRequestURL(ctx, "learnerStrictAuth", "" + lsId); + response.sendRedirect(previewUrl); + } + + private void openLearner(HttpServletRequest request, HttpServletResponse response, Context ctx) + throws InitializationException, BbServiceException, PersistenceException, IOException { + // Authorise current user for Course Access (automatic redirect) + try { + if (!PlugInUtil.authorizeForCourse(request, response)) + return; + } catch (PlugInException e) { + throw new RuntimeException(e); + } + + // Get Course ID and Session User ID + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + String course_idstr = request.getParameter("course_id"); + Id course_id = bbPm.generateId(Course.DATA_TYPE, course_idstr); + User sessionUser = ctx.getUser(); + Id sessionUserId = sessionUser.getId(); + // Get the membership data to determine the User's Role + CourseMembership courseMembership = null; + CourseMembership.Role courseRole = null; + CourseMembershipDbLoader sessionCourseMembershipLoader = (CourseMembershipDbLoader) bbPm + .getLoader(CourseMembershipDbLoader.TYPE); + try { + courseMembership = sessionCourseMembershipLoader.loadByCourseAndUserId(course_id, sessionUserId); + courseRole = courseMembership.getRole(); + } catch (KeyNotFoundException e) { + // There is no membership record. + e.printStackTrace(); + } catch (PersistenceException pe) { + // There is no membership record. + pe.printStackTrace(); + } + + // if the user is not an Instructor, Teaching Assistant or Student - Access Denied + if (!(courseRole.equals(CourseMembership.Role.INSTRUCTOR) + || courseRole.equals(CourseMembership.Role.TEACHING_ASSISTANT) + || courseRole.equals(CourseMembership.Role.COURSE_BUILDER) + || courseRole.equals(CourseMembership.Role.STUDENT))) { + response.sendRedirect("notAllowed.jsp"); + return; + } + + // Get the Login Request URL for authoring LAMS Lessons + String lsid = request.getParameter("lsid"); + String learnerUrl = LamsSecurityUtil.generateRequestURL(ctx, "learnerStrictAuth", lsid); + + response.sendRedirect(learnerUrl); + } + + private void openMonitor(HttpServletRequest request, HttpServletResponse response, Context ctx) + throws IOException, PersistenceException { + // Authorize current user for Course Control Panel (automatic redirect) + try { + if (!PlugInUtil.authorizeForCourseControlPanel(request, response)) + return; + } catch (PlugInException e) { + throw new RuntimeException(e); + } + + // Get Course ID and Session User ID + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + CourseMembershipDbLoader sessionCourseMembershipLoader = CourseMembershipDbLoader.Default.getInstance(); + String _course_id = request.getParameter("course_id"); + Id course_id = bbPm.generateId(Course.DATA_TYPE, _course_id); + User sessionUser = ctx.getUser(); + Id sessionUserId = sessionUser.getId(); + // Get the membership data to determine the User's Role + CourseMembership courseMembership = null; + CourseMembership.Role courseRole = null; + + try { + courseMembership = sessionCourseMembershipLoader.loadByCourseAndUserId(course_id, sessionUserId); + courseRole = courseMembership.getRole(); + } catch (KeyNotFoundException e) { + // There is no membership record. + e.printStackTrace(); + } catch (PersistenceException pe) { + // There is no membership record. + pe.printStackTrace(); + } + + // if the user is not an Instructor or Teaching Assistant - Access Denied + if (!(courseRole.equals(CourseMembership.Role.INSTRUCTOR) + || courseRole.equals(CourseMembership.Role.TEACHING_ASSISTANT) + || courseRole.equals(CourseMembership.Role.COURSE_BUILDER))) { + response.sendRedirect("notAllowed.jsp"); + } + + // construct Login Request URL for monitoring LAMS Lessons + String lsid = request.getParameter("lsid"); + String monitorUrl = LamsSecurityUtil.generateRequestURL(ctx, "monitor", lsid); + response.sendRedirect(monitorUrl); + } + +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/RenderDesignImageServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/RenderDesignImageServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/RenderDesignImageServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,100 @@ +/**************************************************************** + * 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.servlet; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; + +import blackboard.base.InitializationException; +import blackboard.platform.BbServiceException; +import blackboard.platform.BbServiceManager; +import blackboard.platform.context.Context; +import blackboard.platform.context.ContextManager; + +/** + * Makes a call to LAMS server to get a learning design visual image (svg data) + * Looks for either sequence_id for the learning design (sequence) id. + */ +public class RenderDesignImageServlet extends HttpServlet { + + private static final long serialVersionUID = -351131323404991332L; + + public void doGet(HttpServletRequest request, HttpServletResponse response) { + process(request, response); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) { + process(request, response); + } + + public void process(HttpServletRequest request, HttpServletResponse response) { + + String strLearningDesignId = request.getParameter("sequence_id"); + if ( strLearningDesignId != null ) { + strLearningDesignId.trim(); + } + + // validate method parameter and associated parameters + if ( strLearningDesignId == null || strLearningDesignId.length() == 0 ) { + throw new RuntimeException("Requred parameters missing. Add sequence_id for the id of the learning design to be displayed"); + } + + long learningDesignId = 0; + try { + learningDesignId = Long.parseLong(strLearningDesignId); + } catch ( Exception e ) { + throw new RuntimeException("Requred parameters missing. Add sequence_id for the id of the learning design to be displayed",e); + } + + ContextManager ctxMgr = null; + Context ctx = null; + try { + // get Blackboard context + ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + ctx = ctxMgr.setContext(request); + + String username = ctx.getUser().getUserName(); + String learningDesignImageUrl = LamsSecurityUtil.generateRequestLearningDesignImage(username) + "&ldId=" + learningDesignId; + response.sendRedirect(learningDesignImageUrl); + + } catch (InitializationException e) { + throw new RuntimeException(e); + } catch (BbServiceException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + // make sure context is released + if (ctxMgr != null) { + ctxMgr.releaseContext(); + } + } + } +} + Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/StartLessonAjaxServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/StartLessonAjaxServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/StartLessonAjaxServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,113 @@ +package org.lamsfoundation.ld.integration.servlet; +/**************************************************************** + * 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 + * **************************************************************** + */ + +import java.io.IOException; +import java.text.ParseException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.ParserConfigurationException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lamsfoundation.ld.integration.util.BlackboardUtil; +import org.xml.sax.SAXException; + +import blackboard.base.InitializationException; +import blackboard.data.ValidationException; +import blackboard.data.user.User; +import blackboard.persist.PersistenceException; +import blackboard.platform.BbServiceException; +import blackboard.platform.BbServiceManager; +import blackboard.platform.context.ContextManager; + +/** + * 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. + */ +public class StartLessonAjaxServlet extends HttpServlet { + + private static final long serialVersionUID = -351131323404991332L; + private static Logger logger = LoggerFactory.getLogger(StartLessonAjaxServlet.class); + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + process(request, response); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { + process(request, response); + } + + protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException { + + ContextManager ctxMgr = null; + try { + // get Blackboard context + ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + + String courseIdStr = request.getParameter("course_id"); + String contentIdStr = request.getParameter("content_id"); + String strTitle = BlackboardUtil.getTrimmedString(request, "title"); + String strSequenceID = BlackboardUtil.getTrimmedString(request, "sequence_id"); + + if ( courseIdStr == null || contentIdStr == null || strSequenceID.length()==0 || strTitle.length() == 0) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, + "Unable to create error - parameter missing. course_id, content_id, sequence_id and title required"); + } else { + + String username = request.getParameter("username"); + User user = BlackboardUtil.loadUserFromDB(username); + + String bbContentId = BlackboardUtil.storeBlackboardContent(request, response, user); + + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().print("{\"content_id\":" + bbContentId + "}"); + } + + } catch (PersistenceException e) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to start lesson "+e.getMessage()); + } catch (ParseException e) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to start lesson "+e.getMessage()); + } catch (ValidationException e) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to start lesson "+e.getMessage()); + } catch (ParserConfigurationException e) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to start lesson "+e.getMessage()); + } catch (SAXException e) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to start lesson "+e.getMessage()); + } catch (InitializationException e) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to start lesson "+e.getMessage()); + } catch (BbServiceException e) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to start lesson "+e.getMessage()); + } finally { + // make sure context is released + if (ctxMgr != null) { + ctxMgr.releaseContext(); + } + } + + } + +} + Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/UpdateServerUrlServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/UpdateServerUrlServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/UpdateServerUrlServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,126 @@ +/** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + */ + +/* $$ */ +package org.lamsfoundation.ld.integration.servlet; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lamsfoundation.ld.integration.Constants; +import org.lamsfoundation.ld.integration.util.BlackboardUtil; + +import blackboard.base.InitializationException; +import blackboard.data.ValidationException; +import blackboard.data.content.Content; +import blackboard.data.course.Course; +import blackboard.persist.BbPersistenceManager; +import blackboard.persist.Container; +import blackboard.persist.PersistenceException; +import blackboard.persist.PkId; +import blackboard.persist.content.ContentDbPersister; +import blackboard.platform.BbServiceException; +import blackboard.platform.BbServiceManager; +import blackboard.platform.context.ContextManager; +import blackboard.platform.persistence.PersistenceServiceFactory; +import blackboard.platform.plugin.PlugInException; +import blackboard.platform.plugin.PlugInUtil; + +/** + * Updates server urls for all LAMS lessons in the specified course. + */ +public class UpdateServerUrlServlet extends HttpServlet { + + private static final long serialVersionUID = 274843716397522792L; + private static Logger logger = LoggerFactory.getLogger(UpdateServerUrlServlet.class); + + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + // get Parameter values + String courseIdParam = request.getParameter(Constants.PARAM_COURSE_ID); + String oldUrlHost = request.getParameter("oldUrlHost"); + String newUrlHost = request.getParameter("newUrlHost"); + + // check parameters + if (courseIdParam == null || oldUrlHost == null || newUrlHost == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "missing expected parameters"); + return; + } + + ContextManager ctxMgr = null; + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + try { + // check permission + if (!PlugInUtil.authorizeForSystemAdmin(request, response)) { + return; + } + + // get Blackboard context + ctxMgr = (ContextManager) BbServiceManager.lookupService(ContextManager.class); + BbPersistenceManager bbPm = PersistenceServiceFactory.getInstance().getDbPersistenceManager(); + Container bbContainer = bbPm.getContainer(); + ContentDbPersister persister = ContentDbPersister.Default.getInstance(); + + PkId courseId = new PkId(bbContainer, Course.DATA_TYPE, courseIdParam); + + //find all lessons that should be updated + List<Content> lamsContents = BlackboardUtil.getLamsLessonsByCourse(courseId); + for (Content content : lamsContents) { + String oldUrl = content.getUrl(); + String newUrl = oldUrl.replaceFirst(oldUrlHost, newUrlHost); + content.setUrl(newUrl); + persister.persist(content); + + out.write("Old Url" + oldUrl + ". New url:" + newUrl + "\n\r"); + } + + } catch (PersistenceException e) { + throw new ServletException(e); + } catch (ValidationException e) { + throw new ServletException(e); + } catch (InitializationException e) { + throw new ServletException(e); + } catch (BbServiceException e) { + throw new ServletException(e); + } catch (PlugInException e) { + throw new ServletException(e); + } finally { + // make sure context is released + if (ctxMgr != null) { + ctxMgr.releaseContext(); + } + out.flush(); + out.close(); + } + + out.write("OK"); + } + +} Index: lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/UserDataServlet.java =================================================================== diff -u --- lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/UserDataServlet.java (revision 0) +++ lams_bb_integration/src/org/lamsfoundation/ld/integration/servlet/UserDataServlet.java (revision 7b88f06d088a1787ab489b521c8d20480096a9b0) @@ -0,0 +1,126 @@ +/** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + */ +package org.lamsfoundation.ld.integration.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.lamsfoundation.ld.integration.Constants; +import org.lamsfoundation.ld.integration.util.CSVUtil; +import org.lamsfoundation.ld.integration.util.LamsPluginUtil; +import org.lamsfoundation.ld.integration.util.LamsSecurityUtil; + +import blackboard.data.user.User; +import blackboard.persist.PersistenceException; +import blackboard.persist.user.UserDbLoader; + +/** + * @author <a href="mailto:anthony.xiao@lamsinternational.com">Anthony Xiao</a> + */ +public class UserDataServlet extends HttpServlet { + + private static final long serialVersionUID = 2L; + private static Logger logger = LoggerFactory.getLogger(UserDataServlet.class); + + /** + * The doGet method of the servlet. <br> + * + * This method is called when a form has its tag value method equals to get. + * + * @param request + * the request send by the client to the server + * @param response + * the response send by the server to the client + * @throws ServletException + * if an error occurred + * @throws IOException + * if an error occurred + */ + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + // get Parameter values + String usernameParam = request.getParameter(Constants.PARAM_USER_ID); + String tsParam = request.getParameter(Constants.PARAM_TIMESTAMP); + String hashParam = request.getParameter(Constants.PARAM_HASH); + + // check paramaeters + if (usernameParam == null || tsParam == null || hashParam == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "missing expected parameters"); + return; + } + + String secretKey = LamsPluginUtil.getServerSecretKey(); + String serverId = LamsPluginUtil.getServerId(); + + if (!LamsSecurityUtil.sha1( + tsParam.toLowerCase() + usernameParam.toLowerCase() + serverId.toLowerCase() + secretKey.toLowerCase()) + .equals(hashParam)) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "authentication failed"); + return; + } + + // get user list, but no role info since there are no course info + User user; + try { + UserDbLoader userLoader = UserDbLoader.Default.getInstance(); + user = userLoader.loadByUserName(usernameParam); + } catch (PersistenceException e) { + throw new ServletException(e); + } + + if (user == null) { + throw new ServletException("user not found"); + } + + // construct the address + String address = user.getStreet1() + (user.getStreet1().length() == 0 ? "" : " "); + address += user.getStreet2() + (address.length() == 0 ? "" : " "); + address += user.getState() + (address.length() == 0 ? "" : " "); + address += user.getCountry() + (address.length() == 0 ? "" : " "); + address += user.getZipCode(); + // String username = u.getUserName().replaceAll(); + + PrintWriter out = response.getWriter(); + + String locale = user.getLocale(); + String loc_lang = LamsSecurityUtil.getLanguage(locale); + String loc_cntry = LamsSecurityUtil.getCountry(locale); + + // The CSV list should be the format below + // <Title>,<First name>,<Last name>,<Address>,<City>,<State>, + // <Postcode>,<Country>,<Day time number>,<Mobile number>, + // <Fax number>,<Email>,<Locale language>,<Locale country> + String[] valList = { user.getTitle(), user.getGivenName(), user.getFamilyName(), + user.getStreet1() + user.getStreet2(), user.getCity(), user.getState(), user.getZipCode(), + user.getCountry(), user.getHomePhone1(), user.getMobilePhone(), user.getBusinessFax(), + user.getEmailAddress(), loc_lang, loc_cntry }; + + out.println(CSVUtil.write(valList)); + } + +}