Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -rf0f8be27b531db8008a87651d6d1e60d328973c5 -r4ab52d60ee545b9b76bcc65f75c5a400f6105c30 --- lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision f0f8be27b531db8008a87651d6d1e60d328973c5) +++ lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 4ab52d60ee545b9b76bcc65f75c5a400f6105c30) @@ -2,7 +2,7 @@ #language code: en #locale code: AU - # CVS ID: $Id$ Exported from the LAMS Community by Ernie Ghiglione on Wed Dec 19 07:57:25 CST 2012 + # CVS ID: $Id$ Exported from the LAMS Community by Ernie Ghiglione on Wed Dec 05 10:31:17 CST 2012 #=================== labels for LAMS Central =================# @@ -354,5 +354,36 @@ index.remove.lesson.confirm1 =You are about to remove this lesson. Are you sure? index.remove.lesson.confirm2 =Once you remove this lesson you CANNOT bring it back. Are you positive that you want to remove this lesson? +button.add.now =Add now +label.tab.lesson =Lesson +label.tab.class =Class +label.tab.advanced =Advanced +label.tab.lesson.title =Select the sequence to add a lesson, and click on Add now +label.tab.class.title =Please use drag n' drop to select or unselect monitors and learners +label.tab.lesson.name =Lesson name: +label.tab.lesson.size.full =(show full size) +label.tab.lesson.size.fit =(fit to window) +error.tab.lesson.sequence =A valid sequence must be selected +label.tab.class.monitors.unselected =Monitors unselected +label.tab.class.monitors.selected =Monitors selected +label.tab.class.learners.unselected =Learners unselected +label.tab.class.learners.selected =Learners selected +error.tab.class.learners =There must be at least 1 learner selected +error.tab.class.monitors =There must be at least 1 monitor selected +label.tab.advanced.details =Details +label.tab.advanced.intro.enable =Enable lesson intro +label.tab.advanced.intro.description =Description: +label.tab.advanced.intro.image =Display design image +label.tab.advanced.section.advanced =Advanced Options +label.tab.advanced.field.monitor =Start in Monitor +label.tab.advanced.field.liveedit =Enable Live Edit +label.tab.advanced.field.notification =Enable lesson notification +label.tab.advanced.field.export =Enable export portfolio for leaner +label.tab.advanced.field.presence =Allow learners to see who is online +label.tab.advanced.field.im =Enable Instant Messaging +label.tab.advanced.field.split =Split learners into separate copies of this lesson +label.tab.advanced.field.split.number =No. learners per lesson +label.tab.advanced.field.scheduling =Enable scheduling +label.tab.advanced.split.desc =[0] instances of this lesson will be created
and approximately [1] will be allocated to each lesson -#======= End labels: Exported 347 labels for en AU ===== +#======= End labels: Exported 345 labels for en AU ===== Index: lams_central/src/java/org/lamsfoundation/lams/web/HomeAction.java =================================================================== diff -u -re64a90c0cc74118c5b88f5cbae323065abd59cac -r4ab52d60ee545b9b76bcc65f75c5a400f6105c30 --- lams_central/src/java/org/lamsfoundation/lams/web/HomeAction.java (.../HomeAction.java) (revision e64a90c0cc74118c5b88f5cbae323065abd59cac) +++ lams_central/src/java/org/lamsfoundation/lams/web/HomeAction.java (.../HomeAction.java) (revision 4ab52d60ee545b9b76bcc65f75c5a400f6105c30) @@ -23,22 +23,31 @@ /* $$Id$$ */ package org.lamsfoundation.lams.web; +import java.io.FileInputStream; import java.io.IOException; +import java.io.OutputStream; import java.net.URLEncoder; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Vector; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import org.apache.batik.transcoder.TranscoderException; +import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.actions.DispatchAction; +import org.apache.tomcat.util.json.JSONException; +import org.apache.tomcat.util.json.JSONObject; +import org.jdom.JDOMException; +import org.lamsfoundation.lams.contentrepository.RepositoryCheckedException; import org.lamsfoundation.lams.learningdesign.GroupUser; import org.lamsfoundation.lams.learningdesign.dao.IGroupUserDAO; import org.lamsfoundation.lams.learningdesign.service.ILearningDesignService; @@ -47,20 +56,29 @@ import org.lamsfoundation.lams.usermanagement.Organisation; import org.lamsfoundation.lams.usermanagement.Role; import org.lamsfoundation.lams.usermanagement.User; +import org.lamsfoundation.lams.usermanagement.WorkspaceFolder; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; +import org.lamsfoundation.lams.usermanagement.dto.UserFlashDTO; +import org.lamsfoundation.lams.usermanagement.exception.UserAccessDeniedException; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; +import org.lamsfoundation.lams.util.CentralConstants; import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; +import org.lamsfoundation.lams.util.MessageService; import org.lamsfoundation.lams.util.WebUtil; import org.lamsfoundation.lams.util.svg.SVGGenerator; import org.lamsfoundation.lams.web.session.SessionManager; import org.lamsfoundation.lams.web.util.AttributeNames; +import org.lamsfoundation.lams.workspace.dto.FolderContentDTO; +import org.lamsfoundation.lams.workspace.service.IWorkspaceManagementService; +import org.lamsfoundation.lams.workspace.service.WorkspaceManagementService; +import org.lamsfoundation.lams.workspace.web.WorkspaceAction; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; /** - * this is an action where all lams client environments launch. initial - * configuration of the individual environment setting is done here. + * this is an action where all lams client environments launch. initial configuration of the individual environment + * setting is done here. * * @struts:action path="/home" validate="false" parameter="method" * @struts:action-forward name="sysadmin" path="/sysadmin.jsp" @@ -69,6 +87,7 @@ * @struts:action-forward name="author" path="/author.jsp" * @struts:action-forward name="monitorLesson" path="/monitorLesson.jsp" * @struts:action-forward name="addLesson" path="/addLesson.jsp" + * @struts:action-forward name="newLesson" path="/newLesson.jsp" * @struts:action-forward name="error" path=".error" * @struts:action-forward name="message" path=".message" * @struts:action-forward name="passwordChange" path=".passwordChange" @@ -83,6 +102,7 @@ private static ILessonService lessonService; private static ILearningDesignService learningDesignService; private static IGroupUserDAO groupUserDAO; + private static IWorkspaceManagementService workspaceManagementService; /** * request for sysadmin environment @@ -91,23 +111,23 @@ HttpServletResponse res) throws IOException, ServletException { try { - log.debug("request sysadmin"); + HomeAction.log.debug("request sysadmin"); int orgId = new Integer(req.getParameter("orgId")).intValue(); UserDTO user = getUser(); if (user == null) { - log.error("admin: User missing from session. "); + HomeAction.log.error("admin: User missing from session. "); return mapping.findForward("error"); } else if (getService().isUserInRole(user.getUserID(), orgId, Role.SYSADMIN)) { - log.debug("user is sysadmin"); + HomeAction.log.debug("user is sysadmin"); return mapping.findForward("sysadmin"); } else { - log.error("User " + user.getLogin() + HomeAction.log.error("User " + user.getLogin() + " tried to get sysadmin screen but isn't sysadmin in organisation: " + orgId); return displayMessage(mapping, req, "error.authorisation"); } } catch (Exception e) { - log.error("Failed to load sysadmin", e); + HomeAction.log.error("Failed to load sysadmin", e); return mapping.findForward("error"); } } @@ -119,67 +139,68 @@ throws IOException, ServletException { try { - log.debug("request learner"); + HomeAction.log.debug("request learner"); Long lessonId = WebUtil.readLongParam(req, AttributeNames.PARAM_LESSON_ID); String mode = WebUtil.readStrParam(req, AttributeNames.PARAM_MODE, true); UserDTO user = getUser(); if (user == null) { - log.error("learner: User missing from session. "); + HomeAction.log.error("learner: User missing from session. "); return mapping.findForward("error"); } else { Lesson lesson = lessonId != null ? getLessonService().getLesson(lessonId) : null; - if (lesson == null || !lesson.isLessonStarted()) { + if ((lesson == null) || !lesson.isLessonStarted()) { return displayMessage(mapping, req, "message.lesson.not.started.cannot.participate"); } if (!getLessonService().checkLessonReleaseConditions(lessonId, user.getUserID())) { return displayMessage(mapping, req, "message.preceding.lessons.not.finished.cannot.participate"); } - - if (lesson.getLessonClass() == null + if ((lesson.getLessonClass() == null) || !lesson.getLessonClass().getLearners().contains(getRealUser(user))) { - log.error("learner: User " + user.getLogin() + HomeAction.log.error("learner: User " + user.getLogin() + " is not a learner in the requested lesson. Cannot access the lesson."); return displayMessage(mapping, req, "error.authorisation"); } - - //check if the lesson is scheduled to be finished to individual users + + // check if the lesson is scheduled to be finished to individual users if (lesson.isScheduledToCloseForIndividuals()) { GroupUser groupUser = getGroupUserDAO().getGroupUser(lesson, user.getUserID()); - if ((groupUser != null) && (groupUser.getScheduledLessonEndDate() != null) && groupUser.getScheduledLessonEndDate().before(new Date())) { - log.error("learner: User " + user.getLogin() + if ((groupUser != null) && (groupUser.getScheduledLessonEndDate() != null) + && groupUser.getScheduledLessonEndDate().before(new Date())) { + HomeAction.log.error("learner: User " + user.getLogin() + " cannot access the lesson due to lesson end date has passed."); return displayMessage(mapping, req, "error.finish.date.passed"); } } - if (mode != null) + if (mode != null) { req.setAttribute(AttributeNames.PARAM_MODE, mode); + } req.setAttribute(AttributeNames.PARAM_EXPORT_PORTFOLIO_ENABLED, lesson.getLearnerExportAvailable() != null ? lesson.getLearnerExportAvailable() : Boolean.TRUE); req.setAttribute(AttributeNames.PARAM_PRESENCE_ENABLED, lesson.getLearnerPresenceAvailable()); req.setAttribute(AttributeNames.PARAM_PRESENCE_IM_ENABLED, lesson.getLearnerImAvailable()); req.setAttribute(AttributeNames.PARAM_TITLE, lesson.getLessonName()); - + /* Date Format for Chat room append */ DateFormat sfm = new SimpleDateFormat("yyyyMMdd_HHmmss"); req.setAttribute(AttributeNames.PARAM_CREATE_DATE_TIME, sfm.format(lesson.getCreateDateTime())); - + String serverUrl = Configuration.get(ConfigurationKeys.SERVER_URL); req.setAttribute("serverUrl", serverUrl); req.setAttribute(AttributeNames.PARAM_LESSON_ID, lessonId); - - //show lesson intro page if required + + // show lesson intro page if required if (lesson.isEnableLessonIntro()) { req.setAttribute("lesson", lesson); req.setAttribute("displayDesignImage", lesson.isDisplayDesignImage()); req.setAttribute("isMonitor", lesson.getLessonClass().isStaffMember(getRealUser(user))); - - //check if we need to create learning design SVG + + // check if we need to create learning design SVG if (lesson.isDisplayDesignImage()) { Long learningDesignId = lesson.getLearningDesign().getLearningDesignId(); req.setAttribute(AttributeNames.PARAM_LEARNINGDESIGN_ID, learningDesignId); @@ -190,17 +211,17 @@ SVGGenerator.OUTPUT_FORMAT_SVG); getLearningDesignService().createLearningDesignSVG(learningDesignId, SVGGenerator.OUTPUT_FORMAT_PNG); - } + } } return mapping.findForward("lessonIntro"); } else { return mapping.findForward("learner"); } - + } } catch (Exception e) { - log.error("Failed to load learner", e); + HomeAction.log.error("Failed to load learner", e); return mapping.findForward("error"); } } @@ -212,10 +233,10 @@ throws IOException, ServletException { try { - log.debug("request author"); + HomeAction.log.debug("request author"); UserDTO user = getUser(); if (user == null) { - log.error("admin: User missing from session. "); + HomeAction.log.error("admin: User missing from session. "); return mapping.findForward("error"); } else { Long learningDesignID = null; @@ -229,20 +250,25 @@ String customCSV = req.getParameter(AttributeNames.PARAM_CUSTOM_CSV); String extLmsId = req.getParameter(AttributeNames.PARAM_EXT_LMS_ID); - if (req.getParameter("learningDesignID") != null) + if (req.getParameter("learningDesignID") != null) { learningDesignID = WebUtil.readLongParam(req, "learningDesignID"); + } - if (req.getParameter("layout") != null) + if (req.getParameter("layout") != null) { layout = WebUtil.readStrParam(req, "layout"); + } - if (layout != null) + if (layout != null) { req.setAttribute("layout", layout); + } - if (req.getParameter("learningDesignID") != null) + if (req.getParameter("learningDesignID") != null) { learningDesignID = WebUtil.readLongParam(req, "learningDesignID"); + } - if (learningDesignID != null) + if (learningDesignID != null) { req.setAttribute("learningDesignID", learningDesignID); + } req.setAttribute("requestSrc", requestSrc); req.setAttribute("notifyCloseURL", notifyCloseURL); @@ -254,7 +280,7 @@ } } catch (Exception e) { - log.error("Failed to load author", e); + HomeAction.log.error("Failed to load author", e); return mapping.findForward("error"); } } @@ -266,34 +292,36 @@ HttpServletResponse res) throws IOException, ServletException { try { - log.debug("request monitorLesson"); + HomeAction.log.debug("request monitorLesson"); Long lessonId = WebUtil.readLongParam(req, AttributeNames.PARAM_LESSON_ID); UserDTO user = getUser(); if (user == null) { - log.error("admin: User missing from session. "); + HomeAction.log.error("admin: User missing from session. "); return mapping.findForward("error"); } else { Lesson lesson = lessonId != null ? getLessonService().getLesson(lessonId) : null; if (lesson == null) { - log.error("monitorLesson: Lesson " + lessonId + " does not exist. Unable to monitor lesson"); + HomeAction.log.error("monitorLesson: Lesson " + lessonId + + " does not exist. Unable to monitor lesson"); return mapping.findForward("error"); } - if (lesson.getLessonClass() == null || !lesson.getLessonClass().isStaffMember(getRealUser(user)) - && !req.isUserInRole(Role.GROUP_MANAGER)) { - log.error("learner: User " + user.getLogin() + if ((lesson.getLessonClass() == null) + || (!lesson.getLessonClass().isStaffMember(getRealUser(user)) && !req + .isUserInRole(Role.GROUP_MANAGER))) { + HomeAction.log.error("learner: User " + user.getLogin() + " is not a learner in the requested lesson. Cannot access the lesson."); return displayMessage(mapping, req, "error.authorisation"); } - log.debug("user is staff"); + HomeAction.log.debug("user is staff"); String serverUrl = Configuration.get(ConfigurationKeys.SERVER_URL); req.setAttribute("serverUrl", serverUrl); req.setAttribute(AttributeNames.PARAM_LESSON_ID, lessonId); return mapping.findForward("monitorLesson"); } } catch (Exception e) { - log.error("Failed to load monitor lesson", e); + HomeAction.log.error("Failed to load monitor lesson", e); return mapping.findForward("error"); } } @@ -305,60 +333,187 @@ HttpServletResponse res) throws IOException, ServletException { try { - log.debug("request addLesson"); - + HomeAction.log.debug("request addLesson"); + Integer courseId = WebUtil.readIntParam(req, AttributeNames.PARAM_COURSE_ID, false); Integer classId = WebUtil.readIntParam(req, AttributeNames.PARAM_CLASS_ID, true); - + UserDTO user = getUser(); if (user == null) { - log.error("admin: User missing from session. "); + HomeAction.log.error("admin: User missing from session. "); return mapping.findForward("error"); } else { Integer orgId = classId != null ? classId : courseId; if (getService().isUserInRole(user.getUserID(), orgId, Role.MONITOR) || getService().isUserInRole(user.getUserID(), orgId, Role.GROUP_MANAGER)) { - log.debug("user is staff"); + HomeAction.log.debug("user is staff"); String orgName = ((Organisation) getService().findById(Organisation.class, orgId)).getName(); - + req.setAttribute(AttributeNames.PARAM_ORGANISATION_ID, orgId); req.setAttribute(AttributeNames.PARAM_ORGANISATION_NAME, orgName); - + return mapping.findForward("addLesson"); } else { - log.error("User " + user.getLogin() + HomeAction.log.error("User " + user.getLogin() + " tried to get staff screen but isn't staff in organisation: " + orgId); return displayMessage(mapping, req, "error.authorisation"); } } } catch (Exception e) { - log.error("Failed to load add lesson", e); + HomeAction.log.error("Failed to load add lesson", e); return mapping.findForward("error"); } } + public ActionForward newLesson(ActionMapping mapping, ActionForm form, HttpServletRequest req, + HttpServletResponse res) throws IOException, UserAccessDeniedException, JSONException, + RepositoryCheckedException { + UserDTO userDTO = getUser(); + + // get all user accessible folders and LD descriptions as JSON + JSONObject learningDesigns = getDeepFolderContents(userDTO.getUserID(), null); + req.setAttribute("folderContents", learningDesigns.toString()); + + Integer organisationID = new Integer(WebUtil.readIntParam(req, "organisationID")); + JSONObject users = new JSONObject(); + + // get learners available for newly created lesson + Vector learners = getWorkspaceManagementService().getUsersFromOrganisationByRole(organisationID, + "LEARNER"); + for (UserFlashDTO user : learners) { + JSONObject userJSON = new JSONObject(); + userJSON.put("userID", user.getUserID()); + userJSON.put("firstName", user.getFirstName()); + userJSON.put("lastName", user.getLastName()); + userJSON.put("login", user.getLogin()); + + users.append("selectedLearners", userJSON); + } + + Vector monitors = getWorkspaceManagementService().getUsersFromOrganisationByRole(organisationID, + "MONITOR"); + for (UserFlashDTO user : monitors) { + JSONObject userJSON = new JSONObject(); + userJSON.put("userID", user.getUserID()); + userJSON.put("firstName", user.getFirstName()); + userJSON.put("lastName", user.getLastName()); + userJSON.put("login", user.getLogin()); + + if (userDTO.getUserID().equals(user.getUserID())) { + // creator is always selected + users.append("selectedMonitors", userJSON); + } else { + users.append("unselectedMonitors", userJSON); + } + } + + req.setAttribute("users", users.toString()); + + return mapping.findForward("newLesson"); + } + + public ActionForward createLearningDesignThumbnail(ActionMapping mapping, ActionForm form, HttpServletRequest req, + HttpServletResponse res) throws JDOMException, IOException, TranscoderException { + Long learningDesignId = WebUtil.readLongParam(req, CentralConstants.PARAM_LEARNING_DESIGN_ID); + String imagePath = getLearningDesignService().createLearningDesignSVG(learningDesignId, + SVGGenerator.OUTPUT_FORMAT_PNG); + + res.setContentType("image/png"); + OutputStream output = res.getOutputStream(); + FileInputStream input = new FileInputStream(imagePath); + IOUtils.copy(input, output); + IOUtils.closeQuietly(input); + IOUtils.closeQuietly(output); + + return null; + } + + @SuppressWarnings("unchecked") + private JSONObject getDeepFolderContents(Integer userID, Integer folderID) throws JSONException, IOException, + UserAccessDeniedException, RepositoryCheckedException { + JSONObject result = new JSONObject(); + Vector folderContents = null; + + // get use accessible folders in the start + if (folderID == null) { + folderContents = new Vector(3); + MessageService msgService = getWorkspaceManagementService().getMessageService(); + + FolderContentDTO userFolder = getWorkspaceManagementService().getUserWorkspaceFolder(userID); + if (userFolder != null) { + folderContents.add(userFolder); + } + + FolderContentDTO myGroupsFolder = new FolderContentDTO(msgService.getMessage("organisations"), + msgService.getMessage("folder"), null, null, FolderContentDTO.FOLDER, + WorkspaceAction.ORG_FOLDER_ID.longValue(), WorkspaceFolder.READ_ACCESS, null); + + folderContents.add(myGroupsFolder); + + FolderContentDTO publicFolder = getWorkspaceManagementService().getPublicWorkspaceFolder(userID); + if (publicFolder != null) { + folderContents.add(publicFolder); + } + // special behaviour for organisation folders + } else if (folderID.equals(WorkspaceAction.ORG_FOLDER_ID)) { + folderContents = getWorkspaceManagementService().getAccessibleOrganisationWorkspaceFolders(userID); + + if (folderContents.size() == 1) { + FolderContentDTO folder = folderContents.firstElement(); + if (folder.getResourceID().equals(WorkspaceAction.ROOT_ORG_FOLDER_ID)) { + return getDeepFolderContents(userID, WorkspaceAction.ROOT_ORG_FOLDER_ID); + } + } + } else { + WorkspaceFolder folder = getWorkspaceManagementService().getWorkspaceFolder(folderID); + folderContents = getWorkspaceManagementService().getFolderContents(userID, folder, + WorkspaceManagementService.AUTHORING); + } + + // recursively check folders, building a tree + for (FolderContentDTO folderContent : folderContents) { + String contentType = folderContent.getResourceType(); + if (FolderContentDTO.FOLDER.equals(contentType)) { + JSONObject subfolder = getDeepFolderContents(userID, folderContent.getResourceID().intValue()); + subfolder.put("name", folderContent.getName()); + result.append("folders", subfolder); + } else if (FolderContentDTO.DESIGN.equals(contentType)) { + JSONObject learningDesign = new JSONObject(); + learningDesign.put("learningDesignId", folderContent.getResourceID()); + learningDesign.put("name", folderContent.getName()); + result.append("learningDesigns", learningDesign); + } else { + if (HomeAction.log.isDebugEnabled()) { + HomeAction.log.debug("Unsupported folder content found, named \"" + folderContent.getName() + "\""); + } + } + } + + return result; + } + public ActionForward logout(ActionMapping mapping, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { UserDTO userDTO = (UserDTO) SessionManager.getSession().getAttribute(AttributeNames.USER); - if (userDTO.getLoggedIntoLamsCommunity() != null && userDTO.getLoggedIntoLamsCommunity()) { - log.debug("Need to log out user from lamscoomunity"); + if ((userDTO.getLoggedIntoLamsCommunity() != null) && userDTO.getLoggedIntoLamsCommunity()) { + HomeAction.log.debug("Need to log out user from lamscoomunity"); req.getSession().invalidate(); - //clear system shared session. + // clear system shared session. SessionManager.getSession().invalidate(); - + // redirect to lamscommunity logout servlet to log out. String url = "http://lamscommunity.org/register/logout?return_url="; url += URLEncoder.encode(Configuration.get(ConfigurationKeys.SERVER_URL), "UTF8"); res.sendRedirect(url); return null; - + } else { req.getSession().invalidate(); - //clear system shared session. + // clear system shared session. SessionManager.getSession().invalidate(); return mapping.findForward("index"); @@ -369,43 +524,54 @@ req.setAttribute("messageKey", messageKey); return mapping.findForward("message"); } - + private IUserManagementService getService() { - if (service == null) { + if (HomeAction.service == null) { WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() .getServletContext()); - service = (IUserManagementService) ctx.getBean("userManagementService"); + HomeAction.service = (IUserManagementService) ctx.getBean("userManagementService"); } - return service; + return HomeAction.service; } private ILessonService getLessonService() { - if (lessonService == null) { + if (HomeAction.lessonService == null) { WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() .getServletContext()); - lessonService = (ILessonService) ctx.getBean("lessonService"); + HomeAction.lessonService = (ILessonService) ctx.getBean("lessonService"); } - return lessonService; + return HomeAction.lessonService; } - + private ILearningDesignService getLearningDesignService() { - if (learningDesignService == null) { + if (HomeAction.learningDesignService == null) { WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() .getServletContext()); - learningDesignService = (ILearningDesignService) ctx.getBean("learningDesignService"); + HomeAction.learningDesignService = (ILearningDesignService) ctx.getBean("learningDesignService"); } - return learningDesignService; + return HomeAction.learningDesignService; } - + private IGroupUserDAO getGroupUserDAO() { - if (groupUserDAO == null) { + if (HomeAction.groupUserDAO == null) { WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() .getServletContext()); - groupUserDAO = (IGroupUserDAO) ctx.getBean("groupUserDAO"); + HomeAction.groupUserDAO = (IGroupUserDAO) ctx.getBean("groupUserDAO"); } - return groupUserDAO; + return HomeAction.groupUserDAO; } + private IWorkspaceManagementService getWorkspaceManagementService() { + if (HomeAction.workspaceManagementService == null) { + WebApplicationContext webContext = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() + .getServletContext()); + HomeAction.workspaceManagementService = (IWorkspaceManagementService) webContext + .getBean("workspaceManagementService"); + } + + return HomeAction.workspaceManagementService; + } + private UserDTO getUser() { HttpSession ss = SessionManager.getSession(); return (UserDTO) ss.getAttribute(AttributeNames.USER); Index: lams_central/web/css/newLesson.css =================================================================== diff -u --- lams_central/web/css/newLesson.css (revision 0) +++ lams_central/web/css/newLesson.css (revision 4ab52d60ee545b9b76bcc65f75c5a400f6105c30) @@ -0,0 +1,202 @@ +div#tabs { + width: 578px; + height: 450px; +} + +div#tabs > div { + height: 420px; +} + +.ui-tabs .ui-tabs-panel { + padding: 0px; +} + +div#tabs ul li a, a.ygtvspacer, a#closeButton, a#addButton { + border-bottom: none; +} + +a#closeButton { + position: absolute; + right: 10px; + top: 11px; + width: 19px; + height: 18px; +} + +a#closeButton span { + background-color: #D0E5F5; +} + +a#addButton { + position: absolute; + right: 60px; + top: 6px; + width: 80px; + padding: 5px 0 5px 25px; + vertical-align: center; + font-weight:bolder; + border: thin solid #2E6E9E; + background-color: #D0E5F5; +} + +table.tabTable { + height: 393px; + table-layout: fixed; + border-top: thin dotted #2E6E9E; +} + +div.tabTitle { + padding: 5px 0px 5px 0px; + font-size: small; + font-weight: bold; + text-align: center; +} + +.errorMessage { + font-weight: bold; + color: red; +} + +.errorField { + border: thick solid red; +} + + +td#learningDesignTreeCell { + padding: 2px 2px 0px 5px; + vertical-align: top; + width: 30%; + border-right: thin dotted #2E6E9E; +} + +.ygtv-highlight1, .ygtv-highlight1 .ygtvlabel { + background-color: #dfeffc; +} + +td#canvasControlCell { + padding: 2px 0px 0px 10px; + height: 15px; +} + +td#canvasControlCell a { + color: #47BC23; +} + +td#canvasCell { + text-align: center; + padding: 10px 0px 0px 10px; +} + +div#canvasDiv { + overflow: visible; + height: 330px; +} + +.ldChoiceDependentCanvasElement { + display: none; +} + +td#lessonNameCell { + height: 20px; + padding: 0px 0px 5px 10px; +} + +#lessonNameInput { + width: 250px; + margin-left: 10px; +} + + +table#classTable td { + width: 50%; + vertical-align: top; + padding: 5px 10px 0px 10px; +} + +.draggableUser { + padding: 3px 0px 3px 0px; + cursor: default; +} + +.userContainer { + height: 150px; + overflow: auto; + border: thin solid black; + padding : 5px; +} + +.userContainerTitle { + padding: 0px 0px 5px 0px; + height: 15px; + font-size: small; + font-weight: bold; + text-align: center; +} + +.droppableHighlight { + padding : 1px; + border: 5px solid #5c9ccc; +} + +.draggableUserSelected { + background-color: #5c9ccc !important; + color: white !important; +} + + +div#tabAdvanced { + padding-left: 30px; +} + +div#tabAdvanced input[type="checkbox"] { + margin: 5px 5px 0px 0px; + border: none; +} + +.fieldSectionTitle { + padding: 15px 0px 5px 15px; + font-size: small; + font-weight: bold; +} + +.fieldInnerSection { + padding-left: 30px; +} + +div#introSection { + padding-top: 5px; +} + +div#introSection span { + vertical-align: top; +} + +textarea#introDescriptionField { + width: 200px; +} + +table#splitLearnersTable { + height: 40px; +} + +td#splitLearnersCell { + padding: 0px 0px 0px 30px; +} + +#splitLearnersCountField { + width: 50px; + margin: 0px 0px 0px 5px !important; +} + +td#splitLearnersDescriptionCell { + padding: 0px; +} + +span#splitLearnersDescription { + font-style: italic; + display: none; +} + +#schedulingDatetimeField { + margin-left: 40px; +} \ No newline at end of file Index: lams_central/web/groupHeader.jsp =================================================================== diff -u -r179b09b713c13ec35988ac7c95b9daf603160c5c -r4ab52d60ee545b9b76bcc65f75c5a400f6105c30 --- lams_central/web/groupHeader.jsp (.../groupHeader.jsp) (revision 179b09b713c13ec35988ac7c95b9daf603160c5c) +++ lams_central/web/groupHeader.jsp (.../groupHeader.jsp) (revision 4ab52d60ee545b9b76bcc65f75c5a400f6105c30) @@ -18,7 +18,7 @@ - + @@ -50,5 +50,10 @@ +
  • + + + +
  • Index: lams_central/web/images/ajax-loader-big.gif =================================================================== diff -u -r758651ea72efb26e2d20756999cdbe2cf3a4e957 -r4ab52d60ee545b9b76bcc65f75c5a400f6105c30 Binary files differ Index: lams_central/web/includes/javascript/newLesson.js =================================================================== diff -u --- lams_central/web/includes/javascript/newLesson.js (revision 0) +++ lams_central/web/includes/javascript/newLesson.js (revision 4ab52d60ee545b9b76bcc65f75c5a400f6105c30) @@ -0,0 +1,392 @@ +// ********** MAIN FUNCTIONS ********** + +function initLessonTab(){ + $('#ldScreenshotAuthor').load(function(){ + // hide "loading" animation + $('.ldChoiceDependentCanvasElement').css('display', 'none'); + // show the thumbnail + $('#ldScreenshotAuthor').css('display', 'inline'); + // resize if needed + var resized = resizeImage('ldScreenshotAuthor', 400, null); + toggleCanvasResize(resized ? CANVAS_RESIZE_OPTION_FIT + : CANVAS_RESIZE_OPTION_NONE); + }); + + // generate LD tree; folderContents is declared in newLesson.jsp + var treeNodes = parseFolderTreeNode(folderContents); + // there should be no focus, just highlight + YAHOO.widget.TreeView.FOCUS_CLASS_NAME = null; + tree = new YAHOO.widget.TreeView('learningDesignTreeCell', treeNodes); + tree.singleNodeHighlight = true; + tree.subscribe('clickEvent', function(event){ + if (!event.node.data.learningDesignId){ + // it is a folder + return false; + } + + $('#lessonNameInput').val(event.node.label); + + // display "loading" animation and finally LD thumbnail + $('.ldChoiceDependentCanvasElement').css('display', 'none'); + if (event.node.highlightState == 0) { + $('#ldScreenshotLoading').css('display', 'inline'); + $('#ldScreenshotAuthor').attr('src', LD_THUMBNAIL_URL_BASE + event.node.data.learningDesignId); + $('#ldScreenshotAuthor').css('width', 'auto').css('height', 'auto'); + } + }); + tree.subscribe('clickEvent',tree.onEventToggleHighlight); + tree.render(); + + // if empty folders were empty in the start, they would have been displayed as leafs + // instead, there is a dummy element in the start which is removed + // when user opens the folder + var emptyFolderNodes = tree.getNodesBy(function(node){ + var firstNode = node.children[0]; + return firstNode && firstNode.data.isDummy; + }); + + $.each(emptyFolderNodes, function(){ + this.setDynamicLoad(function(node, callback){ + tree.removeChildren(node); + callback(); + }); + }); + + tree.getRoot().children[0].expand(); +} + + +function initClassTab(){ + // users variable is declared in newLesson.jsp + fillUserContainer(users.selectedLearners, 'selected-learners'); + fillUserContainer(users.unselectedLearners, 'unselected-learners'); + fillUserContainer(users.selectedMonitors, 'selected-monitors'); + fillUserContainer(users.unselectedMonitors, 'unselected-monitors'); + + $('.draggableUser').each(function(){ + $(this).draggable({ 'scope' : getDraggableScope($(this).parents('.userContainer').attr('id')), + 'appendTo' : 'body', + 'containment' : '#classTable', + 'revert' : 'invalid', + 'distance' : 20, + 'scroll' : false, + 'cursor' : 'move', + 'helper' : function(event){ + // include the user from which dragging started + $(this).addClass('draggableUserSelected'); + + // copy selected users + var helperContainer = $('
    '); + $(this).siblings('.draggableUserSelected').andSelf().each(function(){ + $(this).clone().appendTo(helperContainer); + }); + return helperContainer; + } + }).click(function(event){ + var wasSelected = $(this).hasClass('draggableUserSelected'); + var parentId = $(this).parent().attr('id'); + // this is needed for shift+click + var lastSelectedUser = lastSelectedUsers[parentId]; + + if (event.shiftKey && lastSelectedUser && lastSelectedUser != this) { + // clear current selection + $(this).siblings().andSelf().removeClass('draggableUserSelected'); + + // find range of users to select + var lastSelectedIndex = $(lastSelectedUser).index(); + var index = $(this).index(); + + var startingElem = lastSelectedIndex > index ? this : lastSelectedUser; + var endingElem = lastSelectedIndex > index ? lastSelectedUser : this; + + $(startingElem).nextUntil(endingElem).andSelf().add(endingElem) + .addClass('draggableUserSelected'); + } else { + if (!event.ctrlKey) { + // clear current sleection + $(this).siblings().andSelf().removeClass('draggableUserSelected'); + } + + if (wasSelected && !event.shiftKey){ + $(this).removeClass('draggableUserSelected'); + lastSelectedUsers[parentId] = null; + } else { + $(this).addClass('draggableUserSelected'); + lastSelectedUsers[parentId] = this; + } + } + }); + }); + + $('.userContainer').each(function(){ + colorDraggableUsers(this); + + $(this).droppable({'scope' : $(this).attr('id'), + 'activeClass' : 'droppableHighlight', + 'tolerance' : 'touch', + 'accept' : function (draggable) { + // forbid current user from being removed from monitors + return $(this).attr('id') != 'unselected-monitors' + || $(draggable).attr('userId') != userId; + }, + 'drop' : function (event, ui) { + // remove error message, if exists + $(this).children('.errorMessage').remove(); + + // move the selected users + var previousContainer = ui.draggable.parent(); + previousContainer.children('.draggableUserSelected') + .css({'top' : '0px', + 'left' : '0px', + }) + .draggable('option', 'scope', getDraggableScope($(this).attr('id'))) + .appendTo(this); + + // recolour both containers + $(this).children().removeClass('draggableUserSelected'); + colorDraggableUsers(this); + colorDraggableUsers(previousContainer); + + if ($(this).attr('id').indexOf('learners') > 0) { + // number of selected learners changed, so update this control too + updateSplitLearnersFields(); + } + } + }); + }); +} + + +function initAdvancedTab(){ + $('#splitLearnersCountField').spinner({ + 'disabled' : true, + 'incremental' : false, + 'min' : 1, + 'max' : users.selectedLearners ? users.selectedLearners.length : 1, + 'stop' : updateSplitLearnersFields + }).spinner('value', 1); + + $('#splitLearnersField').change(function(){ + updateSplitLearnersFields(); + }); + + $('#introEnableField').change(function(){ + $('#introSection input, #introSection textarea').prop('disabled', !$(this).is(':checked')); + }); + + $('#presenceEnableField').change(function(){ + $('#imEnableField').prop('disabled', !$(this).is(':checked')); + }); + + $('#schedulingEnableField').change(function(){ + $('#schedulingDatetimeField').prop('disabled', !$(this).is(':checked')); + }); + + $('#startMonitorField').change(function(){ + var checked = !$(this).is(':checked'); + var schedulingEnableField = $('#schedulingEnableField'); + if (!checked) { + schedulingEnableField.attr('checked', false); + $('#schedulingDatetimeField').prop('disabled', true); + } + schedulingEnableField.prop('disabled', !checked); + }); + + $('#schedulingDatetimeField').datetimepicker(); +} + + +function addLesson(){ + // some validation at first + var lessonName = $('#lessonNameInput').val(); + if (lessonName){ + $('#lessonNameInput').removeClass('errorField'); + } + + var ldNode = tree.getHighlightedNode(); + if (!ldNode || !ldNode.data.learningDesignId) { + $('#ldNotChosenError').show(); + $('#tabs').tabs('option', 'selected', 0); + return; + } + $('#ldIdField').val(ldNode.data.learningDesignId); + + if (!lessonName){ + $('#lessonNameInput').addClass('errorField'); + $('#tabs').tabs('option', 'selected', 0); + return; + } + $('#lessonNameField').val(lessonName); + + + var learners = getSelectedUserList('selected-learners'); + if (learners == ''){ + $('
    ').addClass('errorMessage') + .text(LABEL_MISSING_LEARNERS) + .appendTo('#selected-learners'); + $('#tabs').tabs('option', 'selected', 1); + return; + } + $('#learnersField').val(learners); + + var monitors = getSelectedUserList('selected-monitors'); + if (monitors == ''){ + $('
    ').addClass('errorMessage') + .text(LABEL_MISSING_MONITORS) + .appendTo('#selected-monitors'); + $('#tabs').tabs('option', 'selected', 1); + return; + } + $('#monitorsField').val(learners); + + if ($('#splitLearnersField').is(':checked')) { + var maxLearnerCount = $('#selected-learners div.draggableUser').length; + var learnerCount = $('#splitLearnersCountField').spinner('value'); + var instances = Math.ceil(maxLearnerCount/learnerCount); + $('#splitNumberLessonsField').val(instances); + } + + $('#lessonForm').ajaxSubmit({ + 'success' : function(){ + window.parent.closeAddLessonDialog(true); + }}); +} + +// ********** LESSON TAB FUNCTIONS ********** + +function resizeImage(id, width, height) { + var resized = false; + var elem = $('#' + id); + + if (width != null && elem.width() > width) { + elem.css('width', width); + resized = true; + } + if (height != null && elem.height() > height) { + elem.css('height', height); + resized = true; + } + return resized; +} + + +function toggleCanvasResize(mode) { + var toggleCanvasResizeLink = $('#toggleCanvasResizeLink'); + switch (mode) { + case CANVAS_RESIZE_OPTION_NONE: + toggleCanvasResizeLink.css('display', 'none'); + $('#canvasDiv').css('overflow', 'visible'); + break; + case CANVAS_RESIZE_OPTION_FIT: + toggleCanvasResizeLink.html(CANVAS_RESIZE_LABEL_FULL).one('click', + function() { + toggleCanvasResize(CANVAS_RESIZE_OPTION_FULL) + }); + toggleCanvasResizeLink.css('display', 'inline'); + resizeImage('ldScreenshotAuthor', 385, null); + $('#canvasDiv').css('overflow', 'visible'); + break; + case CANVAS_RESIZE_OPTION_FULL: + toggleCanvasResizeLink.html(CANVAS_RESIZE_LABEL_FIT).one('click', + function() { + toggleCanvasResize(CANVAS_RESIZE_OPTION_FIT) + }); + toggleCanvasResizeLink.css('display', 'inline'); + $('#ldScreenshotAuthor').css('width', 'auto').css('height', 'auto'); + $('#canvasDiv').css('overflow', 'auto'); + break; + } +} + + +function parseFolderTreeNode(nodeJSON) { + var result = []; + + if (!nodeJSON.folders && !nodeJSON.learningDesigns) { + // add dummy node, otherwise empty folder is displayed as a leaf + result.push({'type' : 'text', + 'label' : '(dummy)', + 'isDummy' : true + }); + } else { + if (nodeJSON.folders) { + $.each(nodeJSON.folders, function(){ + result.push({'type' : 'text', + 'label' : this.name, + 'children' : parseFolderTreeNode(this) + }); + }); + } + if (nodeJSON.learningDesigns) { + $.each(nodeJSON.learningDesigns, function(){ + result.push({'type' : 'text', + 'label' : this.name, + 'learningDesignId' : this.learningDesignId + }); + }); + } + } + + return result; +} + +// ********** CLASS TAB FUNCTIONS ********** + +function fillUserContainer(users, containerID) { + if (users) { + // create user DIVs + $.each(users, function(index, userJSON) { + $('#' + containerID).append($('
    ').attr('userId', userJSON.userID) + .addClass('draggableUser') + .text(userJSON.firstName + ' ' + userJSON.lastName + + ' (' + userJSON.login + ')') + ); + }); + } +} + + +function getDraggableScope(containerId) { + // switch scope to opposite after drop, so user can be dragged back if needed + var scopeParts = containerId.split('-'); + return (scopeParts[0] == 'selected' ? 'unselected' : 'selected') + '-' + scopeParts[1]; +} + + +function colorDraggableUsers(container) { + // every second line is different + $(container).find('div').each(function(index, userDiv){ + // exact colour should be defined in CSS, but it's easier this way... + $(this).css('background-color', index % 2 ? '#dfeffc' : 'inherit'); + }); +} + + +function getSelectedUserList(containerId) { + var list = ''; + $('#' + containerId).children('div.draggableUser').each(function(){ + list += $(this).attr('userId') + ','; + }); + return list; +} + +// ********** ADVANCED TAB FUNCTIONS ********** + +function updateSplitLearnersFields(){ + var splitEnabled = $('#splitLearnersField').is(':checked'); + if (splitEnabled) { + $('#splitLearnersCountField').spinner('enable'); + + // put users into groups + var maxLearnerCount = $('#selected-learners div.draggableUser').length; + var learnerCount = $('#splitLearnersCountField').spinner('option', 'max', maxLearnerCount < 1 ? 1 : maxLearnerCount) + .spinner('value'); + var instances = Math.ceil(maxLearnerCount/learnerCount); + learnerCount = Math.ceil(maxLearnerCount/instances); + var description = SPLIT_LEARNERS_DESCRIPTION.replace('[0]', instances).replace('[1]', learnerCount); + $('#splitLearnersDescription').html(description).show(); + } else { + $('#splitLearnersCountField').spinner('disable'); + $('#splitLearnersDescription').hide(); + } +} \ No newline at end of file Index: lams_central/web/main.jsp =================================================================== diff -u -r0a29fecdac875617ddbb0f0dda8d098e63991c50 -r4ab52d60ee545b9b76bcc65f75c5a400f6105c30 --- lams_central/web/main.jsp (.../main.jsp) (revision 0a29fecdac875617ddbb0f0dda8d098e63991c50) +++ lams_central/web/main.jsp (.../main.jsp) (revision 4ab52d60ee545b9b76bcc65f75c5a400f6105c30) @@ -29,17 +29,39 @@ + + + - - - +