Index: lams_build/lib/lams/lams.jar =================================================================== diff -u -r18e768db89ac085de499f22cc961bed9a5785581 -r1b117caf4135f53248542cbc97d71aac448f3de9 Binary files differ Index: lams_central/src/java/org/lamsfoundation/lams/web/HomeAction.java =================================================================== diff -u -r679fe3220201c33cc6396ef0b3c0592f1da293f6 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_central/src/java/org/lamsfoundation/lams/web/HomeAction.java (.../HomeAction.java) (revision 679fe3220201c33cc6396ef0b3c0592f1da293f6) +++ lams_central/src/java/org/lamsfoundation/lams/web/HomeAction.java (.../HomeAction.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -420,9 +420,14 @@ Long learningDesignId = WebUtil.readLongParam(req, CentralConstants.PARAM_LEARNING_DESIGN_ID); Integer format = WebUtil.readIntParam(req, CentralConstants.PARAM_SVG_FORMAT, true); format = format == null ? SVGGenerator.OUTPUT_FORMAT_PNG : format; + Long branchingActivityId = WebUtil.readLongParam(req, "branchingActivityID", true); + String imagePath = null; + if (branchingActivityId == null) { + imagePath = getLearningDesignService().createLearningDesignSVG(learningDesignId, format); + } else { + imagePath = getLearningDesignService().createBranchingSVG(branchingActivityId, format); + } - String imagePath = getLearningDesignService().createLearningDesignSVG(learningDesignId, format); - res.setContentType(format == SVGGenerator.OUTPUT_FORMAT_PNG ? "image/png" : "image/svg+xml"); OutputStream output = res.getOutputStream(); FileInputStream input = new FileInputStream(imagePath); Index: lams_central/src/java/org/lamsfoundation/lams/webservice/LearningDesignSVGServlet.java =================================================================== diff -u -r27a9e58b46446d891cf6b081e8fb6fb6f6294e2e -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_central/src/java/org/lamsfoundation/lams/webservice/LearningDesignSVGServlet.java (.../LearningDesignSVGServlet.java) (revision 27a9e58b46446d891cf6b081e8fb6fb6f6294e2e) +++ lams_central/src/java/org/lamsfoundation/lams/webservice/LearningDesignSVGServlet.java (.../LearningDesignSVGServlet.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -18,9 +18,9 @@ * * http://www.gnu.org/licenses/gpl.txt * **************************************************************** - */ - -/* $Id$ */ + */ + +/* $Id$ */ package org.lamsfoundation.lams.webservice; import java.io.File; @@ -51,7 +51,7 @@ private static Logger log = Logger.getLogger(LearningDesignSVGServlet.class); private static IntegrationService integrationService = null; - + private ILearningDesignService learningDesignService; /** @@ -60,13 +60,13 @@ * 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 + * the request send by the client to the server * @param response - * the response send by the server to the client + * the response send by the server to the client * @throws ServletException - * if an error occurred + * if an error occurred * @throws IOException - * if an error occurred + * if an error occurred */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { @@ -87,7 +87,8 @@ } // check imageFormat parameter is correct - if (!(imageFormat == SVGGenerator.OUTPUT_FORMAT_SVG) && !(imageFormat == SVGGenerator.OUTPUT_FORMAT_PNG)) { + if (!(imageFormat == SVGGenerator.OUTPUT_FORMAT_SVG) && !(imageFormat == SVGGenerator.OUTPUT_FORMAT_PNG) + && !(imageFormat == SVGGenerator.OUTPUT_FORMAT_SVG_LAMS_COMMUNITY)) { String msg = "Image format parameter is incorrect"; log.error(msg); response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); @@ -111,39 +112,40 @@ contentType = "image/svg+xml"; } else { contentType = "image/png"; - } + } response.setContentType(contentType); - + String imagePath = learningDesignService.createLearningDesignSVG(learningDesignId, imageFormat); - + OutputStream output = response.getOutputStream(); FileInputStream input = new FileInputStream(imagePath); IOUtils.copy(input, output); IOUtils.closeQuietly(input); IOUtils.closeQuietly(output); - + } catch (Exception e) { log.error("Problem with LearningDesignRepositoryServlet request", e); - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Problem with LearningDesignRepositoryServlet request"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, + "Problem with LearningDesignRepositoryServlet request"); } } - + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - doGet(request, response); + doGet(request, response); } /** * Initialization of the servlet.
* * @throws ServletException - * if an error occure + * if an error occure */ public void init() throws ServletException { integrationService = (IntegrationService) WebApplicationContextUtils.getRequiredWebApplicationContext( - getServletContext()).getBean("integrationService"); - - learningDesignService = (ILearningDesignService) WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()) - .getBean("learningDesignService"); + getServletContext()).getBean("integrationService"); + + learningDesignService = (ILearningDesignService) WebApplicationContextUtils.getRequiredWebApplicationContext( + getServletContext()).getBean("learningDesignService"); } } Index: lams_central/web/images/icons/door_in.png =================================================================== diff -u -r38d257ae9ba914964511a4a8fa1f51d5ace5af2e -r1b117caf4135f53248542cbc97d71aac448f3de9 Binary files differ Index: lams_central/web/images/icons/door_out.png =================================================================== diff -u -r38d257ae9ba914964511a4a8fa1f51d5ace5af2e -r1b117caf4135f53248542cbc97d71aac448f3de9 Binary files differ Index: lams_central/web/includes/javascript/progressBar.js =================================================================== diff -u -r0bcaf8988ab41f69f5b2181942883f6beb95e4af -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_central/web/includes/javascript/progressBar.js (.../progressBar.js) (revision 0bcaf8988ab41f69f5b2181942883f6beb95e4af) +++ lams_central/web/includes/javascript/progressBar.js (.../progressBar.js) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -294,9 +294,14 @@ // label underneath the shape var label = null; if (isHorizontalBar) { + // cut lengthy activity names so they don't overlap + var content = activity.name; + if (content.length > 23) { + content = content.substring(0,20) + '...'; + } label = paper.text(activity.middle, 40 + (activity.index % 2 == 0 ? 0 : 15), - activity.name); + content); } else { label = paper.text(activity.middle, 47 + 60 * activity.index + (isLarger ? 10 : 0), Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/dto/AuthoringActivityDTO.java =================================================================== diff -u -r28827d671ccbd07e8f70eaaf3ca7d63047186bb3 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_common/src/java/org/lamsfoundation/lams/learningdesign/dto/AuthoringActivityDTO.java (.../AuthoringActivityDTO.java) (revision 28827d671ccbd07e8f70eaaf3ca7d63047186bb3) +++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/dto/AuthoringActivityDTO.java (.../AuthoringActivityDTO.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -1111,7 +1111,7 @@ * The parentActivityID to set. */ public void setParentActivityID(Long parentActivityID) { - if (!parentActivityID.equals(WDDXTAGS.NUMERIC_NULL_VALUE_LONG)) { + if (!WDDXTAGS.NUMERIC_NULL_VALUE_LONG.equals(parentActivityID)) { this.parentActivityID = parentActivityID; } } Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java =================================================================== diff -u -r4380bee72735f5311cf4f474a711c38fe33a8585 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java (.../ExportToolContentService.java) (revision 4380bee72735f5311cf4f474a711c38fe33a8585) +++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java (.../ExportToolContentService.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -669,7 +669,7 @@ if (format != ExportToolContentService.PACKAGE_FORMAT_IMS) { String destinationPath = FileUtil.getFullPath(contentDir, ExportToolContentService.SVG_IMAGE_FILE_NAME); - String svgPath = service.createLearningDesignSVG(learningDesignId, SVGGenerator.OUTPUT_FORMAT_SVG); + String svgPath = service.createLearningDesignSVG(learningDesignId, SVGGenerator.OUTPUT_FORMAT_SVG_LAMS_COMMUNITY); FileUtils.copyFile(new File(svgPath), new File(destinationPath)); destinationPath = FileUtil.getFullPath(contentDir, ExportToolContentService.PNG_IMAGE_FILE_NAME); @@ -720,13 +720,7 @@ } catch (IOException e) { log.error("IOException:", e); throw new ExportToolContentException(e); - } catch (JDOMException e) { - log.error("JDOMException:", e); - throw new ExportToolContentException(e); - } catch (TranscoderException e) { - log.error("TranscoderException:", e); - throw new ExportToolContentException(e); - } + } } /** Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ILearningDesignService.java =================================================================== diff -u -r27a9e58b46446d891cf6b081e8fb6fb6f6294e2e -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ILearningDesignService.java (.../ILearningDesignService.java) (revision 27a9e58b46446d891cf6b081e8fb6fb6f6294e2e) +++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ILearningDesignService.java (.../ILearningDesignService.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -38,55 +38,66 @@ * @author Mitchell Seaton */ -public interface ILearningDesignService{ - - /** - * Get the learning design DTO, suitable to send to Flash via WDDX - * @param learningDesignId - * @param languageCode Two letter language code needed to I18N the help url - * @return LearningDesignDTO - */ - public LearningDesignDTO getLearningDesignDTO(Long learningDesignID, String languageCode); +public interface ILearningDesignService { - /** - * This method calls other validation methods which apply the validation - * rules to determine whether or not the learning design is valid. - * - * @param learningDesign - * @return list of validation errors - */ - public Vector validateLearningDesign(LearningDesign learningDesign); - - /** - * Get the DTO list of all valid learning libraries, which equals getAllLearningLibraryDetails(true) method. - * @return list of LearningLibraryDTO - * @throws IOException - */ - public ArrayList getAllLearningLibraryDetails(String languageCode) throws IOException; - /** - * Get the DTO list of all learning libraries whatever it is valid or not. - * @param valid - * @return - * @throws IOException - */ - public ArrayList getAllLearningLibraryDetails(boolean valid, String languageCode) throws IOException; - - /** - * Set valid flag to learning library. - * @param learningLibraryId - * @param valid - */ - public void setValid(Long learningLibraryId, boolean valid); - - /** - * Creates learning design SVG/PNG file. Also stores it into the file system for caching. - * - * @param learningDesignId source learning design for the outcome image - * @param imageFormat it can be either SVGGenerator.OUTPUT_FORMAT_SVG or SVGGenerator.OUTPUT_FORMAT_PNG - * @return - * @throws JDOMException - * @throws IOException - * @throws TranscoderException - */ - String createLearningDesignSVG(Long learningDesignId, int imageFormat) throws JDOMException, IOException, TranscoderException; + /** + * Get the learning design DTO, suitable to send to Flash via WDDX + * + * @param learningDesignId + * @param languageCode + * Two letter language code needed to I18N the help url + * @return LearningDesignDTO + */ + public LearningDesignDTO getLearningDesignDTO(Long learningDesignID, String languageCode); + + /** + * This method calls other validation methods which apply the validation rules to determine whether or not the + * learning design is valid. + * + * @param learningDesign + * @return list of validation errors + */ + public Vector validateLearningDesign(LearningDesign learningDesign); + + /** + * Get the DTO list of all valid learning libraries, which equals getAllLearningLibraryDetails(true) method. + * + * @return list of LearningLibraryDTO + * @throws IOException + */ + public ArrayList getAllLearningLibraryDetails(String languageCode) throws IOException; + + /** + * Get the DTO list of all learning libraries whatever it is valid or not. + * + * @param valid + * @return + * @throws IOException + */ + public ArrayList getAllLearningLibraryDetails(boolean valid, String languageCode) + throws IOException; + + /** + * Set valid flag to learning library. + * + * @param learningLibraryId + * @param valid + */ + public void setValid(Long learningLibraryId, boolean valid); + + /** + * Creates learning design SVG/PNG file. Also stores it into the file system for caching. + * + * @param learningDesignId + * source learning design for the outcome image + * @param imageFormat + * it can be either SVGGenerator.OUTPUT_FORMAT_SVG or SVGGenerator.OUTPUT_FORMAT_PNG + * @return + * @throws JDOMException + * @throws IOException + * @throws TranscoderException + */ + String createLearningDesignSVG(Long learningDesignId, int imageFormat) throws IOException; + + String createBranchingSVG(Long branchingActivityId, int imageFormat) throws IOException; } Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/LearningDesignService.java =================================================================== diff -u -r27a9e58b46446d891cf6b081e8fb6fb6f6294e2e -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/LearningDesignService.java (.../LearningDesignService.java) (revision 27a9e58b46446d891cf6b081e8fb6fb6f6294e2e) +++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/LearningDesignService.java (.../LearningDesignService.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -23,13 +23,10 @@ /* $$Id$$ */ package org.lamsfoundation.lams.learningdesign.service; -import java.io.BufferedInputStream; import java.io.File; -import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -38,11 +35,10 @@ import java.util.Locale; import java.util.Vector; -import org.apache.batik.transcoder.TranscoderException; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; -import org.jdom.JDOMException; import org.lamsfoundation.lams.learningdesign.Activity; +import org.lamsfoundation.lams.learningdesign.BranchingActivity; import org.lamsfoundation.lams.learningdesign.LearningDesign; import org.lamsfoundation.lams.learningdesign.LearningLibrary; import org.lamsfoundation.lams.learningdesign.dao.hibernate.ActivityDAO; @@ -57,230 +53,269 @@ import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; import org.lamsfoundation.lams.util.FileUtil; -import org.lamsfoundation.lams.util.FileUtilException; import org.lamsfoundation.lams.util.ILoadedMessageSourceService; import org.lamsfoundation.lams.util.MessageService; import org.lamsfoundation.lams.util.svg.SVGGenerator; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; /** - * The LearningDesignService class contains methods which applies validation rules - * to determine the validity of a learning design. For the validation rules, please - * see the AuthoringDesignDoc in lams_documents. + * The LearningDesignService class contains methods which applies validation rules to determine the validity of a + * learning design. For the validation rules, please see the AuthoringDesignDoc in lams_documents. * - * If no errors are found, a learning design is considered valid, it will set the valid_design_flag to true. - * If validation fails, the validation messages will be returned in the response packet. The validation - * messages are a list of ValidationErrorDTO objects. + * If no errors are found, a learning design is considered valid, it will set the valid_design_flag to true. If + * validation fails, the validation messages will be returned in the response packet. The validation messages are a list + * of ValidationErrorDTO objects. * * @author mtruong - * + * */ -public class LearningDesignService implements ILearningDesignService{ - - protected Logger log = Logger.getLogger(LearningDesignService.class); - protected MessageService messageService; - - protected LearningDesignDAO learningDesignDAO; - protected ActivityDAO activityDAO; - protected GroupingDAO groupingDAO; - - protected LearningLibraryDAO learningLibraryDAO; - protected ILoadedMessageSourceService toolActMessageService; - - /* - * Default constructor - * - */ - public LearningDesignService() - { - } +public class LearningDesignService implements ILearningDesignService { - /********************************************** - * Setter/Getter Methods - * *******************************************/ - /** - * Set i18n MessageService - */ - public void setMessageService(MessageService messageService) { - this.messageService = messageService; - } + protected Logger log = Logger.getLogger(LearningDesignService.class); + protected MessageService messageService; - /** - * Get i18n MessageService - */ - public MessageService getMessageService() { - return this.messageService; - } - - /** Access a message service related to a programatically loaded message file. - * Authoring uses this to access the message files for tools and activities. - */ - public ILoadedMessageSourceService getToolActMessageService() { - return toolActMessageService; - } + protected LearningDesignDAO learningDesignDAO; + protected ActivityDAO activityDAO; + protected GroupingDAO groupingDAO; - public void setToolActMessageService(ILoadedMessageSourceService toolActMessageService) { - this.toolActMessageService = toolActMessageService; + protected LearningLibraryDAO learningLibraryDAO; + protected ILoadedMessageSourceService toolActMessageService; + + /* + * Default constructor + * + */ + public LearningDesignService() { + } + + /********************************************** + * Setter/Getter Methods + * *******************************************/ + /** + * Set i18n MessageService + */ + public void setMessageService(MessageService messageService) { + this.messageService = messageService; + } + + /** + * Get i18n MessageService + */ + public MessageService getMessageService() { + return this.messageService; + } + + /** + * Access a message service related to a programatically loaded message file. Authoring uses this to access the + * message files for tools and activities. + */ + public ILoadedMessageSourceService getToolActMessageService() { + return toolActMessageService; + } + + public void setToolActMessageService(ILoadedMessageSourceService toolActMessageService) { + this.toolActMessageService = toolActMessageService; + } + + public void setLearningLibraryDAO(LearningLibraryDAO learningLibraryDAO) { + this.learningLibraryDAO = learningLibraryDAO; + } + + public void setActivityDAO(ActivityDAO activityDAO) { + this.activityDAO = activityDAO; + } + + public void setLearningDesignDAO(LearningDesignDAO learningDesignDAO) { + this.learningDesignDAO = learningDesignDAO; + } + + public void setGroupingDAO(GroupingDAO groupingDAO) { + this.groupingDAO = groupingDAO; + } + + /********************************************** + * Service Methods + * *******************************************/ + + /** + * Get the learning design DTO, suitable to send to Flash via WDDX + * + * @param learningDesignId + * @param languageCode + * Two letter language code needed to I18N the help url + * @return LearningDesignDTO + */ + @Override + public LearningDesignDTO getLearningDesignDTO(Long learningDesignID, String languageCode) { + LearningDesign design = learningDesignID != null ? learningDesignDAO.getLearningDesignById(learningDesignID) + : null; + return design != null ? new LearningDesignDTO(design, activityDAO, groupingDAO, languageCode) : null; + } + + /** + * This method calls other validation methods which apply the validation rules to determine whether or not the + * learning design is valid. + * + * @param learningDesign + * @return list of validation errors + */ + @Override + public Vector validateLearningDesign(LearningDesign learningDesign) { + LearningDesignValidator validator = new LearningDesignValidator(learningDesign, messageService); + return validator.validate(); + } + + @Override + public void setValid(Long learningLibraryId, boolean valid) { + LearningLibrary library = learningLibraryDAO.getLearningLibraryById(learningLibraryId); + library.setValidLibrary(valid); + learningLibraryDAO.update(library); + } + + @Override + public ArrayList getAllLearningLibraryDetails(String languageCode) throws IOException { + // only return valid learning library + return getAllLearningLibraryDetails(true, languageCode); + } + + @Override + public ArrayList getAllLearningLibraryDetails(boolean valid, String languageCode) + throws IOException { + Iterator iterator = learningLibraryDAO.getAllLearningLibraries(valid).iterator(); + ArrayList libraries = new ArrayList(); + while (iterator.hasNext()) { + LearningLibrary learningLibrary = (LearningLibrary) iterator.next(); + List templateActivities = activityDAO.getActivitiesByLibraryID(learningLibrary.getLearningLibraryId()); + + if ((templateActivities != null) & (templateActivities.size() == 0)) { + log.error("Learning Library with ID " + learningLibrary.getLearningLibraryId() + + " does not have a template activity"); + } + // convert library to DTO format + + LearningLibraryDTO libraryDTO = learningLibrary.getLearningLibraryDTO(templateActivities, languageCode); + internationaliseActivities(libraryDTO.getTemplateActivities()); + libraries.add(libraryDTO); } - - public void setLearningLibraryDAO(LearningLibraryDAO learningLibraryDAO) { - this.learningLibraryDAO = learningLibraryDAO; - } - - public void setActivityDAO(ActivityDAO activityDAO) { - this.activityDAO = activityDAO; - } + return libraries; + } - public void setLearningDesignDAO(LearningDesignDAO learningDesignDAO) { - this.learningDesignDAO = learningDesignDAO; + @Override + public String createBranchingSVG(Long branchingActivityId, int imageFormat) throws IOException { + BranchingActivity branchingActivity = (BranchingActivity) activityDAO + .getActivityByActivityId(branchingActivityId); + Long learningDesignId = branchingActivity.getLearningDesign().getLearningDesignId(); + return createDesignSVG(learningDesignId, branchingActivityId, imageFormat); + } + + @Override + public String createLearningDesignSVG(Long learningDesignId, int imageFormat) throws IOException { + return createDesignSVG(learningDesignId, null, imageFormat); + } + + private void internationaliseActivities(Collection activities) { + Iterator iter = activities.iterator(); + Locale locale = LocaleContextHolder.getLocale(); + + if (log.isDebugEnabled()) { + if (locale != null) { + log.debug("internationaliseActivities: Locale has lang/country " + locale.getLanguage() + "," + + locale.getCountry()); + } else { + log.debug("internationaliseActivities: Locale missing."); + } } - - public void setGroupingDAO(GroupingDAO groupingDAO) { - this.groupingDAO = groupingDAO; - } - - /********************************************** - * Service Methods - * *******************************************/ + while (iter.hasNext()) { + LibraryActivityDTO activity = (LibraryActivityDTO) iter.next(); + // update the activity fields + String languageFilename = activity.getLanguageFile(); + if (languageFilename != null) { + MessageSource toolMessageSource = toolActMessageService.getMessageService(languageFilename); + if (toolMessageSource != null) { + activity.setActivityTitle(toolMessageSource.getMessage(Activity.I18N_TITLE, null, + activity.getActivityTitle(), locale)); + activity.setDescription(toolMessageSource.getMessage(Activity.I18N_DESCRIPTION, null, + activity.getDescription(), locale)); + activity.setHelpText(toolMessageSource.getMessage(Activity.I18N_HELP_TEXT, null, + activity.getHelpText(), locale)); + } else { + log.warn("Unable to internationalise the library activity " + activity.getActivityID() + " " + + activity.getActivityTitle() + " message file " + activity.getLanguageFile() + + ". Activity Message source not available"); + } - /** - * Get the learning design DTO, suitable to send to Flash via WDDX - * @param learningDesignId - * @param languageCode Two letter language code needed to I18N the help url - * @return LearningDesignDTO - */ - public LearningDesignDTO getLearningDesignDTO(Long learningDesignID, String languageCode) { - LearningDesign design = learningDesignID!=null ? learningDesignDAO.getLearningDesignById(learningDesignID) : null; - return design != null ? new LearningDesignDTO(design,activityDAO,groupingDAO, languageCode) : null; + // update the tool field - note only tool activities have a tool entry. + if ((activity.getActivityTypeID() != null) + && (Activity.TOOL_ACTIVITY_TYPE == activity.getActivityTypeID().intValue())) { + languageFilename = activity.getToolLanguageFile(); + toolMessageSource = toolActMessageService.getMessageService(languageFilename); + if (toolMessageSource != null) { + activity.setToolDisplayName(toolMessageSource.getMessage(Tool.I18N_DISPLAY_NAME, null, + activity.getToolDisplayName(), locale)); + } else { + log.warn("Unable to internationalise the library activity " + activity.getActivityID() + " " + + activity.getActivityTitle() + " message file " + activity.getLanguageFile() + + ". Tool Message source not available"); + } + } + } else { + log.warn("Unable to internationalise the library activity " + activity.getActivityID() + " " + + activity.getActivityTitle() + ". No message file supplied."); + } } - - /** - * This method calls other validation methods which apply the validation - * rules to determine whether or not the learning design is valid. - * - * @param learningDesign - * @return list of validation errors - */ - public Vector validateLearningDesign(LearningDesign learningDesign) - { - LearningDesignValidator validator = new LearningDesignValidator(learningDesign, messageService); - return validator.validate(); + } + + private String createDesignSVG(Long learningDesignId, Long branchingActivityId, int imageFormat) + throws IOException, FileNotFoundException { + String fileNameWithoutExtension = learningDesignId.toString(); + if (branchingActivityId != null) { + fileNameWithoutExtension += "_branching" + branchingActivityId; } - - public void setValid(Long learningLibraryId, boolean valid) { - LearningLibrary library = learningLibraryDAO.getLearningLibraryById(learningLibraryId); - library.setValidLibrary(valid); - learningLibraryDAO.update(library); + + // construct absolute filePath to SVG + String earFolder = Configuration.get(ConfigurationKeys.LAMS_EAR_DIR); + if (StringUtils.isBlank(earFolder)) { + log.error("Unable to get path to the LAMS Server URL from the configuration table. SVG image creation failed"); + return null; } + String directoryToStoreFile = FileUtil.getFullPath(earFolder, "lams-www.war\\secure\\learning-design-images"); - public ArrayList getAllLearningLibraryDetails(String languageCode)throws IOException{ - //only return valid learning library - return getAllLearningLibraryDetails(true, languageCode); + // Check whether this dir exists + File svgPngDirectory = new File(directoryToStoreFile); + if (!svgPngDirectory.exists()) { + svgPngDirectory.mkdirs(); } - public ArrayList getAllLearningLibraryDetails(boolean valid, String languageCode)throws IOException{ - Iterator iterator= learningLibraryDAO.getAllLearningLibraries(valid).iterator(); - ArrayList libraries = new ArrayList(); - while(iterator.hasNext()){ - LearningLibrary learningLibrary = (LearningLibrary)iterator.next(); - List templateActivities = activityDAO.getActivitiesByLibraryID(learningLibrary.getLearningLibraryId()); - - if (templateActivities!=null & templateActivities.size()==0) - { - log.error("Learning Library with ID " + learningLibrary.getLearningLibraryId() + " does not have a template activity"); - } - // convert library to DTO format - LearningLibraryDTO libraryDTO = learningLibrary.getLearningLibraryDTO(templateActivities, languageCode); - internationaliseActivities(libraryDTO.getTemplateActivities()); - libraries.add(libraryDTO); - } - return libraries; + String fileExtension; + if ((imageFormat == SVGGenerator.OUTPUT_FORMAT_SVG) + || (imageFormat == SVGGenerator.OUTPUT_FORMAT_SVG_LAMS_COMMUNITY)) { + fileExtension = ".svg"; + } else { + fileExtension = ".png"; } - - public String createLearningDesignSVG(Long learningDesignId, int imageFormat) throws JDOMException, IOException, TranscoderException { - //construct absolute filePath to SVG - String earFolder = Configuration.get(ConfigurationKeys.LAMS_EAR_DIR); - if (StringUtils.isBlank(earFolder)) { - log.error("Unable to get path to the LAMS Server URL from the configuration table. SVG image creation failed"); - return null; - } - String directoryToStoreFile = FileUtil.getFullPath(earFolder, "lams-www.war\\secure\\learning-design-images"); - - // Check whether this dir exists - File svgPngDirectory = new File(directoryToStoreFile); - if (!svgPngDirectory.exists()) { - svgPngDirectory.mkdirs(); - } - - String fileExtension; - if (imageFormat == SVGGenerator.OUTPUT_FORMAT_SVG) { - fileExtension = ".svg"; + String absoluteFilePath = FileUtil.getFullPath(directoryToStoreFile, fileNameWithoutExtension + fileExtension); + File file = new File(absoluteFilePath); + + // check if SVG exists and up-to-date + LearningDesign learningDesign = learningDesignDAO.getLearningDesignById(learningDesignId); + boolean isSvgOutdated = new Date(file.lastModified()).before(learningDesign.getLastModifiedDateTime()); + if (!file.exists() || isSvgOutdated) { + // generate new SVG image and save it to the file system + SVGGenerator svgGenerator = new SVGGenerator(); + LearningDesignDTO ldDto = this.getLearningDesignDTO(learningDesignId, ""); + + if (branchingActivityId == null) { + svgGenerator.generateLearningDesignDOM(ldDto, imageFormat); } else { - fileExtension = ".png"; + svgGenerator.generateBranchingDOM(ldDto, branchingActivityId, imageFormat); } - String absoluteFilePath = FileUtil.getFullPath(directoryToStoreFile, learningDesignId.toString() + fileExtension); - File file = new File(absoluteFilePath); - - //check if SVG exists and up-to-date - LearningDesign learningDesign = learningDesignDAO.getLearningDesignById(learningDesignId); - boolean isSvgOutdated = new Date(file.lastModified()).before(learningDesign.getLastModifiedDateTime()); - if (!file.exists() || isSvgOutdated) { - //generate new SVG image and save it to the file system - SVGGenerator svgGenerator = new SVGGenerator(); - LearningDesignDTO ldDto = this.getLearningDesignDTO(learningDesignId, ""); - svgGenerator.generateSvgDom(ldDto); - FileOutputStream fileOutputStream = new FileOutputStream(file); - svgGenerator.streamOutDocument(fileOutputStream, imageFormat); - } - - return absoluteFilePath; - } - private void internationaliseActivities(Collection activities) { - Iterator iter = activities.iterator(); - Locale locale = LocaleContextHolder.getLocale(); - - if ( log.isDebugEnabled()) { - if ( locale != null ) - log.debug("internationaliseActivities: Locale has lang/country "+locale.getLanguage()+","+locale.getCountry()); - else - log.debug("internationaliseActivities: Locale missing."); - } - - while (iter.hasNext()) { - LibraryActivityDTO activity = (LibraryActivityDTO) iter.next(); - // update the activity fields - String languageFilename = activity.getLanguageFile(); - if ( languageFilename != null ) { - MessageSource toolMessageSource = toolActMessageService.getMessageService(languageFilename); - if ( toolMessageSource != null ) { - activity.setActivityTitle(toolMessageSource.getMessage(Activity.I18N_TITLE,null,activity.getActivityTitle(),locale)); - activity.setDescription(toolMessageSource.getMessage(Activity.I18N_DESCRIPTION,null,activity.getDescription(),locale)); - activity.setHelpText(toolMessageSource.getMessage(Activity.I18N_HELP_TEXT,null,activity.getHelpText(),locale)); - } else { - log.warn("Unable to internationalise the library activity "+activity.getActivityID()+" "+activity.getActivityTitle() - +" message file "+activity.getLanguageFile()+". Activity Message source not available"); - } - - // update the tool field - note only tool activities have a tool entry. - if ( activity.getActivityTypeID()!=null && Activity.TOOL_ACTIVITY_TYPE == activity.getActivityTypeID().intValue() ) { - languageFilename = activity.getToolLanguageFile(); - toolMessageSource = toolActMessageService.getMessageService(languageFilename); - if ( toolMessageSource != null ) { - activity.setToolDisplayName(toolMessageSource.getMessage(Tool.I18N_DISPLAY_NAME,null,activity.getToolDisplayName(),locale)); - } else { - log.warn("Unable to internationalise the library activity "+activity.getActivityID()+" "+activity.getActivityTitle() - +" message file "+activity.getLanguageFile()+". Tool Message source not available"); - } - } - } else { - log.warn("Unable to internationalise the library activity "+activity.getActivityID()+" "+activity.getActivityTitle() - +". No message file supplied."); - } - } + FileOutputStream fileOutputStream = new FileOutputStream(file); + svgGenerator.streamOutDocument(fileOutputStream); } + return absoluteFilePath; + } - } \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGConstants.java =================================================================== diff -u -rf5b809c51711e2c74ebe9bd51867ce2e7eff320c -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGConstants.java (.../SVGConstants.java) (revision f5b809c51711e2c74ebe9bd51867ce2e7eff320c) +++ lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGConstants.java (.../SVGConstants.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -1,69 +1,59 @@ package org.lamsfoundation.lams.util.svg; -/**************************************************************** - * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) - * ============================================================= - * License Information: http://lamsfoundation.org/licensing/lams/2.0/ +/**************************************************************** + * 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 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. + * 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 + * 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 - * **************************************************************** + * http://www.gnu.org/licenses/gpl.txt **************************************************************** */ /* $Id$ */ public class SVGConstants { - - public static final String SVG_NAMESPACE = "http://www.w3.org/2000/svg";//SVGDOMImplementation.SVG_NAMESPACE_URI; + + public static final String SVG_NAMESPACE = "http://www.w3.org/2000/svg"; public static final String SVG_NAMESPACE_XLINK = "http://www.w3.org/1999/xlink"; - + public static final String PATH_TO_LAMSCOMMUNITY_SVG_IMAGES = "http://lamscommunity.org/lamscentral/images/acts/"; public static final String PATH_TO_LAMS_CENTRAL_SVG_IMAGES = "/lams-central.war/images/svg/"; - + public static final String ROOT_ELEMENT_ID = "rootElement"; - // canvas dimensions - public static final int CANVAS_DEFAULT_WIDTH = 1024; - public static final int CANVAS_DEFAULT_HEIGHT = 768; - // gate dimensions public static final int GATE_WIDTH = 30; public static final int GATE_HEIGHT = 30; - // These are the octogon proportions so according to one point we calculate the new dimensions/proportions - public static final double[][] GATE_PROPORTIONS = { { 13.6, 34.4 }, { 1, 34.4 }, { -9.4, 24 }, { -9.4, 11.4 }, { 1, 1 }, - { 13.6, 1 }, { 24, 11.4 }, { 24, 24 } }; - + // These are the octogon proportions so according to one point we calculate the new dimensions/proportions + public static final double[][] GATE_PROPORTIONS = { { 13.6, 34.4 }, { 1, 34.4 }, { -9.4, 24 }, { -9.4, 11.4 }, + { 1, 1 }, { 13.6, 1 }, { 24, 11.4 }, { 24, 24 } }; + // tool dimensions public static final int TOOL_WIDTH = 125; public static final int TOOL_HEIGHT = 50; public static final int TOOL_INSIDE_OPTIONAL_WIDTH = 60; - + public static final int PARALLEL_ACTIVITY_HEIGHT = 172; public static final int OPTIONS_ACTIVITY_HEIGHT_MULTIPLIER = 63; public static final int OPTIONS_ACTIVITY_HEIGHT_ADD = 53; public static final int PARALLEL_OR_OPTIONS_ACTIVITY_WIDTH = 141; public static final int CONTAINER_HEADER_HEIGHT = 39; - + public static final int BRANCHING_ACTIVITY_HEIGHT = 100; public static final int BRANCHING_ACTIVITY_WIDTH = 165; - + public static final double BRANCHING_ACTIVITY_POINT = 8.5; - //distance between points in branching activity + // distance between points in branching activity public static final int BRANCHING_STEP = 15; - + // css public static final String CSS_CONTAINER = "fill:#A9C8FD;stroke:#E1F0FD;stroke-width:2.2;opacity:1"; - -} +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGGenerator.java =================================================================== diff -u -r76aaa4283f0c9c4b95cae2104bdf453c02ee7721 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGGenerator.java (.../SVGGenerator.java) (revision 76aaa4283f0c9c4b95cae2104bdf453c02ee7721) +++ lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGGenerator.java (.../SVGGenerator.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -1,4 +1,5 @@ package org.lamsfoundation.lams.util.svg; + /**************************************************************** * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) * ============================================================= @@ -24,15 +25,20 @@ /* $Id$ */ import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; import java.awt.geom.Point2D; import java.io.IOException; import java.io.OutputStream; -import java.io.StringWriter; -import java.io.Writer; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.TreeSet; import org.apache.batik.dom.svg.SVGDOMImplementation; @@ -41,10 +47,10 @@ import org.apache.batik.transcoder.TranscoderOutput; import org.apache.batik.transcoder.image.PNGTranscoder; import org.apache.commons.lang.StringUtils; -import org.apache.xml.serialize.OutputFormat; -import org.apache.xml.serialize.XMLSerializer; +import org.apache.log4j.Logger; import org.jdom.JDOMException; import org.lamsfoundation.lams.learningdesign.Activity; +import org.lamsfoundation.lams.learningdesign.ActivityDTOOrderComparator; import org.lamsfoundation.lams.learningdesign.dto.AuthoringActivityDTO; import org.lamsfoundation.lams.learningdesign.dto.LearningDesignDTO; import org.lamsfoundation.lams.learningdesign.dto.TransitionDTO; @@ -54,239 +60,193 @@ import org.w3c.dom.DOMImplementation; import org.w3c.dom.Element; import org.w3c.dom.Node; -import org.w3c.dom.NodeList; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; import org.w3c.dom.svg.SVGDocument; /** * Generates SVG document based on exported learning design's xml file. * * @author Andrey Balan */ -public class SVGGenerator extends SVGConstants{ - - public final static int OUTPUT_FORMAT_SVG = 1; - public final static int OUTPUT_FORMAT_PNG = 2; - public final static int OUTPUT_FORMAT_SYSTEM_OUT = 3; - +public class SVGGenerator { + public final static int OUTPUT_FORMAT_SVG = 1; + public final static int OUTPUT_FORMAT_PNG = 2; + public final static int OUTPUT_FORMAT_SYSTEM_OUT = 3; + public final static int OUTPUT_FORMAT_SVG_LAMS_COMMUNITY = 4; + private SVGDocument doc; - private Integer adjustedDocumentWidth = null; - + private Integer outputFormat; + private Integer adjustedDocumentWidth; + private final String localSvgIconsPath = Configuration.get(ConfigurationKeys.SERVER_URL) + "images/svg/"; + + // fille only for branching DOM + private Point branchingStartPoint; + private Point branchingEndPoint; + + private static final Logger log = Logger.getLogger(SVGGenerator.class); + // tools used in all instances + private static final DOMImplementation svgDOMImplementation = SVGDOMImplementation.getDOMImplementation(); + private static final PNGTranscoder pngTranscoder = new PNGTranscoder(); + private static DOMImplementationLS lsDOMImplementation; + private static LSSerializer serializer; + + static { + try { + // initialize common tools + DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); + SVGGenerator.lsDOMImplementation = (DOMImplementationLS) registry.getDOMImplementation("XML 1.0 LS"); + + SVGGenerator.serializer = SVGGenerator.lsDOMImplementation.createLSSerializer(); + SVGGenerator.serializer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); + } catch (Exception e) { + SVGGenerator.log.error(e); + } + } + /** * Adjusts resulted SVG with user-specific width * - * @param width user specific width + * @param width + * user specific width */ public void adjustDocumentWidth(Integer width) { this.adjustedDocumentWidth = width; } - + /** * @return actual width and height of resulted image */ public Dimension getDocumentWidthHeight() { - - Element svg = doc.getDocumentElement(); - String widthStr = svg.getAttributeNS(null, "width"); - String heightStr = svg.getAttributeNS(null, "height"); - - int width = Integer.parseInt(widthStr); - int height = Integer.parseInt(heightStr); - - return new Dimension(width, height); + Element svg = doc.getDocumentElement(); + String widthStr = svg.getAttributeNS(null, "width"); + String heightStr = svg.getAttributeNS(null, "height"); + + int width = Integer.parseInt(widthStr); + int height = Integer.parseInt(heightStr); + + return new Dimension(width, height); } - + /** * Stream out SVG document into specified outputStream. * - * @param outputStream stream where we put resulted data. It can be null in case of RESULT_TYPE_DISPLAY - * @param resultType one of SVGGenerator's constants: either RESULT_TYPE_SVG or RESULT_TYPE_PNG or RESULT_TYPE_DISPLAY + * @param outputStream + * stream where we put resulted data. It can be null in case of RESULT_TYPE_DISPLAY + * @param outputFormat + * one of SVGGenerator's constants: either RESULT_TYPE_SVG or RESULT_TYPE_PNG or RESULT_TYPE_DISPLAY * * @throws TranscoderException * @throws IOException + * @throws IllegalAccessException + * @throws InstantiationException + * @throws ClassNotFoundException + * @throws ClassCastException */ - public void streamOutDocument(OutputStream outputStream, int resultType) throws TranscoderException, IOException { - - switch (resultType) { - case OUTPUT_FORMAT_SVG: - OutputFormat format = new OutputFormat(doc); - format.setLineWidth(65); - format.setIndenting(true); - format.setIndent(2); - - XMLSerializer serializer = new XMLSerializer(outputStream, format); - serializer.serialize(doc); - outputStream.flush(); - outputStream.close(); - break; - - case OUTPUT_FORMAT_PNG: - NodeList imageNodes = null; - String FULL_PATH_TO_LAMS_CENTRAL_SVG_IMAGES = null; - - boolean isLocalImagesUsed = (Configuration.get(ConfigurationKeys.LAMS_EAR_DIR) != null); - // change image references to local ones - if (isLocalImagesUsed) { - imageNodes = doc.getElementsByTagNameNS(SVG_NAMESPACE, "image"); - FULL_PATH_TO_LAMS_CENTRAL_SVG_IMAGES = "file://" - + Configuration.get(ConfigurationKeys.LAMS_EAR_DIR).replaceAll("\\\\", "/") - + "/lams-central.war/images/svg/"; - for (int i = 0; i < imageNodes.getLength(); i++) { - Element imageNode = (Element) imageNodes.item(i); - String imageFileName = imageNode.getAttributeNS(SVG_NAMESPACE_XLINK, "href"); - imageFileName = imageFileName.replaceFirst(PATH_TO_LAMSCOMMUNITY_SVG_IMAGES, - FULL_PATH_TO_LAMS_CENTRAL_SVG_IMAGES); - imageNode.setAttributeNS(SVG_NAMESPACE_XLINK, "xlink:href", imageFileName); - } - } - - PNGTranscoder transcoder = new PNGTranscoder(); + public void streamOutDocument(OutputStream outputStream) throws IOException { + if (SVGGenerator.OUTPUT_FORMAT_PNG == outputFormat) { // Set the transcoder input and output. TranscoderInput input = new TranscoderInput(doc); TranscoderOutput output = new TranscoderOutput(outputStream); // Perform the transcoding. - transcoder.transcode(input, output); - outputStream.flush(); - outputStream.close(); - - //roll back all image references for later use - if (isLocalImagesUsed) { - for (int i = 0; i < imageNodes.getLength(); i++) { - Element imageNode = (Element) imageNodes.item(i); - String imageFileName = imageNode.getAttributeNS(SVG_NAMESPACE_XLINK, "href"); - imageFileName = imageFileName.replaceFirst(FULL_PATH_TO_LAMS_CENTRAL_SVG_IMAGES, - PATH_TO_LAMSCOMMUNITY_SVG_IMAGES); - imageNode.setAttributeNS(SVG_NAMESPACE_XLINK, "xlink:href", imageFileName); - } + + try { + SVGGenerator.pngTranscoder.transcode(input, output); + } catch (TranscoderException e) { + throw new IOException("Error while transcoding SVG into PNG", e); } - break; - -// // to see resulted SVG image in swing component -// case OUTPUT_FORMAT_DISPLAY: -// JSVGCanvas canvas = new JSVGCanvas(); -// JFrame f = new JFrame(); -// f.getContentPane().add(canvas); -// canvas.setSVGDocument(doc); -// f.pack(); -// f.setSize(CANVAS_DEFAULT_WIDTH, CANVAS_DEFAULT_HEIGHT); -// f.setVisible(true); -// break; - - default: - OutputFormat format2 = new OutputFormat(doc); - format2.setLineWidth(65); - format2.setIndenting(true); - format2.setIndent(2); - Writer out = new StringWriter(); - XMLSerializer serializer2 = new XMLSerializer(out, format2); - serializer2.serialize(doc); - System.out.println(out.toString()); + } else { + LSOutput output = SVGGenerator.lsDOMImplementation.createLSOutput(); + output.setEncoding("UTF-8"); + output.setByteStream((SVGGenerator.OUTPUT_FORMAT_SVG == outputFormat) + || (SVGGenerator.OUTPUT_FORMAT_SVG_LAMS_COMMUNITY == outputFormat) ? outputStream : System.out); + SVGGenerator.serializer.write(doc, output); } + if (outputStream != null) { + outputStream.flush(); + outputStream.close(); + } } - + /** * Generates SVG image based on learning design provided. * * @param learningDesign * @throws JDOMException * @throws IOException */ - public void generateSvgDom(LearningDesignDTO learningDesign) throws JDOMException, IOException { + @SuppressWarnings("unchecked") + public void generateLearningDesignDOM(LearningDesignDTO learningDesign, int outputFormat) throws IOException { + this.outputFormat = outputFormat; initializeSvgDocument(); - //initialize all tree nodes - ArrayList activities = learningDesign.getActivities(); - HashMap allNodes = new HashMap(); - for (AuthoringActivityDTO activity : activities) { - ActivityTreeNode node = new ActivityTreeNode(activity); - allNodes.put(activity.getActivityID(), node); - } - - //construct activities tree - ActivityTreeNode root = new ActivityTreeNode(); - for (ActivityTreeNode node : allNodes.values()) { - AuthoringActivityDTO activity = node.getActivity(); - if (activity.getParentActivityID() == null) { - root.add(node); - } else { - Long parentId = activity.getParentActivityID(); - ActivityTreeNode parent = allNodes.get(parentId); - parent.add(node); - } - } - - //**************** Draw transitions******************************************************** - Element svgRoot = doc.getElementById(ROOT_ELEMENT_ID); - ArrayList transitions = learningDesign.getTransitions(); - for (TransitionDTO transition : transitions) { - - ActivityTreeNode fromActivity = allNodes.get(transition.getFromActivityID()); - ActivityTreeNode toActivity = allNodes.get(transition.getToActivityID()); - Point2D fromIntersection = SVGTrigonometryUtils.getRectangleAndLineSegmentIntersection(fromActivity, toActivity); - Point2D toIntersection = SVGTrigonometryUtils.getRectangleAndLineSegmentIntersection(toActivity, fromActivity); - - //skip optional sequence's childs and lines between overlapped activities - if (fromActivity.isOptionalSequenceActivityChild() - || (fromIntersection == null) || (toIntersection == null)) { - continue; - } - - // Create the line - Element line = doc.createElementNS(SVG_NAMESPACE, "line"); - line.setAttributeNS(null, "id", transition.getFromActivityID() + "_to_" + transition.getToActivityID()); - line.setAttributeNS(null, "x1", Double.toString(fromIntersection.getX())); - line.setAttributeNS(null, "y1", Double.toString(fromIntersection.getY())); - line.setAttributeNS(null, "x2", Double.toString(toIntersection.getX())); - line.setAttributeNS(null, "y2", Double.toString(toIntersection.getY())); - line.setAttributeNS(null, "style", "stroke:#8C8FA6;stroke-width:2;opacity:1"); - line.setAttributeNS(null, "parentID", "0"); - - double a = (toIntersection.getX() - fromIntersection.getX()); - double b = (toIntersection.getY() - fromIntersection.getY()); - double yArrowShift = (a*a + b*b == 0) ? 0 : 5* b/Math.sqrt(a*a + b*b); - double xArrowShift = (a*a + b*b == 0) ? 0 : 5* a/Math.sqrt(a*a + b*b); - // Create the arrowhead - Element arrowhead = doc.createElementNS(SVG_NAMESPACE, "line"); - arrowhead.setAttributeNS(null, "id", "arrowhead_" + transition.getFromActivityID() + "_to_" + transition.getToActivityID()); - arrowhead.setAttributeNS(null, "x1", Double.toString(fromIntersection.getX())); - arrowhead.setAttributeNS(null, "y1", Double.toString(fromIntersection.getY())); - arrowhead.setAttributeNS(null, "x2", Double.toString((fromIntersection.getX() + toIntersection.getX())/2 - xArrowShift)); - arrowhead.setAttributeNS(null, "y2", Double.toString((fromIntersection.getY() + toIntersection.getY())/2 - yArrowShift)); - arrowhead.setAttributeNS(null, "style", "fill:#8C8FA6;stroke:#8C8FA6;stroke-width:2;opacity:1"); - arrowhead.setAttributeNS(null, "marker-end", "url(#Triangle)"); - arrowhead.setAttributeNS(null, "parentID", "0"); + ActivityTreeNode root = new ActivityTreeNode(); + Map allNodes = getActivityTree(learningDesign.getActivities(), root); - // Attach the line to the root 'svg' element. - svgRoot.appendChild(line); - svgRoot.appendChild(arrowhead); - } - - //**************** Draw activities ******************************************************** - //tree traverse - treeTraverse(root); - - setUpDocumentWidthHeight(allNodes.values()); + // **************** Draw transitions******************************************************** + + ArrayList transitions = (ArrayList) learningDesign.getTransitions(); + createActivityTransitionLines(allNodes, transitions); + + // **************** Draw activities ******************************************************** + treeTraverse(root); + + setUpDocumentWidthHeight(allNodes.values()); } - + /** - * Sets up Svg root and defs. - */ + * Generates SVG image based on branching activity provided. + */ + @SuppressWarnings("unchecked") + public void generateBranchingDOM(LearningDesignDTO learningDesign, long branchingActivityId, int outputFormat) + throws IOException { + this.outputFormat = outputFormat; + initializeSvgDocument(); + + ActivityTreeNode root = new ActivityTreeNode(); + Set> branches = new HashSet>(); + Map allNodes = getActivityTree(learningDesign.getActivities(), root, + branchingActivityId, branches); + + // get only transitions between activities inside the given branching + Set interActivityTransitions = new HashSet(); + Set allActivityIDs = allNodes.keySet(); + for (TransitionDTO transition : (ArrayList) learningDesign.getTransitions()) { + if (allActivityIDs.contains(transition.getFromActivityID()) + || allActivityIDs.contains(transition.getToActivityID())) { + interActivityTransitions.add(transition); + } + } + + // **************** Draw transitions******************************************************** + // draw initial lines + createBranchingTransitionLines(branches, root); + + createActivityTransitionLines(allNodes, interActivityTransitions); + + // **************** Draw activities ******************************************************** + treeTraverse(root); + + setUpDocumentWidthHeight(allNodes.values()); + } + + /** + * Sets up Svg root and defs. + */ private void initializeSvgDocument() { - - // Create an SVG document. - DOMImplementation impl = SVGDOMImplementation.getDOMImplementation(); - doc = (SVGDocument) impl.createDocument(SVG_NAMESPACE, "svg", null); - // Get the root element (the 'svg' element). - Element svgRoot = doc.getDocumentElement(); - // Set the width and height attributes on the root 'svg' element. - svgRoot.setAttributeNS(null, "xmlns", SVG_NAMESPACE); - svgRoot.setAttributeNS(null, "xmlns:xlink", SVG_NAMESPACE_XLINK); - - //create arrow definition - Element defs = doc.createElementNS(SVG_NAMESPACE, "defs"); + // Create an SVG document. + doc = (SVGDocument) SVGGenerator.svgDOMImplementation.createDocument(SVGConstants.SVG_NAMESPACE, "svg", null); + // Get the root element (the 'svg' element). + Element svgRoot = doc.getDocumentElement(); + + // create arrow definition + Element defs = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "defs"); svgRoot.appendChild(defs); - Element marker = doc.createElementNS(SVG_NAMESPACE, "marker"); + Element marker = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "marker"); marker.setAttributeNS(null, "id", "Triangle"); marker.setAttributeNS(null, "viewBox", "0 0 10 10"); marker.setAttributeNS(null, "refX", "0"); @@ -296,18 +256,18 @@ marker.setAttributeNS(null, "markerHeight", "5"); marker.setAttributeNS(null, "orient", "auto"); defs.appendChild(marker); - Element path = doc.createElementNS(SVG_NAMESPACE, "path"); + Element path = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "path"); path.setAttributeNS(null, "d", "M 0 0 L 10 5 L 0 10 z"); marker.appendChild(path); - + // Create root g element - Element g = doc.createElementNS(SVG_NAMESPACE, "g"); - g.setAttributeNS(null, "id", ROOT_ELEMENT_ID); + Element g = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "g"); + g.setAttributeNS(null, "id", SVGConstants.ROOT_ELEMENT_ID); svgRoot.appendChild(g); } - + /** - * Recursive tree traverse. + * Recursive tree traverse. * * @param doc * @param svgRoot @@ -316,28 +276,27 @@ */ private void treeTraverse(ActivityTreeNode node) { AuthoringActivityDTO activity = node.getActivity(); - - //draw root's activity, unless it's the start root which doesn't contain activity + + // draw root's activity, unless it's the start root which doesn't contain activity if (activity != null) { createActivity(node); - - //in case of branching activity OR child of optional sequence activity OR optional actvity - //don't traverse child activities + + // in case of branching activity OR child of optional sequence activity OR optional actvity + // don't traverse child activities if (activity.getActivityTypeID().equals(Activity.CHOSEN_BRANCHING_ACTIVITY_TYPE) || activity.getActivityTypeID().equals(Activity.GROUP_BRANCHING_ACTIVITY_TYPE) || activity.getActivityTypeID().equals(Activity.TOOL_BRANCHING_ACTIVITY_TYPE) - || node.isOptionalSequenceActivityChild() - || node.isOptionalActivityChild()) { + || node.isOptionalSequenceActivityChild() || node.isOptionalActivityChild()) { return; } } - - //traverse child subtrees - for (ActivityTreeNode child : node.getChildren()) { - treeTraverse(child); - } + + // traverse child subtrees + for (ActivityTreeNode child : node.getChildren()) { + treeTraverse(child); + } } - + /** * Adds activity to SVG DOM. * @@ -347,277 +306,331 @@ * @param activity */ private void createActivity(ActivityTreeNode node) { - AuthoringActivityDTO activity = node.getActivity(); - + // Create current activity element - Element g = doc.createElementNS(SVG_NAMESPACE, "g"); + Element g = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "g"); String activityId = activity.getActivityID().toString(); g.setAttributeNS(null, "id", activityId); String parentID = (activity.getParentActivityID() == null) ? "0" : activity.getParentActivityID().toString(); g.setAttributeNS(null, "parentID", parentID); // Attach the g element to the root 'svg' element. - Element svgRoot = doc.getElementById(ROOT_ELEMENT_ID); + Element svgRoot = doc.getElementById(SVGConstants.ROOT_ELEMENT_ID); svgRoot.appendChild(g); int x = node.getActivityCoordinates().x; int y = node.getActivityCoordinates().y; // activities with parents (paralles, optionals, branching, etc) if (activity.getParentActivityID() != null) { AuthoringActivityDTO parentActivity = node.getParentActivity(); - x += (parentActivity.getxCoord() == null) ? 0 : parentActivity.getxCoord(); - y += (parentActivity.getyCoord() == null) ? 0 : parentActivity.getyCoord(); + x += (parentActivity.getxCoord() == null) ? 0 : parentActivity.getxCoord(); + y += (parentActivity.getyCoord() == null) ? 0 : parentActivity.getyCoord(); } - Integer width = node.getActivityDimension().width; - Integer height = node.getActivityDimension().height; - String text = activity.getActivityTitle(); - // if this activity is a children of a sequence activity, if it is, then we need to change its size if (node.isOptionalSequenceActivityChild()) { - ActivityTreeNode parentNode = (ActivityTreeNode) node.getParent(); - AuthoringActivityDTO grandParentActivity = parentNode.getParentActivity(); - x += (grandParentActivity.getxCoord() == null) ? 0 : grandParentActivity.getxCoord(); - y += (grandParentActivity.getyCoord() == null) ? 0 : grandParentActivity.getyCoord(); - - //create activity's rectangle - createRectangle(node, x, y, g); + createOptionalSequenceActivityChild(node, g, x, y); - createImage(node, x, y, g); - - // this activity is a children of an optional activity + // this activity is a children of an optional activity } else if (node.isOptionalActivityChild()) { + createOptionalActivityChild(activity, node, g, x, y); - //create activity's rectangle - createRectangle(node, x, y, g); - - // Create text label - if (text != null) { - int xText = x + (width / 2); - int yText = y + (height / 2) + 18; - createText("TextElement-" + activityId, xText, yText, null, "middle", "11.4", "Verdana", null, text, g); - } - - createImage(node, x, y, g); - } else if (activity.getActivityTypeID().equals(Activity.SYNCH_GATE_ACTIVITY_TYPE) || activity.getActivityTypeID().equals(Activity.SCHEDULE_GATE_ACTIVITY_TYPE) || activity.getActivityTypeID().equals(Activity.PERMISSION_GATE_ACTIVITY_TYPE) || activity.getActivityTypeID().equals(Activity.CONDITION_GATE_ACTIVITY_TYPE)) { - // if this is a stop gate we need to draw an octogon instead (and don't care about SYSTEM_GATE_ACTIVITY_TYPE) - x += 8; - y -= 2; + createGateActivity(activity, node, g, x, y); - String finalProportions = ""; - for (double[] proportion : GATE_PROPORTIONS) { - finalProportions += (x + proportion[0]) + "," + (y + proportion[1]) + " "; - } - - Element polygon = doc.createElementNS(SVG_NAMESPACE, "polygon"); - polygon.setAttributeNS(null, "style", node.getActivityCss()); - polygon.setAttributeNS(null, "points", finalProportions); - g.appendChild(polygon); - - // calculate midpoint for STOP text - double x1 = x + GATE_PROPORTIONS[6][0]; - double x2 = x + GATE_PROPORTIONS[2][0]; - double midpointX = (x1 + x2) / 2; - - double y1 = y + GATE_PROPORTIONS[6][1]; - double y2 = y + GATE_PROPORTIONS[2][1]; - double midpointY = (y1 + y2) / 2 + 3; - - createText("Gate_" + activityId, midpointX, midpointY, "0", "middle", "10", "Verdana", "fill:#FFFFFF;stroke:#FFFFFF;stroke-width:.5;", "STOP", g); - } else if (activity.getActivityTypeID().equals(Activity.PARALLEL_ACTIVITY_TYPE)) { // This is a parallel activity + createParallelActivity(activity, node, g, x, y); - // if the parallel is grouped, show it - if (activity.getApplyGrouping()) { - createGroupingEffect(node, x, y, g); - } - - //Create parallelContainer - createRectangle(node, x, y, g); - - createActvityHeader(node, x, y, g); - - if (StringUtils.isNotEmpty(text)) { - createText("TextElement-" + activityId, x +9, y +19, null, "start", "12", "Arial", "fill:#828990", text, g); - } - } else if (activity.getActivityTypeID().equals(Activity.CHOSEN_BRANCHING_ACTIVITY_TYPE) || activity.getActivityTypeID().equals(Activity.GROUP_BRANCHING_ACTIVITY_TYPE) || activity.getActivityTypeID().equals(Activity.TOOL_BRANCHING_ACTIVITY_TYPE)) { // This is a branching activity + createBranchingActivity(activity, node, g, x, y); - // if the parallel is grouped, show it - if (activity.getApplyGrouping()) { - createGroupingEffect(node, x, y, g); - } - - //Create branchingContainer + } else if (activity.getActivityTypeID().equals(Activity.OPTIONS_WITH_SEQUENCES_TYPE)) { + // This is an optional sequence + createOptionalSequenceActivity(activity, node, g, x, y); + + } else if (activity.getActivityTypeID().equals(Activity.SEQUENCE_ACTIVITY_TYPE)) { + // This is a sequence within an optional + + // Create sequenceContainer createRectangle(node, x, y, g); - - int startingPointX = x + 26; - int startingPointY = y + 40; - if (node.getChildCount() == 4) { - startingPointY -= 12; - } else if (node.getChildCount() > 4) { - startingPointY -= 2*12; - } - - Element startingPoint = doc.createElementNS(SVG_NAMESPACE, "rect"); - startingPoint.setAttributeNS(null, "x", Integer.toString(startingPointX)); - startingPoint.setAttributeNS(null, "y", Integer.toString(startingPointY)); - startingPoint.setAttributeNS(null, "width", Double.toString(BRANCHING_ACTIVITY_POINT + 0.5)); - startingPoint.setAttributeNS(null, "height", Double.toString(BRANCHING_ACTIVITY_POINT + 0.5)); - startingPoint.setAttributeNS(null, "style", "fill:#000000"); - g.appendChild(startingPoint); - - TreeSet sequenceNodes = new TreeSet(new ActivityTreeNodeComparator()); - sequenceNodes.addAll(node.getChildren()); - Iterator sequenceNodeIterator = sequenceNodes.iterator(); - for (int sequenceIndex = -1; sequenceNodeIterator.hasNext() && (sequenceIndex < 4); sequenceIndex++) { - ActivityTreeNode sequenceNode = sequenceNodeIterator.next(); - double previousActivityPointX = startingPointX + BRANCHING_ACTIVITY_POINT/2; - double previousActivityPointY = startingPointY + BRANCHING_ACTIVITY_POINT/2; - // Create the lines - Iterator activityNodeIterator = sequenceNode.getChildren().iterator(); - for (int activityIndex=1; activityNodeIterator.hasNext() && (activityIndex <= 6); activityIndex++, activityNodeIterator.next()) { - double activityPointX = startingPointX + activityIndex*BRANCHING_STEP + BRANCHING_ACTIVITY_POINT/2; - double activityPointY = startingPointY + sequenceIndex*BRANCHING_STEP + BRANCHING_ACTIVITY_POINT/2; - - Element line = doc.createElementNS(SVG_NAMESPACE, "line"); - line.setAttributeNS(null, "x1", Double.toString(previousActivityPointX)); - line.setAttributeNS(null, "y1", Double.toString(previousActivityPointY)); - line.setAttributeNS(null, "x2", Double.toString(activityPointX)); - line.setAttributeNS(null, "y2", Double.toString(activityPointY)); - line.setAttributeNS(null, "style", "stroke:black;stroke-width:1;"); - g.appendChild(line); + } else if (activity.getActivityTypeID().equals(Activity.OPTIONS_ACTIVITY_TYPE)) { + // This is an optional activity + createOptionalActivity(activity, node, g, x, y); - previousActivityPointX = activityPointX; - previousActivityPointY = activityPointY; - } + } else if (activity.getActivityTypeID().equals(Activity.FLOATING_ACTIVITY_TYPE)) { + // This is a support activity + createSupportActivity(activity, node, g, x, y); - //check if we need to draw line connecting last activity with endingPoint - if (!sequenceNode.getActivity().getStopAfterActivity()) { - Element line = doc.createElementNS(SVG_NAMESPACE, "line"); - line.setAttributeNS(null, "x1", Double.toString(previousActivityPointX)); - line.setAttributeNS(null, "y1", Double.toString(previousActivityPointY)); - line.setAttributeNS(null, "x2", Double.toString(x + 132 + BRANCHING_ACTIVITY_POINT / 2)); - line.setAttributeNS(null, "y2", Double.toString(startingPointY + BRANCHING_ACTIVITY_POINT / 2)); - line.setAttributeNS(null, "style", "stroke:black;stroke-width:1;"); - g.appendChild(line); + } else { + // This is a tool activity + createToolActivity(activity, node, g, x, y); + } + } - } - - // Create activity points - activityNodeIterator = sequenceNode.getChildren().iterator(); - for (int activityIndex=1; activityNodeIterator.hasNext() && (activityIndex <= 6); activityIndex++) { - ActivityTreeNode activityNode = activityNodeIterator.next(); - String activityStyle = sequenceNode.getActivity().getStopAfterActivity()&&!activityNodeIterator.hasNext() ? "stroke:red" : "stroke:black"; - activityStyle += ";stroke-width:0.8;opacity:1" + activityNode.getActivityColor(); - double activityPointX = startingPointX + activityIndex*BRANCHING_STEP; - double activityPointY = startingPointY + sequenceIndex*BRANCHING_STEP; - - Element activityPoint = doc.createElementNS(SVG_NAMESPACE, "rect"); - activityPoint.setAttributeNS(null, "x", Double.toString(activityPointX)); - activityPoint.setAttributeNS(null, "y", Double.toString(activityPointY)); - activityPoint.setAttributeNS(null, "width", "" + BRANCHING_ACTIVITY_POINT); - activityPoint.setAttributeNS(null, "height", "" + BRANCHING_ACTIVITY_POINT); - activityPoint.setAttributeNS(null, "style", activityStyle); - g.appendChild(activityPoint); - } - } - - Element endingPoint = doc.createElementNS(SVG_NAMESPACE, "rect"); - endingPoint.setAttributeNS(null, "x", Integer.toString(x + 132)); - endingPoint.setAttributeNS(null, "y", Integer.toString(startingPointY)); - endingPoint.setAttributeNS(null, "width", Double.toString(BRANCHING_ACTIVITY_POINT + 0.5)); - endingPoint.setAttributeNS(null, "height", Double.toString(BRANCHING_ACTIVITY_POINT + 0.5)); - endingPoint.setAttributeNS(null, "style", "fill:#000000"); - g.appendChild(endingPoint); + private void createOptionalActivityChild(AuthoringActivityDTO activity, ActivityTreeNode node, Element g, int x, + int y) { + // create activity's rectangle + createRectangle(node, x, y, g); - if (StringUtils.isNotEmpty(text)) { - createText("TextElement-" + activityId, x + BRANCHING_ACTIVITY_WIDTH/2, y +90, null, "middle", "11.4", "Verdana", null, text, g); - } - - } else if (activity.getActivityTypeID().equals(Activity.OPTIONS_WITH_SEQUENCES_TYPE)) { - // This is an optional sequence - - //Create optionalContainer - createRectangle(node, x, y, g); - - createActvityHeader(node, x, y, g); + // Create text label - if (StringUtils.isNotEmpty(text)) { - createText("TextElement-" + activityId, x +9, y +19, null, "start", "12", "Arial", "fill:#828990", text, g); - } - - createText("Children-" + activityId, x +9, y +19*2+1, null, "start", "11", "Arial", "fill:#828990", node.getChildCount() + " - Sequences", g); + String text = activity.getActivityTitle(); + if (text != null) { + Integer width = node.getActivityDimension().width; + Integer height = node.getActivityDimension().height; + int xText = x + (width / 2); + int yText = y + (height / 2) + 18; + createText("TextElement-" + activity.getActivityID(), xText, yText, null, "middle", "11.4", "Verdana", + null, text, g); + } - } else if (activity.getActivityTypeID().equals(Activity.SEQUENCE_ACTIVITY_TYPE)) { - // This is a sequence within an optional - - //Create sequenceContainer - createRectangle(node, x, y, g); - - } else if (activity.getActivityTypeID().equals(Activity.OPTIONS_ACTIVITY_TYPE)) { - // This is an optional activity - - int childActivitiesSize = node.getChildCount(); + createImage(node, x, y, g); + } - // Create optionalContainer - createRectangle(node, x, y, g); - - createActvityHeader(node, x, y, g); + private void createOptionalSequenceActivityChild(ActivityTreeNode node, Element g, int x, int y) { + ActivityTreeNode parentNode = (ActivityTreeNode) node.getParent(); + AuthoringActivityDTO grandParentActivity = parentNode.getParentActivity(); + x += (grandParentActivity.getxCoord() == null) ? 0 : grandParentActivity.getxCoord(); + y += (grandParentActivity.getyCoord() == null) ? 0 : grandParentActivity.getyCoord(); - if (StringUtils.isNotEmpty(text)) { - createText("TextElement-" + activityId, x +9, y +19, null, "start", "12", "Arial", "fill:#828990", text, g); - } - - createText("Children-" + activityId, x +9, y +19*2+1, null, "start", "11", "Arial", "fill:#828990", childActivitiesSize + " - Activities", g); + // create activity's rectangle + createRectangle(node, x, y, g); - } else if (activity.getActivityTypeID().equals(Activity.FLOATING_ACTIVITY_TYPE)) { - // This is a support activity + createImage(node, x, y, g); + } - // Create supportContainer - createRectangle(node, x, y, g); - - createActvityHeader(node, x, y, g); + private void createGateActivity(AuthoringActivityDTO activity, ActivityTreeNode node, Element g, int x, int y) { + // if this is a stop gate we need to draw an octogon instead (and don't care about + // SYSTEM_GATE_ACTIVITY_TYPE) + x += 8; + y -= 2; - if (StringUtils.isNotEmpty(text)) { - createText("TextElement-" + activityId, x +9, y +19, null, "start", "12", "Arial", "fill:#828990", text, g); - } - - int supportActivityChildrenSize = node.getChildCount(); - createText("Children-" + activityId, x +9, y +19*2+1, null, "start", "11", "Arial", "fill:#828990", supportActivityChildrenSize + " - Activities", g); + String finalProportions = ""; + for (double[] proportion : SVGConstants.GATE_PROPORTIONS) { + finalProportions += (x + proportion[0]) + "," + (y + proportion[1]) + " "; + } - } else { - // This is a tool activity - - // if activity uses a grouping we need to add a second rect layer to show that it's grouped - if (activity.getApplyGrouping()) { - createGroupingEffect(node, x, y, g); + Element polygon = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "polygon"); + polygon.setAttributeNS(null, "style", node.getActivityCss()); + polygon.setAttributeNS(null, "points", finalProportions); + g.appendChild(polygon); + + // calculate midpoint for STOP text + double x1 = x + SVGConstants.GATE_PROPORTIONS[6][0]; + double x2 = x + SVGConstants.GATE_PROPORTIONS[2][0]; + double midpointX = (x1 + x2) / 2; + + double y1 = y + SVGConstants.GATE_PROPORTIONS[6][1]; + double y2 = y + SVGConstants.GATE_PROPORTIONS[2][1]; + double midpointY = (y1 + y2) / 2 + 3; + + createText("Gate_" + activity.getActivityID(), midpointX, midpointY, "0", "middle", "10", "Verdana", + "fill:#FFFFFF;stroke:#FFFFFF;stroke-width:.5;", "STOP", g); + } + + private void createParallelActivity(AuthoringActivityDTO activity, ActivityTreeNode node, Element g, int x, int y) { + // if the parallel is grouped, show it + if (activity.getApplyGrouping()) { + createGroupingEffect(node, x, y, g); + } + + // Create parallelContainer + createRectangle(node, x, y, g); + + createActvityHeader(node, x, y, g); + + String text = activity.getActivityTitle(); + if (StringUtils.isNotEmpty(text)) { + createText("TextElement-" + activity.getActivityID(), x + 9, y + 19, null, "start", "12", "Arial", + "fill:#828990", text, g); + } + } + + private void createSupportActivity(AuthoringActivityDTO activity, ActivityTreeNode node, Element g, int x, int y) { + // Create supportContainer + createRectangle(node, x, y, g); + + createActvityHeader(node, x, y, g); + + String text = activity.getActivityTitle(); + if (StringUtils.isNotEmpty(text)) { + createText("TextElement-" + activity.getActivityID(), x + 9, y + 19, null, "start", "12", "Arial", + "fill:#828990", text, g); + } + + int supportActivityChildrenSize = node.getChildCount(); + createText("Children-" + activity.getActivityID(), x + 9, y + 19 * 2 + 1, null, "start", "11", "Arial", + "fill:#828990", supportActivityChildrenSize + " - Activities", g); + } + + private void createToolActivity(AuthoringActivityDTO activity, ActivityTreeNode node, Element g, int x, int y) { + // if activity uses a grouping we need to add a second rect layer to show that it's grouped + if (activity.getApplyGrouping()) { + createGroupingEffect(node, x, y, g); + } + + // create activity's rectangle + createRectangle(node, x, y, g); + + // Create text label + String text = activity.getActivityTitle(); + if (StringUtils.isNotEmpty(text)) { + Integer width = node.getActivityDimension().width; + Integer height = node.getActivityDimension().height; + int xText = x + (width / 2); + int yText = y + (height / 2) + 18; + createText("TextElement-" + activity.getActivityID(), xText, yText, null, "middle", "11.4", "Verdana", + null, text, g); + } + + createImage(node, x, y, g); + } + + private void createOptionalActivity(AuthoringActivityDTO activity, ActivityTreeNode node, Element g, int x, int y) { + int childActivitiesSize = node.getChildCount(); + + // Create optionalContainer + createRectangle(node, x, y, g); + + createActvityHeader(node, x, y, g); + + String text = activity.getActivityTitle(); + if (StringUtils.isNotEmpty(text)) { + createText("TextElement-" + activity.getActivityID(), x + 9, y + 19, null, "start", "12", "Arial", + "fill:#828990", text, g); + } + + createText("Children-" + activity.getActivityID(), x + 9, y + 19 * 2 + 1, null, "start", "11", "Arial", + "fill:#828990", childActivitiesSize + " - Activities", g); + } + + private void createOptionalSequenceActivity(AuthoringActivityDTO activity, ActivityTreeNode node, Element g, int x, + int y) { + // Create optionalContainer + createRectangle(node, x, y, g); + + createActvityHeader(node, x, y, g); + + String text = activity.getActivityTitle(); + if (StringUtils.isNotEmpty(text)) { + createText("TextElement-" + activity.getActivityID(), x + 9, y + 19, null, "start", "12", "Arial", + "fill:#828990", text, g); + } + + createText("Children-" + activity.getActivityID(), x + 9, y + 19 * 2 + 1, null, "start", "11", "Arial", + "fill:#828990", node.getChildCount() + " - Sequences", g); + } + + private void createBranchingActivity(AuthoringActivityDTO activity, ActivityTreeNode node, Element g, int x, int y) { + // if the parallel is grouped, show it + if (activity.getApplyGrouping()) { + createGroupingEffect(node, x, y, g); + } + + // Create branchingContainer + createRectangle(node, x, y, g); + + int startingPointX = x + 26; + int startingPointY = y + 40; + if (node.getChildCount() == 4) { + startingPointY -= 12; + } else if (node.getChildCount() > 4) { + startingPointY -= 2 * 12; + } + + Element startingPoint = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "rect"); + startingPoint.setAttributeNS(null, "x", Integer.toString(startingPointX)); + startingPoint.setAttributeNS(null, "y", Integer.toString(startingPointY)); + startingPoint.setAttributeNS(null, "width", Double.toString(SVGConstants.BRANCHING_ACTIVITY_POINT + 0.5)); + startingPoint.setAttributeNS(null, "height", Double.toString(SVGConstants.BRANCHING_ACTIVITY_POINT + 0.5)); + startingPoint.setAttributeNS(null, "style", "fill:#000000"); + g.appendChild(startingPoint); + + TreeSet sequenceNodes = new TreeSet(new ActivityTreeNodeComparator()); + sequenceNodes.addAll(node.getChildren()); + Iterator sequenceNodeIterator = sequenceNodes.iterator(); + for (int sequenceIndex = -1; sequenceNodeIterator.hasNext() && (sequenceIndex < 4); sequenceIndex++) { + ActivityTreeNode sequenceNode = sequenceNodeIterator.next(); + double previousActivityPointX = startingPointX + SVGConstants.BRANCHING_ACTIVITY_POINT / 2; + double previousActivityPointY = startingPointY + SVGConstants.BRANCHING_ACTIVITY_POINT / 2; + + // Create the lines + Iterator activityNodeIterator = sequenceNode.getChildren().iterator(); + for (int activityIndex = 1; activityNodeIterator.hasNext() && (activityIndex <= 6); activityIndex++, activityNodeIterator + .next()) { + double activityPointX = startingPointX + activityIndex * SVGConstants.BRANCHING_STEP + + SVGConstants.BRANCHING_ACTIVITY_POINT / 2; + double activityPointY = startingPointY + sequenceIndex * SVGConstants.BRANCHING_STEP + + SVGConstants.BRANCHING_ACTIVITY_POINT / 2; + + Element line = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "line"); + line.setAttributeNS(null, "x1", Double.toString(previousActivityPointX)); + line.setAttributeNS(null, "y1", Double.toString(previousActivityPointY)); + line.setAttributeNS(null, "x2", Double.toString(activityPointX)); + line.setAttributeNS(null, "y2", Double.toString(activityPointY)); + line.setAttributeNS(null, "style", "stroke:black;stroke-width:1;"); + g.appendChild(line); + + previousActivityPointX = activityPointX; + previousActivityPointY = activityPointY; } - //create activity's rectangle - createRectangle(node, x, y, g); + // check if we need to draw line connecting last activity with endingPoint + if (!sequenceNode.getActivity().getStopAfterActivity()) { + Element line = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "line"); + line.setAttributeNS(null, "x1", Double.toString(previousActivityPointX)); + line.setAttributeNS(null, "y1", Double.toString(previousActivityPointY)); + line.setAttributeNS(null, "x2", Double.toString(x + 132 + SVGConstants.BRANCHING_ACTIVITY_POINT / 2)); + line.setAttributeNS(null, "y2", + Double.toString(startingPointY + SVGConstants.BRANCHING_ACTIVITY_POINT / 2)); + line.setAttributeNS(null, "style", "stroke:black;stroke-width:1;"); + g.appendChild(line); - // Create text label - if (text != null) { - int xText = x + (width / 2); - int yText = y + (height / 2) + 18; - createText("TextElement-" + activityId, xText, yText, null, "middle", "11.4", "Verdana", null, text, g); } - createImage(node, x, y, g); + // Create activity points + activityNodeIterator = sequenceNode.getChildren().iterator(); + for (int activityIndex = 1; activityNodeIterator.hasNext() && (activityIndex <= 6); activityIndex++) { + ActivityTreeNode activityNode = activityNodeIterator.next(); + String activityStyle = sequenceNode.getActivity().getStopAfterActivity() + && !activityNodeIterator.hasNext() ? "stroke:red" : "stroke:black"; + activityStyle += ";stroke-width:0.8;opacity:1" + activityNode.getActivityColor(); + double activityPointX = startingPointX + activityIndex * SVGConstants.BRANCHING_STEP; + double activityPointY = startingPointY + sequenceIndex * SVGConstants.BRANCHING_STEP; + + Element activityPoint = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "rect"); + activityPoint.setAttributeNS(null, "x", Double.toString(activityPointX)); + activityPoint.setAttributeNS(null, "y", Double.toString(activityPointY)); + activityPoint.setAttributeNS(null, "width", "" + SVGConstants.BRANCHING_ACTIVITY_POINT); + activityPoint.setAttributeNS(null, "height", "" + SVGConstants.BRANCHING_ACTIVITY_POINT); + activityPoint.setAttributeNS(null, "style", activityStyle); + g.appendChild(activityPoint); + } } + Element endingPoint = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "rect"); + endingPoint.setAttributeNS(null, "x", Integer.toString(x + 132)); + endingPoint.setAttributeNS(null, "y", Integer.toString(startingPointY)); + endingPoint.setAttributeNS(null, "width", Double.toString(SVGConstants.BRANCHING_ACTIVITY_POINT + 0.5)); + endingPoint.setAttributeNS(null, "height", Double.toString(SVGConstants.BRANCHING_ACTIVITY_POINT + 0.5)); + endingPoint.setAttributeNS(null, "style", "fill:#000000"); + g.appendChild(endingPoint); + + String text = activity.getActivityTitle(); + if (StringUtils.isNotEmpty(text)) { + createText("TextElement-" + activity.getActivityID(), x + SVGConstants.BRANCHING_ACTIVITY_WIDTH / 2, + y + 90, null, "middle", "11.4", "Verdana", null, text, g); + } } - + /** * Returns estimated width and height of the whole SVG document. * @@ -632,7 +645,7 @@ int maxY = 0; for (ActivityTreeNode node : nodes) { Dimension dimension = node.getActivityDimension(); - + int rightestActivityPoint = node.getActivityCoordinates().x + dimension.width; if (rightestActivityPoint > maxX) { maxX = rightestActivityPoint; @@ -642,7 +655,7 @@ maxY = bottomActivityPoint; } } - + int minX = Integer.MAX_VALUE; int minY = Integer.MAX_VALUE; for (ActivityTreeNode node : nodes) { @@ -658,41 +671,65 @@ } } } - - Element svg = doc.getDocumentElement(); - - //Removes padding of the SVG image. - minX--; - minY--; - int width = maxX - minX +5; - int height = maxY - minY +5; - svg.setAttributeNS(null, "viewBox", minX + " " + minY + " " + Integer.toString(width) + " " + Integer.toString(height)); - - // adjust width and height to adjustedDocumentWidth - if ((adjustedDocumentWidth != null) && adjustedDocumentWidth < width) { - double scale = (double) adjustedDocumentWidth / (double) width; - width = adjustedDocumentWidth; - height *= scale; - } - - //Sets the width and height - svg.setAttributeNS(null, "width", Integer.toString(width)); - svg.setAttributeNS(null, "height", Integer.toString(height)); + + for (Point branchingEdgePoint : new Point[] { branchingStartPoint, branchingEndPoint }) { + if (branchingEdgePoint != null) { + int x = new Double(branchingEdgePoint.getX()).intValue(); + if (x + 27 > maxX) { + maxX = x + 27; + } + + if (x < minX) { + minX = x; + } + + int y = new Double(branchingEdgePoint.getY()).intValue(); + if (y + 27 > maxY) { + maxY = y + 27; + } + + if (y < minY) { + minY = y; + } + } + } + + Element svg = doc.getDocumentElement(); + + // Removes padding of the SVG image. + minX--; + minY--; + int width = maxX - minX + 5; + int height = maxY - minY + 5; + svg.setAttributeNS(null, "viewBox", + minX + " " + minY + " " + Integer.toString(width) + " " + Integer.toString(height)); + + // adjust width and height to adjustedDocumentWidth + if ((adjustedDocumentWidth != null) && (adjustedDocumentWidth < width)) { + double scale = (double) adjustedDocumentWidth / (double) width; + width = adjustedDocumentWidth; + height *= scale; + } + + // Sets the width and height + svg.setAttributeNS(null, "width", Integer.toString(width)); + svg.setAttributeNS(null, "height", Integer.toString(height)); } - - //**************** Auxiliary methods for creating svg components ******************************************************** - + + // **************** Auxiliary methods for creating svg components + // ******************************************************** + private void createRectangle(ActivityTreeNode node, double x, double y, Element g) { AuthoringActivityDTO activity = node.getActivity(); Dimension dimension = node.getActivityDimension(); String style = node.getActivityCss(); - + if (style == null) { style = ""; } - - Element rectangle = doc.createElementNS(SVG_NAMESPACE, "rect"); - + + Element rectangle = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "rect"); + rectangle.setAttributeNS(null, "id", "act" + activity.getActivityID()); rectangle.setAttributeNS(null, "x", Double.toString(x)); rectangle.setAttributeNS(null, "y", Double.toString(y)); @@ -701,34 +738,35 @@ rectangle.setAttributeNS(null, "style", style); g.appendChild(rectangle); } - + private void createActvityHeader(ActivityTreeNode node, double x, double y, Element g) { AuthoringActivityDTO activity = node.getActivity(); Dimension dimension = node.getActivityDimension(); - double height = (activity.getActivityTypeID().equals(Activity.PARALLEL_ACTIVITY_TYPE)) ? 23 : CONTAINER_HEADER_HEIGHT; - - Element rectangle = doc.createElementNS(SVG_NAMESPACE, "rect"); - rectangle.setAttributeNS(null, "x", Double.toString(x +4)); - rectangle.setAttributeNS(null, "y", Double.toString(y +5)); - rectangle.setAttributeNS(null, "width", Double.toString(dimension.width -8)); + double height = (activity.getActivityTypeID().equals(Activity.PARALLEL_ACTIVITY_TYPE)) ? 23 + : SVGConstants.CONTAINER_HEADER_HEIGHT; + + Element rectangle = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "rect"); + rectangle.setAttributeNS(null, "x", Double.toString(x + 4)); + rectangle.setAttributeNS(null, "y", Double.toString(y + 5)); + rectangle.setAttributeNS(null, "width", Double.toString(dimension.width - 8)); rectangle.setAttributeNS(null, "height", Double.toString(height)); rectangle.setAttributeNS(null, "style", SVGConstants.CSS_CONTAINER); g.appendChild(rectangle); - } - + } + private void createGroupingEffect(ActivityTreeNode node, double x, double y, Element g) { AuthoringActivityDTO activity = node.getActivity(); Dimension dimension = node.getActivityDimension(); String style = node.getActivityCss(); - - Element groupingRectangle = doc.createElementNS(SVG_NAMESPACE, "rect"); + + Element groupingRectangle = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "rect"); groupingRectangle.setAttributeNS(null, "id", "grouping-" + activity.getActivityID()); groupingRectangle.setAttributeNS(null, "x", Double.toString(x + 4)); groupingRectangle.setAttributeNS(null, "y", Double.toString(y + 4)); groupingRectangle.setAttributeNS(null, "width", Double.toString(dimension.width)); - groupingRectangle.setAttributeNS(null, "height",Double.toString(dimension.height)); + groupingRectangle.setAttributeNS(null, "height", Double.toString(dimension.height)); groupingRectangle.setAttributeNS(null, "style", style + ";stroke:#3b3b3b;stroke-width:3"); - + g.appendChild(groupingRectangle); } @@ -747,16 +785,16 @@ if (fontFamily == null) { fontFamily = "Verdana"; } - - //trim text to fit into container + + // trim text to fit into container if (text.length() > 21) { - text = text.substring(0, 20); + text = text.substring(0, 20); } - - Element textNode = doc.createElementNS(SVG_NAMESPACE, "text"); + + Element textNode = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "text"); Node textContent = doc.createTextNode(text); textNode.appendChild(textContent); - + textNode.setAttributeNS(null, "id", id); textNode.setAttributeNS(null, "x", "" + x); textNode.setAttributeNS(null, "y", "" + y); @@ -767,10 +805,10 @@ if (style != null) { textNode.setAttributeNS(null, "style", style); } - + g.appendChild(textNode); } - + /** * Creates image for a tool. * @@ -783,10 +821,9 @@ * @param g */ private void createImage(ActivityTreeNode node, int x, int y, Element g) { - AuthoringActivityDTO activity = node.getActivity(); Dimension dimension = node.getActivityDimension(); - + // Create image int imageX = x + (dimension.width / 2) - 15; int imageY = y + (dimension.height / 2) - 22; @@ -797,33 +834,276 @@ String imagePath = activity.getLibraryActivityUIImage(); // if png_filename is empty then this is a grouping act: String imageFileName; - if (! StringUtils.isBlank(imagePath)) { + if (!StringUtils.isBlank(imagePath)) { imageFileName = FileUtil.getFileName(imagePath); imageFileName = imageFileName.replaceFirst(".swf$", ".png"); } else if (activity.getActivityTypeID().equals(Activity.SYNCH_GATE_ACTIVITY_TYPE) || activity.getActivityTypeID().equals(Activity.SCHEDULE_GATE_ACTIVITY_TYPE) || activity.getActivityTypeID().equals(Activity.PERMISSION_GATE_ACTIVITY_TYPE) - || activity.getActivityTypeID().equals(Activity.CONDITION_GATE_ACTIVITY_TYPE)) { + || activity.getActivityTypeID().equals(Activity.CONDITION_GATE_ACTIVITY_TYPE)) { imageFileName = "icon_gate.png"; - } else if (activity.getActivityTypeID().equals(Activity.CHOSEN_BRANCHING_ACTIVITY_TYPE) + } else if (activity.getActivityTypeID().equals(Activity.CHOSEN_BRANCHING_ACTIVITY_TYPE) || activity.getActivityTypeID().equals(Activity.GROUP_BRANCHING_ACTIVITY_TYPE) - || activity.getActivityTypeID().equals(Activity.TOOL_BRANCHING_ACTIVITY_TYPE)) { + || activity.getActivityTypeID().equals(Activity.TOOL_BRANCHING_ACTIVITY_TYPE)) { imageFileName = "icon_branching.png"; - } else if (activity.getActivityTypeID().equals(Activity.OPTIONS_WITH_SEQUENCES_TYPE) - || activity.getActivityTypeID().equals(Activity.OPTIONS_ACTIVITY_TYPE)) { + } else if (activity.getActivityTypeID().equals(Activity.OPTIONS_WITH_SEQUENCES_TYPE) + || activity.getActivityTypeID().equals(Activity.OPTIONS_ACTIVITY_TYPE)) { imageFileName = "icon_urlcontentmessageboard.png"; - } else { + } else { imageFileName = "icon_grouping.png"; } - imageFileName = PATH_TO_LAMSCOMMUNITY_SVG_IMAGES + imageFileName; - Element imageNode = doc.createElementNS(SVG_NAMESPACE, "image"); - imageNode.setAttributeNS(null, "id", "image-" + activity.getActivityID()); - imageNode.setAttributeNS(null, "x", Integer.toString(imageX)); - imageNode.setAttributeNS(null, "y", Integer.toString(imageY)); - imageNode.setAttributeNS(SVG_NAMESPACE_XLINK, "xlink:href", imageFileName); + + imageFileName = (SVGGenerator.OUTPUT_FORMAT_SVG_LAMS_COMMUNITY == outputFormat ? SVGConstants.PATH_TO_LAMSCOMMUNITY_SVG_IMAGES + : localSvgIconsPath) + + imageFileName; + String imageId = "image-" + activity.getActivityID(); + createImage(g, imageFileName, imageId, imageX, imageY); + } + + private void createImage(Element g, String imageFileName, String id, int x, int y) { + Element imageNode = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "image"); + imageNode.setAttributeNS(null, "id", id); + imageNode.setAttributeNS(null, "x", Integer.toString(x)); + imageNode.setAttributeNS(null, "y", Integer.toString(y)); + imageNode.setAttributeNS(SVGConstants.SVG_NAMESPACE_XLINK, "xlink:href", imageFileName); imageNode.setAttributeNS(null, "width", Integer.toString(30)); imageNode.setAttributeNS(null, "height", Integer.toString(30)); - g.appendChild(imageNode); + if (g == null) { + Element svgRoot = doc.getDocumentElement(); + svgRoot.appendChild(imageNode); + } else { + g.appendChild(imageNode); + } } - -} + + private void createTransitionLine(String id, Point2D fromIntersection, Point2D toIntersection, String chosenColor) { + // Create the line + String color = chosenColor == null ? "#8C8FA6" : chosenColor; + Element line = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "line"); + line.setAttributeNS(null, "id", id); + line.setAttributeNS(null, "x1", Double.toString(fromIntersection.getX())); + line.setAttributeNS(null, "y1", Double.toString(fromIntersection.getY())); + line.setAttributeNS(null, "x2", Double.toString(toIntersection.getX())); + line.setAttributeNS(null, "y2", Double.toString(toIntersection.getY())); + line.setAttributeNS(null, "style", "stroke:" + color + ";stroke-width:2;opacity:1"); + line.setAttributeNS(null, "parentID", "0"); + + double a = (toIntersection.getX() - fromIntersection.getX()); + double b = (toIntersection.getY() - fromIntersection.getY()); + double yArrowShift = (a * a + b * b == 0) ? 0 : 5 * b / Math.sqrt(a * a + b * b); + double xArrowShift = (a * a + b * b == 0) ? 0 : 5 * a / Math.sqrt(a * a + b * b); + // Create the arrowhead + Element arrowhead = doc.createElementNS(SVGConstants.SVG_NAMESPACE, "line"); + arrowhead.setAttributeNS(null, "id", "arrowhead_" + id); + arrowhead.setAttributeNS(null, "x1", Double.toString(fromIntersection.getX())); + arrowhead.setAttributeNS(null, "y1", Double.toString(fromIntersection.getY())); + arrowhead.setAttributeNS(null, "x2", + Double.toString((fromIntersection.getX() + toIntersection.getX()) / 2 - xArrowShift)); + arrowhead.setAttributeNS(null, "y2", + Double.toString((fromIntersection.getY() + toIntersection.getY()) / 2 - yArrowShift)); + arrowhead.setAttributeNS(null, "style", "fill:" + color + ";stroke:" + color + ";stroke-width:2;opacity:1"); + arrowhead.setAttributeNS(null, "marker-end", "url(#Triangle)"); + arrowhead.setAttributeNS(null, "parentID", "0"); + + // Attach the line to the root 'svg' element. + Element svgRoot = doc.getDocumentElement(); + svgRoot.appendChild(line); + svgRoot.appendChild(arrowhead); + } + + /** + * Gets a map ID -> node for all activities in Learning Design. + */ + private Map getActivityTree(List activities, ActivityTreeNode root) { + // initialize all tree nodes + Map allNodes = new HashMap(); + for (AuthoringActivityDTO activity : activities) { + ActivityTreeNode node = new ActivityTreeNode(activity); + allNodes.put(activity.getActivityID(), node); + } + + // construct activities tree + for (ActivityTreeNode node : allNodes.values()) { + AuthoringActivityDTO activity = node.getActivity(); + if (activity.getParentActivityID() == null) { + root.add(node); + } else { + Long parentId = activity.getParentActivityID(); + ActivityTreeNode parent = allNodes.get(parentId); + parent.add(node); + } + } + + return allNodes; + } + + /** + * Gets map ID -> node for activities within branching. Also groups activities into sets the same way they are + * organised in branches. + */ + private Map getActivityTree(List activities, ActivityTreeNode root, + long branchingActivityId, Set> branchesContainter) { + // this set contains only activities with parents; other activities can not be part of branching + Set allChildActivities = new HashSet(); + // maps Sequence Activity ID to activities it contains + Map> branchMapping = new HashMap>(); + ActivityDTOOrderComparator orderComparator = new ActivityDTOOrderComparator(); + + for (AuthoringActivityDTO activity : activities) { + Long activityId = activity.getActivityID(); + Long parentActivityId = activity.getParentActivityID(); + if (activityId.equals(branchingActivityId)) { + // fill coordinates for doors on canvas + branchingStartPoint = new Point(activity.getStartXCoord(), activity.getStartYCoord()); + branchingEndPoint = new Point(activity.getEndXCoord(), activity.getEndYCoord()); + } else if (parentActivityId != null) { + // get only activities with parents + allChildActivities.add(activity); + + if (parentActivityId.equals(branchingActivityId)) { + // this is Sequence Activity, so a branch + branchMapping.put(activityId, new TreeSet(orderComparator)); + } + } + } + + // now choose only the activities which belong to the given branching + Map branchingNodes = new HashMap(); + for (AuthoringActivityDTO activity : allChildActivities) { + Long parentActivityId = activity.getParentActivityID(); + // is the activity part of Sequence Activity, i.e. branch? + if (branchMapping.keySet().contains(parentActivityId)) { + // branch parts become top-level activities + activity.setParentActivityID(null); + + ActivityTreeNode node = new ActivityTreeNode(activity); + root.add(node); + branchingNodes.put(activity.getActivityID(), node); + + // add the given activity to its branch + branchMapping.get(parentActivityId).add(activity); + } + } + + // activities in branchMapping are sorted with Comparator + // now convert them into a list of nodes + for (Set branch : branchMapping.values()) { + LinkedList branchNodes = new LinkedList(); + for (AuthoringActivityDTO activity : branch) { + branchNodes.add(branchingNodes.get(activity.getActivityID())); + } + branchesContainter.add(branchNodes); + } + + // run the same code twice, for Optional Activities and Sequences + // first iteration sets parents for Optional Activities and sequences within Optional Sequences + for (AuthoringActivityDTO activity : allChildActivities) { + Long parentActivityId = activity.getParentActivityID(); + Long activityId = activity.getActivityID(); + if (branchingNodes.keySet().contains(parentActivityId) && !branchingNodes.keySet().contains(activityId)) { + ActivityTreeNode node = new ActivityTreeNode(activity); + ActivityTreeNode parent = branchingNodes.get(parentActivityId); + parent.add(node); + branchingNodes.put(activityId, node); + } + } + // second iteration sets parents for activities in sequences within Optional Sequences + for (AuthoringActivityDTO activity : allChildActivities) { + Long parentActivityId = activity.getParentActivityID(); + Long activityId = activity.getActivityID(); + if (branchingNodes.keySet().contains(parentActivityId) && !branchingNodes.keySet().contains(activityId)) { + ActivityTreeNode node = new ActivityTreeNode(activity); + ActivityTreeNode parent = branchingNodes.get(parentActivityId); + parent.add(node); + branchingNodes.put(activityId, node); + } + } + + return branchingNodes; + } + + /** + * Draws transitions between activities. + */ + private void createActivityTransitionLines(Map nodes, Collection transitions) { + for (TransitionDTO transition : transitions) { + ActivityTreeNode fromActivity = nodes.get(transition.getFromActivityID()); + ActivityTreeNode toActivity = nodes.get(transition.getToActivityID()); + Point2D fromIntersection = SVGTrigonometryUtils.getActivityAndLineSegmentIntersection(fromActivity, + toActivity); + Point2D toIntersection = SVGTrigonometryUtils.getActivityAndLineSegmentIntersection(toActivity, + fromActivity); + + // skip optional sequence's childs and lines between overlapped activities + if (fromActivity.isOptionalSequenceActivityChild() || (fromIntersection == null) + || (toIntersection == null)) { + continue; + } + + String id = transition.getFromActivityID() + "_to_" + transition.getToActivityID(); + createTransitionLine(id, fromIntersection, toIntersection, null); + } + } + + /** + * Draws initial (from door) and ending (to door) transition lines in branching. + */ + private void createBranchingTransitionLines(Set> branches, ActivityTreeNode root) { + String imageFolder = (SVGGenerator.OUTPUT_FORMAT_SVG_LAMS_COMMUNITY == outputFormat ? SVGConstants.PATH_TO_LAMSCOMMUNITY_SVG_IMAGES + : Configuration.get(ConfigurationKeys.SERVER_URL) + "images/icons/"); + + // first the lines from door + String imageFileName = imageFolder + "door_out.png"; + String imageId = "image-start"; + createImage(null, imageFileName, imageId, new Double(branchingStartPoint.getX()).intValue(), new Double( + branchingStartPoint.getY()).intValue()); + + imageFileName = imageFolder + "door_in.png"; + imageId = "image-end"; + createImage(null, imageFileName, imageId, new Double(branchingEndPoint.getX()).intValue(), new Double( + branchingEndPoint.getY()).intValue()); + + Dimension edgePointsDimensions = new Dimension(27, 27); + Rectangle startRectangle = new Rectangle(branchingStartPoint, edgePointsDimensions); + Rectangle endRectangle = new Rectangle(branchingEndPoint, edgePointsDimensions); + + for (LinkedList branch : branches) { + ActivityTreeNode branchFirstActivity = branch.getFirst(); + Rectangle branchFirstActivityRectangle = new Rectangle(branchFirstActivity.getActivityCoordinates().x, + branchFirstActivity.getActivityCoordinates().y, branchFirstActivity.getActivityDimension().width, + branchFirstActivity.getActivityDimension().height); + + Point2D fromIntersection = SVGTrigonometryUtils.getRectangleAndLineSegmentIntersection(startRectangle, + branchFirstActivityRectangle); + Point2D toIntersection = SVGTrigonometryUtils.getRectangleAndLineSegmentIntersection( + branchFirstActivityRectangle, startRectangle); + + // skip lines between overlapped activities + if ((fromIntersection == null) || (toIntersection == null)) { + continue; + } + + // now the last lines + String id = "start_to_" + branchFirstActivity.getActivity().getActivityID(); + createTransitionLine(id, fromIntersection, toIntersection, "#AFCE63"); + + ActivityTreeNode branchLastActivity = branch.getLast(); + Rectangle branchLastActivityRectangle = new Rectangle(branchLastActivity.getActivityCoordinates().x, + branchLastActivity.getActivityCoordinates().y, branchLastActivity.getActivityDimension().width, + branchLastActivity.getActivityDimension().height); + + fromIntersection = SVGTrigonometryUtils.getRectangleAndLineSegmentIntersection(branchLastActivityRectangle, + endRectangle); + toIntersection = SVGTrigonometryUtils.getRectangleAndLineSegmentIntersection(endRectangle, + branchLastActivityRectangle); + + // skip lines between overlapped activities + if ((fromIntersection == null) || (toIntersection == null)) { + continue; + } + + id = branchLastActivity.getActivity().getActivityID() + "_to_end"; + createTransitionLine(id, fromIntersection, toIntersection, "#AFCE63"); + } + } +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGTrigonometryUtils.java =================================================================== diff -u -ra6a7639e9b454cad77bb2e994192777a8458f8d8 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGTrigonometryUtils.java (.../SVGTrigonometryUtils.java) (revision a6a7639e9b454cad77bb2e994192777a8458f8d8) +++ lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGTrigonometryUtils.java (.../SVGTrigonometryUtils.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -23,52 +23,72 @@ /* $Id$ */ package org.lamsfoundation.lams.util.svg; +import java.awt.Rectangle; import java.awt.geom.Line2D; import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; /** * @author Andrey Balan */ public class SVGTrigonometryUtils { /** - * @param rectangleActivity we draw rectangle based on this activity - * @param toActivity draw transition to this activity + * @param rectangleActivity + * we draw rectangle based on this activity + * @param toActivity + * draw transition to this activity * @return */ - public static Point2D getRectangleAndLineSegmentIntersection(ActivityTreeNode rectangleActivity, ActivityTreeNode toActivity) { - //rectangles dimensions - int rectangleX = rectangleActivity.getActivityCoordinates().x; - int rectangleY = rectangleActivity.getActivityCoordinates().y; - int width = rectangleActivity.getActivityDimension().width; - int height = rectangleActivity.getActivityDimension().height; - - //construct rectangle's lines - Line2D topLine = new Line2D.Double(rectangleX, rectangleY, rectangleX + width, rectangleY); - Line2D rightLine = new Line2D.Double(rectangleX + width, rectangleY, rectangleX + width, rectangleY + height); - Line2D bottomLine = new Line2D.Double(rectangleX, rectangleY + height, rectangleX + width, rectangleY + height); - Line2D leftLine = new Line2D.Double(rectangleX, rectangleY, rectangleX, rectangleY + height); - - //calculate MiddlePoint of the second rectangle - int transitionToX = toActivity.getActivityCoordinates().x + toActivity.getActivityDimension().width /2; - int transitionToY = toActivity.getActivityCoordinates().y + toActivity.getActivityDimension().height /2; - - Line2D transitionLine = new Line2D.Double(rectangleX + width/2, rectangleY + height/2, transitionToX, transitionToY); - + public static Point2D getRectangleAndLineSegmentIntersection(Rectangle2D fromRectangle, Rectangle2D toRectangle) { + // rectangles dimensions + double fromRectangleX = fromRectangle.getX(); + double fromRectangleY = fromRectangle.getY(); + double fromRectangleWidth = fromRectangle.getWidth(); + double fromRectangleHeight = fromRectangle.getHeight(); + + // construct rectangle's lines + Line2D topLine = new Line2D.Double(fromRectangleX, fromRectangleY, fromRectangleX + fromRectangleWidth, + fromRectangleY); + Line2D rightLine = new Line2D.Double(fromRectangleX + fromRectangleWidth, fromRectangleY, fromRectangleX + + fromRectangleWidth, fromRectangleY + fromRectangleHeight); + Line2D bottomLine = new Line2D.Double(fromRectangleX, fromRectangleY + fromRectangleHeight, fromRectangleX + + fromRectangleWidth, fromRectangleY + fromRectangleHeight); + Line2D leftLine = new Line2D.Double(fromRectangleX, fromRectangleY, fromRectangleX, fromRectangleY + + fromRectangleHeight); + + // calculate MiddlePoint of the second rectangle + double transitionToX = toRectangle.getX() + toRectangle.getWidth() / 2; + double transitionToY = toRectangle.getY() + toRectangle.getHeight() / 2; + + Line2D transitionLine = new Line2D.Double(fromRectangleX + fromRectangleWidth / 2, fromRectangleY + + fromRectangleHeight / 2, transitionToX, transitionToY); + Point2D intersectionPoint = null; - if (isLineSegmentsIntersect(topLine, transitionLine)) { - intersectionPoint = getLinesIntersection(topLine, transitionLine); - } else if (isLineSegmentsIntersect(rightLine, transitionLine)) { - intersectionPoint = getLinesIntersection(rightLine, transitionLine); - } else if (isLineSegmentsIntersect(bottomLine, transitionLine)) { - intersectionPoint = getLinesIntersection(bottomLine, transitionLine); - } else if (isLineSegmentsIntersect(leftLine, transitionLine)) { - intersectionPoint = getLinesIntersection(leftLine, transitionLine); + if (SVGTrigonometryUtils.isLineSegmentsIntersect(topLine, transitionLine)) { + intersectionPoint = SVGTrigonometryUtils.getLinesIntersection(topLine, transitionLine); + } else if (SVGTrigonometryUtils.isLineSegmentsIntersect(rightLine, transitionLine)) { + intersectionPoint = SVGTrigonometryUtils.getLinesIntersection(rightLine, transitionLine); + } else if (SVGTrigonometryUtils.isLineSegmentsIntersect(bottomLine, transitionLine)) { + intersectionPoint = SVGTrigonometryUtils.getLinesIntersection(bottomLine, transitionLine); + } else if (SVGTrigonometryUtils.isLineSegmentsIntersect(leftLine, transitionLine)) { + intersectionPoint = SVGTrigonometryUtils.getLinesIntersection(leftLine, transitionLine); } - + return intersectionPoint; } + public static Point2D getActivityAndLineSegmentIntersection(ActivityTreeNode fromActivity, + ActivityTreeNode toActivity) { + Rectangle fromActivityRectangle = new Rectangle(fromActivity.getActivityCoordinates().x, + fromActivity.getActivityCoordinates().y, fromActivity.getActivityDimension().width, + fromActivity.getActivityDimension().height); + Rectangle toActivityRectangle = new Rectangle(toActivity.getActivityCoordinates().x, + toActivity.getActivityCoordinates().y, toActivity.getActivityDimension().width, + toActivity.getActivityDimension().height); + return SVGTrigonometryUtils.getRectangleAndLineSegmentIntersection(fromActivityRectangle, toActivityRectangle); + } + /** * Computes the intersection between first line (x1, y1)--(x2, y2) and second one (x3, y3)--(x4, y4) * @@ -85,8 +105,9 @@ double y4 = line2.getY2(); double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); - if (d == 0) + if (d == 0) { return null; + } double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d; double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d; @@ -105,17 +126,20 @@ double x4 = line2.getX2(); double y4 = line2.getY2(); - int d1 = computeDirection(x3, y3, x4, y4, x1, y1); - int d2 = computeDirection(x3, y3, x4, y4, x2, y2); - int d3 = computeDirection(x1, y1, x2, y2, x3, y3); - int d4 = computeDirection(x1, y1, x2, y2, x4, y4); - return (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) - || (d1 == 0 && isOnSegment(x3, y3, x4, y4, x1, y1)) || (d2 == 0 && isOnSegment(x3, y3, x4, y4, x2, y2)) - || (d3 == 0 && isOnSegment(x1, y1, x2, y2, x3, y3)) || (d4 == 0 && isOnSegment(x1, y1, x2, y2, x4, y4)); + int d1 = SVGTrigonometryUtils.computeDirection(x3, y3, x4, y4, x1, y1); + int d2 = SVGTrigonometryUtils.computeDirection(x3, y3, x4, y4, x2, y2); + int d3 = SVGTrigonometryUtils.computeDirection(x1, y1, x2, y2, x3, y3); + int d4 = SVGTrigonometryUtils.computeDirection(x1, y1, x2, y2, x4, y4); + return ((((d1 > 0) && (d2 < 0)) || ((d1 < 0) && (d2 > 0))) && (((d3 > 0) && (d4 < 0)) || ((d3 < 0) && (d4 > 0)))) + || ((d1 == 0) && SVGTrigonometryUtils.isOnSegment(x3, y3, x4, y4, x1, y1)) + || ((d2 == 0) && SVGTrigonometryUtils.isOnSegment(x3, y3, x4, y4, x2, y2)) + || ((d3 == 0) && SVGTrigonometryUtils.isOnSegment(x1, y1, x2, y2, x3, y3)) + || ((d4 == 0) && SVGTrigonometryUtils.isOnSegment(x1, y1, x2, y2, x4, y4)); } private static boolean isOnSegment(double xi, double yi, double xj, double yj, double xk, double yk) { - return (xi <= xk || xj <= xk) && (xk <= xi || xk <= xj) && (yi <= yk || yj <= yk) && (yk <= yi || xk <= yj); + return ((xi <= xk) || (xj <= xk)) && ((xk <= xi) || (xk <= xj)) && ((yi <= yk) || (yj <= yk)) + && ((yk <= yi) || (xk <= yj)); } private static int computeDirection(double xi, double yi, double xj, double yj, double xk, double yk) { Index: lams_monitoring/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r0460377ca4b1af0fb546741cd617b9a79af6cb36 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_monitoring/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 0460377ca4b1af0fb546741cd617b9a79af6cb36) +++ lams_monitoring/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -291,5 +291,11 @@ learners.search.phrase =Enter search query
or page number learners.search.phrase.go.tooltip =Run search learners.search.phrase.clear.tooltip =Clear search results - +button.live.edit =Live Edit +button.live.edit.tooltip =Edit the current design for this lesson +button.live.edit.confirm =You are about to close Monitor screen and open Live Edit. Do you wish to continue? +button.close.branching =Close Branching +button.close.branching.tooltip =Close the currently open branching activity and come back to the top sequence +button.journal.entries =Journal Entries +button.journal.entries.tooltip =View all Journal Entries saved by Learners #======= End labels: Exported 196 labels for en AU ===== Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java =================================================================== diff -u -rc26bfcb6f8502515eef2c55bba123cb39ce1ccb7 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java (.../IMonitoringService.java) (revision c26bfcb6f8502515eef2c55bba123cb39ce1ccb7) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java (.../IMonitoringService.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -325,7 +325,9 @@ * @return success message. */ public String forceCompleteLessonByUser(Integer learnerId, Integer requesterId, long lessonId, Long activityId); - + + public String forceCompleteActivitiesByUser(Integer learnerId, Integer requesterId, long lessonId, Long activityId); + /** * Archive the specified lesson. When archived, the data is retained but the * learners cannot access the details. Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java =================================================================== diff -u -r97e51b5fbdba25efaa0760489aca191ba14b14d7 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 97e51b5fbdba25efaa0760489aca191ba14b14d7) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -1334,7 +1334,7 @@ * @throws LamsToolServiceException * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#forceCompleteLessonByUser(Integer,long,long) */ - public String forceCompleteLessonByUser(Integer learnerId, Integer requesterId, long lessonId, Long activityId) { + public String forceCompleteActivitiesByUser(Integer learnerId, Integer requesterId, long lessonId, Long activityId) { Lesson lesson = lessonDAO.getLesson(new Long(lessonId)); checkOwnerOrStaffMember(requesterId, lesson, "force complete"); @@ -1344,31 +1344,83 @@ Activity stopActivity = null; if (activityId != null) { - // -1 means back to start of the lesson - if (activityId != -1L) { - stopActivity = activityDAO.getActivityByActivityId(activityId); - if (stopActivity == null) { - throw new MonitoringServiceException("Activity missing. Activity id" + activityId); - } + stopActivity = getActivityById(activityId); + if (stopActivity == null) { + throw new MonitoringServiceException("Activity missing. Activity id" + activityId); } // check if activity is already complete + Activity parentActivity = stopActivity.getParentActivity(); + + // if user is moved into branch, see if he is allowed to do that + if (parentActivity != null && parentActivity.isSequenceActivity()) { + SequenceActivity sequenceActivity = (SequenceActivity) parentActivity; + Group group = sequenceActivity.getSoleGroupForBranch(); + if ((group == null) || !group.hasLearner(learner)) { + return "User is not assigned to the chosen branch"; + } + } + + // check if the target activity or its parents were completed + // if yes, we move user backward, otherwise forward if ((learnerProgress != null) - && ((activityId == -1L) || learnerProgress.getCompletedActivities().containsKey(stopActivity))) { - // if activityID == -1, then stopActivity == null and the method understands it + && (learnerProgress.getCompletedActivities().containsKey(stopActivity) || ((parentActivity != null) && (learnerProgress + .getCompletedActivities().containsKey(parentActivity) || ((parentActivity + .getParentActivity() != null) && learnerProgress.getCompletedActivities().containsKey( + parentActivity.getParentActivity())))))) { return forceUncompleteActivity(learnerProgress, stopActivity); } } Activity currentActivity = learnerProgress.getCurrentActivity(); + Activity stopPreviousActivity = null; + if (stopActivity != null) { + // force complete operates on previous activity, not target + stopPreviousActivity = stopActivity.getTransitionTo().getFromActivity(); + } String stopReason = null; if (currentActivity != null) { - stopReason = forceCompleteActivity(learner, lessonId, learnerProgress, currentActivity, stopActivity, - new ArrayList()); + stopReason = forceCompleteActivity(learner, lessonId, learnerProgress, currentActivity, + stopPreviousActivity, new ArrayList()); + + // without this, there are errors when target is in branching + learnerService.createToolSessionsIfNecessary(stopActivity, learnerProgress); } + return stopReason != null ? stopReason : messageService .getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_STOPPED_UNEXPECTEDLY); + } + + /** + * @throws LamsToolServiceException + * @see org.lamsfoundation.lams.monitoring.service.IMonitoringService#forceCompleteLessonByUser(Integer,long,long) + */ + public String forceCompleteLessonByUser(Integer learnerId, Integer requesterId, long lessonId, Long activityId) { + //TODO: REMOVE THIS METHOD AFTER OLD, FLASH MONITORING IS REMOVED + Lesson lesson = lessonDAO.getLesson(new Long(lessonId)); + checkOwnerOrStaffMember(requesterId, lesson, "force complete"); + User learner = (User) baseDAO.find(User.class, learnerId); + + LearnerProgress learnerProgress = learnerService.getProgress(learnerId, lessonId); + Activity stopActivity = null; + + if (activityId != null) { + stopActivity = getActivityById(activityId); + if (stopActivity == null) { + throw new MonitoringServiceException("Activity missing. Activity id" + activityId); + } + } + + Activity currentActivity = learnerProgress.getCurrentActivity(); + String stopReason = null; + if (currentActivity != null) { + stopReason = forceCompleteActivity(learner, lessonId, learnerProgress, currentActivity, + stopActivity, new ArrayList()); + } + + return stopReason != null ? stopReason : messageService + .getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_STOPPED_UNEXPECTEDLY); } /** @@ -1536,7 +1588,8 @@ * achieve it. */ @SuppressWarnings("unchecked") - private String forceUncompleteActivity(LearnerProgress learnerProgress, Activity previousActivity) { + private String forceUncompleteActivity(LearnerProgress learnerProgress, Activity targetActivity) { + User learner = learnerProgress.getUser(); Activity currentActivity = learnerProgress.getCurrentActivity(); if (currentActivity == null) { // Learner has finished the whole lesson. Find the last activity by traversing the transition. @@ -1546,48 +1599,80 @@ } } + // set of activities for which "attempted" and "completed" status will be removed + Set uncompleteActivities = new HashSet(); + + // check if the target is a part of complex activity + CompletedActivityProgress completedActivityProgress = learnerProgress.getCompletedActivities().get( + targetActivity); + Activity previousActivity = null; + Activity targetParentActivity = targetActivity.getParentActivity(); + if (targetParentActivity != null) { + uncompleteActivities.add(targetParentActivity); + if (targetParentActivity.getParentActivity() != null) { + targetParentActivity = targetParentActivity.getParentActivity(); + uncompleteActivities.add(targetParentActivity); + } + if (completedActivityProgress == null) { + completedActivityProgress = learnerProgress.getCompletedActivities().get(targetParentActivity); + } + } + + // find the activity just before the target activity + if (targetActivity.getTransitionTo() == null) { + // if user is moved to first activity in branch + // previous activity is the one before whole branching activity + if ((targetParentActivity != null) && (targetParentActivity.getTransitionTo() != null)) { + previousActivity = targetParentActivity.getTransitionTo().getFromActivity(); + } + } else { + previousActivity = targetActivity.getTransitionTo().getFromActivity(); + } + learnerProgress.setLessonComplete(LearnerProgress.LESSON_NOT_COMPLETE); learnerProgress.setFinishDate(null); - - // set previous/current/next activity fields learnerProgress.setPreviousActivity(previousActivity); - Activity targetActivity = previousActivity == null ? learnerProgress.getLesson().getLearningDesign() - .getFirstActivity() : previousActivity.getTransitionFrom().getToActivity(); learnerProgress.setCurrentActivity(targetActivity); learnerProgress.setNextActivity(targetActivity); - CompletedActivityProgress completedActivityProgress = learnerProgress.getCompletedActivities().get( - targetActivity); - // grouping activities which need to be reset + // grouping and branch activities which need to be reset Set groupings = new HashSet(); // remove completed activities step by step, all the way from current to target activity do { + uncompleteActivities.add(currentActivity); + if (currentActivity.isComplexActivity()) { - // remove all records within complex activity - for (Activity childActivity : (Set) ((ComplexActivity) currentActivity).getActivities()) { - learnerProgress.getAttemptedActivities().remove(childActivity); - learnerProgress.getCompletedActivities().remove(childActivity); + if (currentActivity.equals(targetParentActivity)) { + // we came to the complex activity which contains our target + currentActivity = targetActivity; + while (currentActivity.getTransitionFrom() != null) { + // find the last activity in the branch and carry on with backwards traversal + currentActivity = currentActivity.getTransitionFrom().getToActivity(); + } + continue; + } else { + // forget all records within complex activity + for (Activity childActivity : (Set) ((ComplexActivity) currentActivity).getActivities()) { + uncompleteActivities.add(childActivity); + if (childActivity.isComplexActivity()) { + uncompleteActivities.addAll((Set) ((ComplexActivity) childActivity) + .getActivities()); + } - if (childActivity.isComplexActivity()) { - for (Activity innerChildActivity : (Set) ((ComplexActivity) childActivity) - .getActivities()) { - learnerProgress.getAttemptedActivities().remove(innerChildActivity); - learnerProgress.getCompletedActivities().remove(innerChildActivity); + // mark the activity to be "unbranched" + if (childActivity.isSequenceActivity()) { + groupings.add(childActivity); } } } } - learnerProgress.getAttemptedActivities().remove(currentActivity); - learnerProgress.getCompletedActivities().remove(currentActivity); - - Long id = currentActivity.getActivityId(); Transition transitionTo = currentActivity.getTransitionTo(); if (transitionTo == null) { // reached beginning of either sequence or complex activity if (currentActivity.getParentActivity() == null) { - // special case when learning design has only on activity + // special case when learning design has only one activity if (!((previousActivity == null) && currentActivity.equals(targetActivity))) { // reached beginning of sequence and target activity was not found, something is wrong throw new MonitoringServiceException("Target activity was not found sequence. Activity id: " @@ -1599,7 +1684,6 @@ // for optional sequences, the real complex activity is 2 tiers up, not just one currentActivity = currentActivity.getParentActivity(); } - // now the current activity is the complex one in main sequence } } else { @@ -1612,25 +1696,76 @@ } while (!currentActivity.equals(targetActivity)); + // forget that user completed and attempted activiites + for (Activity activity : uncompleteActivities) { + learnerProgress.getAttemptedActivities().remove(activity); + learnerProgress.getCompletedActivities().remove(activity); + } + // set target activity as attempted learnerProgress.getCompletedActivities().remove(targetActivity); learnerProgress.getAttemptedActivities().put(targetActivity, completedActivityProgress.getStartDate()); + if (targetParentActivity != null) { + // set parent as attempted + learnerProgress.getAttemptedActivities() + .put(targetParentActivity, completedActivityProgress.getStartDate()); + targetParentActivity = targetActivity.getParentActivity(); + if (targetParentActivity != null) { + // if target was part of branch, then immediate parent was Sequence + // and parent's parent is Branching + learnerProgress.getAttemptedActivities().put(targetParentActivity, + completedActivityProgress.getStartDate()); + } + } learnerProgressDAO.updateLearnerProgress(learnerProgress); - User learner = learnerProgress.getUser(); + // do ungrouping and unbranching for (Activity activity : groupings) { - // fetch real object, otherwise there is a cast error - GroupingActivity groupingActivity = (GroupingActivity) getActivityById(activity.getActivityId()); - Grouping grouping = groupingActivity.getCreateGrouping(); - if (grouping.doesLearnerExist(learner)) { - // cancel existing grouping, so the learner has a chance to be grouped again - Group group = grouping.getGroupBy(learner); - group.getUsers().remove(learner); - groupDAO.saveGroup(group); + if (activity.isGroupingActivity()) { + // fetch real object, otherwise there is a cast error + GroupingActivity groupingActivity = (GroupingActivity) getActivityById(activity.getActivityId()); + Grouping grouping = groupingActivity.getCreateGrouping(); + if (grouping.doesLearnerExist(learner)) { + // cancel existing grouping, so the learner has a chance to be grouped again + Group group = grouping.getGroupBy(learner); + group.getUsers().remove(learner); + groupDAO.saveGroup(group); + } + } else if (activity.isSequenceActivity()) { + SequenceActivity sequenceActivity = (SequenceActivity) getActivityById(activity.getActivityId()); + Group group = sequenceActivity.getSoleGroupForBranch(); + if ((group != null) && group.hasLearner(learner)) { + // remove learner from the branch + removeUsersFromBranch(sequenceActivity.getActivityId(), new String[] { learner.getUserId() + .toString() }); + } + } else { + MonitoringService.log.warn("Unknow activity type marked for ungrouping: " + activity.getActivityId()); } } + if (targetParentActivity != null) { + // needed by Monitor to display user's progress in the given activity + learnerService.createToolSessionsIfNecessary(targetActivity, learnerProgress); + + // if user was moved to a branch, he needs to be force completed from beginning of the branch + // all the way to target activity + Activity precedingUncompleteActivity = null; + Activity precedingActivity = targetActivity; + while (precedingActivity.getTransitionTo() != null) { + precedingActivity = precedingActivity.getTransitionTo().getFromActivity(); + if (!learnerProgress.getCompletedActivities().containsKey(precedingActivity)) { + precedingUncompleteActivity = precedingActivity; + } + } + + if (precedingUncompleteActivity != null) { + return forceCompleteActivity(learner, learnerProgress.getLesson().getLessonId(), learnerProgress, + precedingUncompleteActivity, previousActivity, new ArrayList()); + } + } + return messageService.getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_ACTIVITY, new Object[] { targetActivity.getTitle() }); } Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringServiceProxy.java =================================================================== diff -u -r4ab52d60ee545b9b76bcc65f75c5a400f6105c30 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringServiceProxy.java (.../MonitoringServiceProxy.java) (revision 4ab52d60ee545b9b76bcc65f75c5a400f6105c30) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringServiceProxy.java (.../MonitoringServiceProxy.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -26,62 +26,69 @@ import javax.servlet.ServletContext; +import org.lamsfoundation.lams.authoring.service.IAuthoringService; import org.lamsfoundation.lams.learning.service.ICoreLearnerService; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; - /** - *

This class act as the proxy between web layer and service layer. It is - * designed to decouple the presentation logic and business logic completely. - * In this way, the presentation tier will no longer be aware of the changes in - * service layer. Therefore we can feel free to switch the business logic - * implementation.

+ *

+ * This class act as the proxy between web layer and service layer. It is designed to decouple the presentation logic + * and business logic completely. In this way, the presentation tier will no longer be aware of the changes in service + * layer. Therefore we can feel free to switch the business logic implementation. + *

* * @author Jacky Fang - * @since 2005-4-15 + * @since 2005-4-15 * @version 1.1 * */ -public class MonitoringServiceProxy -{ +public class MonitoringServiceProxy { /** - * Return the monitor domain service object. It will delegate to the Spring - * helper method to retrieve the proper bean from Spring bean factory. - * @param servletContext the servletContext for current application + * Return the monitor domain service object. It will delegate to the Spring helper method to retrieve the proper + * bean from Spring bean factory. + * + * @param servletContext + * the servletContext for current application * @return monitoring service object. */ - public static final IMonitoringService getMonitoringService(ServletContext servletContext) - { - return (IMonitoringService)getDomainService(servletContext,"monitoringService"); + public static final IMonitoringService getMonitoringService(ServletContext servletContext) { + return (IMonitoringService) MonitoringServiceProxy.getDomainService(servletContext, "monitoringService"); } - + /** - * Return the learner domain service object. It will delegate to the Spring - * helper method to retrieve the proper bean from Spring bean factory. - * @param servletContext the servletContext for current application + * Return the learner domain service object. It will delegate to the Spring helper method to retrieve the proper + * bean from Spring bean factory. + * + * @param servletContext + * the servletContext for current application * @return learner service object. */ - public static final ICoreLearnerService getLearnerService(ServletContext servletContext) - { - return (ICoreLearnerService)getDomainService(servletContext,"learnerService"); + public static final ICoreLearnerService getLearnerService(ServletContext servletContext) { + return (ICoreLearnerService) MonitoringServiceProxy.getDomainService(servletContext, "learnerService"); } - + public static final IUserManagementService getUserManagementService(ServletContext servletContext) { - return (IUserManagementService) getDomainService(servletContext, "userManagementService"); + return (IUserManagementService) MonitoringServiceProxy + .getDomainService(servletContext, "userManagementService"); } + public static final IAuthoringService getAuthoringService(ServletContext servletContext) { + return (IAuthoringService) MonitoringServiceProxy.getDomainService(servletContext, "authoringService"); + } + /** - * Retrieve the proper Spring bean from bean factory. - * @param servletContext the servletContext for current application + * Retrieve the proper Spring bean from bean factory. + * + * @param servletContext + * the servletContext for current application * @return the Spring service bean. */ - private static Object getDomainService(ServletContext servletContext,String serviceName) - { - WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); - return wac.getBean(serviceName); + private static Object getDomainService(ServletContext servletContext, String serviceName) { + WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); + return wac.getBean(serviceName); } } Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java =================================================================== diff -u -rdf0f027b7be924b18e67ae36248b7deada2e6f2d -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java (.../MonitoringAction.java) (revision df0f027b7be924b18e67ae36248b7deada2e6f2d) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java (.../MonitoringAction.java) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -51,10 +51,12 @@ import org.apache.tomcat.util.json.JSONArray; import org.apache.tomcat.util.json.JSONException; import org.apache.tomcat.util.json.JSONObject; +import org.lamsfoundation.lams.authoring.service.IAuthoringService; import org.lamsfoundation.lams.learning.service.ICoreLearnerService; import org.lamsfoundation.lams.learning.web.bean.ActivityURL; import org.lamsfoundation.lams.learningdesign.Activity; import org.lamsfoundation.lams.learningdesign.ContributionTypes; +import org.lamsfoundation.lams.learningdesign.exception.LearningDesignException; import org.lamsfoundation.lams.lesson.LearnerProgress; import org.lamsfoundation.lams.lesson.Lesson; import org.lamsfoundation.lams.lesson.dto.LessonDetailsDTO; @@ -73,7 +75,9 @@ import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; import org.lamsfoundation.lams.usermanagement.exception.UserAccessDeniedException; +import org.lamsfoundation.lams.usermanagement.exception.UserException; 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.DateUtil; @@ -637,7 +641,16 @@ long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); Integer learnerId = new Integer(WebUtil.readIntParam(request, MonitoringConstants.PARAM_LEARNER_ID)); Integer requesterId = getUserId(); - String message = monitoringService.forceCompleteLessonByUser(learnerId, requesterId, lessonId, activityId); + + // true if old, Flash Monitoring is used; to be removed after new Monitoring is adopted + boolean isPreviousActivity = WebUtil.readBooleanParam(request, "isPreviousActivity", true); + String message = null; + if (isPreviousActivity) { + message = monitoringService.forceCompleteLessonByUser(learnerId, requesterId, lessonId, activityId); + } else { + message = monitoringService.forceCompleteActivitiesByUser(learnerId, requesterId, lessonId, activityId); + } + if (LamsDispatchAction.log.isDebugEnabled()) { LamsDispatchAction.log.debug("Force complete for learner " + learnerId + " lesson " + lessonId + ". " + message); @@ -1272,6 +1285,8 @@ public ActionForward getLessonProgressJSON(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws JSONException, IOException { long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); + Long branchingActivityId = WebUtil.readLongParam(request, "branchingActivityID", true); + Lesson lesson = getLessonService().getLesson(lessonId); IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() .getServletContext()); @@ -1282,36 +1297,52 @@ // few details for each activity Map activitiesMap = new TreeMap(); for (Activity activity : (Set) lesson.getLearningDesign().getActivities()) { - JSONObject activityJSON = new JSONObject(); - Long activityId = activity.getActivityId(); - activityJSON.put("id", activityId); - String monitorUrl = monitoringService.getActivityMonitorURL(lessonId, activityId, contentFolderId, - monitorUserId); - if (monitorUrl != null) { - // whole activity monitor URL - activityJSON.put("url", monitorUrl); + if ((branchingActivityId == null) || MonitoringAction.isBranchingChild(branchingActivityId, activity)) { + Long activityId = activity.getActivityId(); + JSONObject activityJSON = new JSONObject(); + activityJSON.put("id", activityId); + + if (activity.isBranchingActivity()) { + activityJSON.put("isBranching", true); + } else { + String monitorUrl = monitoringService.getActivityMonitorURL(lessonId, activityId, contentFolderId, + monitorUserId); + if (monitorUrl != null) { + // whole activity monitor URL + activityJSON.put("url", monitorUrl); + } + } + activitiesMap.put(activityId, activityJSON); } - activitiesMap.put(activityId, activityJSON); } JSONObject responseJSON = new JSONObject(); for (LearnerProgress learnerProgress : (Set) lesson.getLearnerProgresses()) { User learner = learnerProgress.getUser(); - JSONObject learnerJSON = MonitoringAction.userToJSON(learner); if (learnerProgress.isComplete()) { + JSONObject learnerJSON = MonitoringAction.userToJSON(learner); // no more details are needed for learners who completed the lesson responseJSON.append("completedLearners", learnerJSON); } else { Activity currentActivity = learnerProgress.getCurrentActivity(); - if (currentActivity != null) { - Long activityId = currentActivity.getActivityId(); + if ((currentActivity != null) + && ((branchingActivityId == null) || MonitoringAction.isBranchingChild(branchingActivityId, + currentActivity))) { + JSONObject learnerJSON = MonitoringAction.userToJSON(learner); + Long currentActivityId = currentActivity.getActivityId(); // monitoring URL for the given learner - String learnerUrl = monitoringService.getLearnerActivityURL(lessonId, activityId, + String learnerUrl = monitoringService.getLearnerActivityURL(lessonId, currentActivityId, learner.getUserId(), monitorUserId); learnerJSON.put("url", learnerUrl); - JSONObject currentActivityJSON = activitiesMap.get(currentActivity.getActivityId()); - currentActivityJSON.append("learners", learnerJSON); + Activity parentActivity = currentActivity.getParentActivity(); + Long targetActivityId = (branchingActivityId != null) || (parentActivity == null) + || (parentActivity.getParentActivity() == null) + || !parentActivity.getParentActivity().isBranchingActivity() ? currentActivity + .getActivityId() : parentActivity.getParentActivity().getActivityId(); + + JSONObject targetActivityJSON = activitiesMap.get(targetActivityId); + targetActivityJSON.append("learners", learnerJSON); } } } @@ -1415,6 +1446,23 @@ return null; } + public ActionForward startLiveEdit(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws LearningDesignException, UserException, IOException { + long learningDesignId = WebUtil.readLongParam(request, CentralConstants.PARAM_LEARNING_DESIGN_ID); + Integer userID = getUserId(); + + IAuthoringService authoringService = MonitoringServiceProxy.getAuthoringService(getServlet() + .getServletContext()); + + if (authoringService.setupEditOnFlyLock(learningDesignId, userID)) { + authoringService.setupEditOnFlyGate(learningDesignId, userID); + } else { + response.getWriter().write("Someone else is editing the design at the moment."); + } + + return null; + } + /** * Delete all old preview lessons and their related data, across all organisations. Should go to a monitoring * webservice maybe ? @@ -1681,4 +1729,13 @@ } return result; } + + private static boolean isBranchingChild(Long branchingActivityId, Activity activity) { + if ((branchingActivityId == null) || (activity == null)) { + return false; + } + Activity parentActivity = activity.getParentActivity(); + return (parentActivity != null) && (parentActivity.getParentActivity() != null) + && parentActivity.getParentActivity().getActivityId().equals(branchingActivityId); + } } \ No newline at end of file Index: lams_monitoring/web/css/monitorLesson.css =================================================================== diff -u -r0460377ca4b1af0fb546741cd617b9a79af6cb36 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_monitoring/web/css/monitorLesson.css (.../monitorLesson.css) (revision 0460377ca4b1af0fb546741cd617b9a79af6cb36) +++ lams_monitoring/web/css/monitorLesson.css (.../monitorLesson.css) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -164,8 +164,14 @@ overflow: hidden; } +#closeBranchingButton { + font-weight: bold; + margin-right: 40px; + display: none; +} + img#sequenceCanvasLoading { - padding: 5px 0 0 370px; + padding: 5px 0 0 100px; display: none; } Index: lams_monitoring/web/includes/javascript/monitorLesson.js =================================================================== diff -u -r0460377ca4b1af0fb546741cd617b9a79af6cb36 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_monitoring/web/includes/javascript/monitorLesson.js (.../monitorLesson.js) (revision 0460377ca4b1af0fb546741cd617b9a79af6cb36) +++ lams_monitoring/web/includes/javascript/monitorLesson.js (.../monitorLesson.js) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -1,9 +1,12 @@ // ********** GLOBAL VARIABLES ********** -// copy of lesson SVG so it does no need to be fetched every time +// copy of lesson/branching SVG so it does no need to be fetched every time // HTML with SVG of the lesson var originalSequenceCanvas = null; -// DIV container for lesson SVG; it gets accessed so many times it's worth to cache it here +// DIV container for lesson/branching SVG +// it gets accessed so many times it's worth to cache it here var sequenceCanvas = null; +// ID of currently shown branching activity; if NULL, the whole lesson is shown +var sequenceBranchingId = null; // how learners in pop up lists are currently sorted var sortOrderAsc = { learnerGroup : false, @@ -489,11 +492,17 @@ data : { 'method' : 'createLearningDesignThumbnail', 'svgFormat' : 1, - 'ldId' : ldId + 'ldId' : ldId, + 'branchingActivityID' : sequenceBranchingId }, success : function(response) { originalSequenceCanvas = response; - sequenceCanvas = $('#sequenceCanvas').html(originalSequenceCanvas); + sequenceCanvas = $('#sequenceCanvas') + // remove previously set padding and dimensions, if any + .removeAttr('style') + .html(originalSequenceCanvas) + // if it was faded out by showBranchingSequence() + .fadeIn(); var canvasHeight = sequenceCanvas.height(); var canvasWidth = sequenceCanvas.width(); @@ -533,12 +542,13 @@ cache : false, data : { 'method' : 'getLessonProgressJSON', - 'lessonID' : lessonId + 'lessonID' : lessonId, + 'branchingActivityID' : sequenceBranchingId }, success : function(response) { // remove the loading animation $('img#sequenceCanvasLoading', sequenceTopButtonsContainer).remove(); - + var learnerCount = 0; $.each(response.activities, function(){ if (this.learners) { @@ -562,12 +572,22 @@ $.each(response.activities, function(activityIndex, activity){ addLearnerIconsHandlers(activity); - if (activity.url) { - var activityGroup = $('g#' + activity.id, sequenceCanvas); - activityGroup.css('cursor', 'pointer').dblclick(function(){ - // double click on activity shape to open Monitoring for this activity - openPopUp(LAMS_URL + activity.url, "MonitorActivity", 720, 900, true); - }); + if (activity.url || activity.isBranching) { + // find activity group, if it is not hidden + $('g#' + activity.id, sequenceCanvas) + .css('cursor', 'pointer') + .dblclick( + // different behaviour for regular/branching activities + activity.isBranching ? + function(){ + showBranchingSequence(activity.id); + } + : + function(){ + // double click on activity shape to open Monitoring for this activity + openPopUp(LAMS_URL + activity.url, "MonitorActivity", 720, 900, true); + } + ); } }); @@ -603,7 +623,7 @@ var actEndY = actY + actHeight; if (x >= actX && x<= actEndX && y>= actY && y<=actEndY) { - var previousActivityId = null; + var targetActivityId = null; var executeForceComplete = false; if (act.attr('id') == 'completedLearnersContainer') { @@ -612,12 +632,6 @@ } else { var targetActivityId = act.parent().attr('id'); if (currentActivityId != targetActivityId) { - var transitionLine = $('line[id$="to_' + targetActivityId + '"]:not([id^="arrow"])' - ,sequenceCanvas); - // if move to start of sequence, the value is -1 - previousActivityId = transitionLine.length == 1 ? - transitionLine.attr('id').split('_')[0] : -1; - var targetActivityName = act.is('polygon') ? "Gate" : act.siblings('text[id^="TextElement"]').text(); executeForceComplete = confirm(FORCE_COMPLETE_ACTIVITY_CONFIRM_LABEL @@ -635,7 +649,7 @@ 'method' : 'forceComplete', 'lessonID' : lessonId, 'learnerID' : learnerId, - 'activityID' : previousActivityId + 'activityID' : targetActivityId }, success : function(response) { // inform user of result @@ -674,6 +688,9 @@ var polygonStartPoints = polygonPoints[4].split(','); actX = +polygonStartPoints[0]; actY = +polygonStartPoints[1] - 10; + } else { + // unknown or invisible shape (System Gate?) + return; } } else { actX = +activityShape.attr('x') + 1; @@ -710,6 +727,7 @@ appendXMLElement('title', null, groupTitle, element); // stop processing learners return false; + } else { /* make an icon for each learner */ var element = appendXMLElement('image', { @@ -734,53 +752,59 @@ * After SVG refresh, add click/dblclick/drag handlers to user icons. */ function addLearnerIconsHandlers(activity) { - if (activity.learners) { - var activityGroup = $('g#' + activity.id, sequenceCanvas); - // gate activity does not allows users' view - var usersViewable = $('polygon', activityGroup).length == 0; - - $.each(activity.learners, function(learnerIndex, learner){ - var learnerIcon = $('image[id="act' + activity.id + 'learner' + learner.id + '"]' - ,activityGroup); - learnerIcon .css('cursor', 'pointer') - // drag learners to force complete activities - .draggable({ - 'appendTo' : '#tabSequence', - 'containment' : '#tabSequence', - 'distance' : 20, - 'scroll' : false, - 'cursorAt' : {'left' : 10, 'top' : 15}, - 'helper' : function(event){ - // copy of the icon for dragging - return $('').attr('src', LAMS_URL + 'images/icons/user.png'); - }, - 'stop' : function(event, ui) { - // jQuery droppable does not work for SVG, so this is a workaround - forceComplete(activity.id, learner.id, getLearnerDisplayName(learner), - ui.offset.left, ui.offset.top); - } - }); - - if (usersViewable) { - learnerIcon.dblclick(function(event){ - // double click on learner icon to see activity from his perspective - event.stopPropagation(); - openPopUp(LAMS_URL + learner.url, "LearnActivity", 600, 800, true); - }); + if (!activity.learners) { + return; + } + + var activityGroup = $('g#' + activity.id, sequenceCanvas); + if (activityGroup.length == 0) { + // the activity is probably hidden (branching child, system gate) + return; + } + // gate activity does not allows users' view + var usersViewable = $('polygon', activityGroup).length == 0; + + $.each(activity.learners, function(learnerIndex, learner){ + var learnerIcon = $('image[id="act' + activity.id + 'learner' + learner.id + '"]' + ,activityGroup); + learnerIcon .css('cursor', 'pointer') + // drag learners to force complete activities + .draggable({ + 'appendTo' : '#tabSequence', + 'containment' : '#tabSequence', + 'distance' : 20, + 'scroll' : false, + 'cursorAt' : {'left' : 10, 'top' : 15}, + 'helper' : function(event){ + // copy of the icon for dragging + return $('').attr('src', LAMS_URL + 'images/icons/user.png'); + }, + 'stop' : function(event, ui) { + // jQuery droppable does not work for SVG, so this is a workaround + forceComplete(activity.id, learner.id, getLearnerDisplayName(learner), + ui.offset.left, ui.offset.top); } }); - - var learnerGroupIcon = $('*[id^="act' + activity.id + 'learnerGroup"]', activityGroup); - // 0 is for no group icon, 2 is for icon + digits - if (learnerGroupIcon.length == 2) { - var activityName = $('text[id^="TextElement"]', activityGroup).text(); - learnerGroupIcon.dblclick(function(event){ + if (usersViewable) { + learnerIcon.dblclick(function(event){ // double click on learner icon to see activity from his perspective event.stopPropagation(); - showLearnerGroupDialog(activity.id, activityName, activity.learners, true, usersViewable); - }) + openPopUp(LAMS_URL + learner.url, "LearnActivity", 600, 800, true); + }); } + }); + + + var learnerGroupIcon = $('*[id^="act' + activity.id + 'learnerGroup"]', activityGroup); + // 0 is for no group icon, 2 is for icon + digits + if (learnerGroupIcon.length == 2) { + var activityName = $('text[id^="TextElement"]', activityGroup).text(); + learnerGroupIcon.dblclick(function(event){ + // double click on learner icon to see activity from his perspective + event.stopPropagation(); + showLearnerGroupDialog(activity.id, activityName, activity.learners, true, usersViewable); + }) } } @@ -933,7 +957,56 @@ } +/** + * Opens Authoring for live edit. + */ +function openLiveEdit(){ + if (confirm(LIVE_EDIT_CONFIRM_LABEL)) { + $.ajax({ + dataType : 'text', + url : LAMS_URL + 'monitoring/monitoring.do', + cache : false, + async : false, + data : { + 'method' : 'startLiveEdit', + 'ldId' : ldId + }, + success : function(response) { + if (response) { + alert(response); + } else { + openPopUp(LAMS_URL + 'home.do?method=author&layout=editonfly&learningDesignID=' + ldId, + 'Live Edit', 600, 800, false); + window.parent.closeMonitorLessonDialog(); + } + } + }); + } +} + +/** + * Replaces canvas with the given branchin activity contents + */ +function showBranchingSequence(branchingActivityId){ + sequenceBranchingId = branchingActivityId; + originalSequenceCanvas = null; + $('#closeBranchingButton').show(); + sequenceCanvas.fadeOut(function(){ + sequenceCanvas.html(null); + updateSequenceTab(); + }); +} + + +/** + * Shows Learning Design in canvas. + */ +function closeBranchingSequence(){ + showBranchingSequence(null); + $('#closeBranchingButton').hide(); +} + //********** LEARNERS TAB FUNCTIONS ********** /** Index: lams_monitoring/web/monitor.jsp =================================================================== diff -u -r0460377ca4b1af0fb546741cd617b9a79af6cb36 -r1b117caf4135f53248542cbc97d71aac448f3de9 --- lams_monitoring/web/monitor.jsp (.../monitor.jsp) (revision 0460377ca4b1af0fb546741cd617b9a79af6cb36) +++ lams_monitoring/web/monitor.jsp (.../monitor.jsp) (revision 1b117caf4135f53248542cbc97d71aac448f3de9) @@ -71,7 +71,9 @@ var EXPORT_PORTFOLIO_LEARNER_TOOLTIP_LABEL = ''; var TIME_CHART_LABEL = ''; var TIME_CHART_TOOLTIP_LABEL = ''; - + var LIVE_EDIT_CONFIRM_LABEL = ''; + + $(document).ready(function(){ $('#tabs').tabs(); @@ -280,15 +282,26 @@
- - - - ', 640, 240)"> - + href="http://wiki.lamsfoundation.org/display/lamsdocs/monitoringsequence"> + + + + ', 240, 640, true)"> + + + + + + +
@@ -329,9 +342,16 @@ onChange="javascript:loadLearnerProgressPage()" /> + + - + + ', 570, 796, true)"> +