Index: lams_central/src/java/org/lamsfoundation/lams/authoring/ObjectExtractor.java =================================================================== diff -u -rcbeaf1dfb879f89aa826b58a85ef2343a9813bbc -r016db2a9e2faacd0b9d20dd874571756eea79a14 --- lams_central/src/java/org/lamsfoundation/lams/authoring/ObjectExtractor.java (.../ObjectExtractor.java) (revision cbeaf1dfb879f89aa826b58a85ef2343a9813bbc) +++ lams_central/src/java/org/lamsfoundation/lams/authoring/ObjectExtractor.java (.../ObjectExtractor.java) (revision 016db2a9e2faacd0b9d20dd874571756eea79a14) @@ -239,13 +239,28 @@ Long learningDesignId = WDDXProcessor.convertToLong(table, "learningDesignID"); //if the learningDesignID is not null, load the existing LearningDesign object from the database, otherwise create a new one. learningDesign = learningDesignId!= null ? learningDesignDAO.getLearningDesignById(learningDesignId) : new LearningDesign(); - + + // Check the copy type. Can only update it if it is COPY_TYPE_NONE (ie authoring copy) + Integer copyTypeID = WDDXProcessor.convertToInteger(table,WDDXTAGS.COPY_TYPE); + if ( copyTypeID == null ) { + copyTypeID = LearningDesign.COPY_TYPE_NONE; + } + if ( learningDesign != null && learningDesign.getCopyTypeID() != null && + ! learningDesign.getCopyTypeID().equals(copyTypeID) ) { + throw new ObjectExtractorException("Unable to save learning design. Cannot change copy type on existing design."); + } + if ( ! copyTypeID.equals(LearningDesign.COPY_TYPE_NONE) ) { + throw new ObjectExtractorException("Unable to save learning design. Learning design is read-only"); + } + learningDesign.setCopyTypeID(copyTypeID); + // Pull out all the existing groups. there isn't an easy way to pull them out of the db requires an outer join across // three objects (learning design -> grouping activity -> grouping) so put both the existing ones and the new ones // here for reference later. initialiseGroupings(); - //get the core learning design stuff + //get the core learning design stuff - default to invalid + learningDesign.setValidDesign(Boolean.FALSE); if (keyExists(table, WDDXTAGS.LEARNING_DESIGN_UIID)) learningDesign.setLearningDesignUIID(WDDXProcessor.convertToInteger(table,WDDXTAGS.LEARNING_DESIGN_UIID)); if (keyExists(table, WDDXTAGS.DESCRIPTION)) @@ -266,8 +281,6 @@ learningDesign.setOnlineInstructions(WDDXProcessor.convertToString(table,WDDXTAGS.ONLINE_INSTRUCTIONS)); if (keyExists(table, WDDXTAGS.HELP_TEXT)) learningDesign.setHelpText(WDDXProcessor.convertToString(table,WDDXTAGS.HELP_TEXT)); - if (keyExists(table, WDDXTAGS.COPY_TYPE)) - learningDesign.setCopyTypeID(WDDXProcessor.convertToInteger(table,WDDXTAGS.COPY_TYPE)); if (keyExists(table, WDDXTAGS.CREATION_DATE)) learningDesign.setCreateDateTime(WDDXProcessor.convertToDate(table,WDDXTAGS.CREATION_DATE)); if (keyExists(table, WDDXTAGS.VERSION)) @@ -485,7 +498,7 @@ } } - // clear the transitions + // clear the transitions. // clear the old set and reset up the activities set. Done this way to keep the Hibernate cascading happy. // this means we don't have to manually remove the transition object. // Note: This will leave orphan content in the tool tables. It can be removed by the tool content cleaning job, @@ -526,15 +539,20 @@ Integer parentUIID = WDDXProcessor.convertToInteger(activityDetails, WDDXTAGS.PARENT_UIID); if( parentUIID!=null ) { Activity parentActivity = newActivityMap.get(parentUIID); + if ( parentActivity == null ) { + throw new ObjectExtractorException("Parent activity "+parentUIID+" missing for activity "+existingActivity.getTitle()+": "+existingActivity.getActivityUIID()); + } existingActivity.setParentActivity(parentActivity); existingActivity.setParentUIID(parentUIID); if(parentActivity.isComplexActivity()){ ((ComplexActivity) parentActivity).addActivity(existingActivity); activityDAO.update(parentActivity); } + } else { existingActivity.setParentActivity(null); existingActivity.setParentUIID(null); + existingActivity.setOrderId(null); // top level activities don't have order ids. } } activityDAO.update(existingActivity); @@ -705,9 +723,12 @@ else if(activity.isGateActivity()) buildGateActivity(activity,activityDetails); else - buildComplexActivity(activity,activityDetails); + buildComplexActivity((ComplexActivity)activity,activityDetails); } - private void buildComplexActivity(Object activity,Hashtable activityDetails) throws WDDXProcessorConversionException{ + private void buildComplexActivity(ComplexActivity activity,Hashtable activityDetails) throws WDDXProcessorConversionException{ + // clear all the children - will be built up again by parseActivitiesToMatchUpParentActivityByParentUIID + // we don't use all-delete-orphan on the activities relationship so we can do this clear. + activity.getActivities().clear(); if(activity instanceof OptionsActivity) buildOptionsActivity((OptionsActivity)activity,activityDetails); else if (activity instanceof ParallelActivity) Index: lams_central/src/java/org/lamsfoundation/lams/authoring/service/AuthoringService.java =================================================================== diff -u -r0b59b8e9d849190d6748b20c29b88ee7c7eb8fd2 -r016db2a9e2faacd0b9d20dd874571756eea79a14 --- lams_central/src/java/org/lamsfoundation/lams/authoring/service/AuthoringService.java (.../AuthoringService.java) (revision 0b59b8e9d849190d6748b20c29b88ee7c7eb8fd2) +++ lams_central/src/java/org/lamsfoundation/lams/authoring/service/AuthoringService.java (.../AuthoringService.java) (revision 016db2a9e2faacd0b9d20dd874571756eea79a14) @@ -39,8 +39,6 @@ import org.apache.log4j.Logger; import org.lamsfoundation.lams.authoring.IObjectExtractor; -import org.lamsfoundation.lams.authoring.ObjectExtractorException; -import org.lamsfoundation.lams.authoring.dto.StoreLearningDesignResultsDTO; import org.lamsfoundation.lams.dao.hibernate.BaseDAO; import org.lamsfoundation.lams.learningdesign.Activity; import org.lamsfoundation.lams.learningdesign.ActivityOrderComparator; @@ -62,6 +60,7 @@ import org.lamsfoundation.lams.learningdesign.dto.LearningDesignDTO; import org.lamsfoundation.lams.learningdesign.dto.LearningLibraryDTO; import org.lamsfoundation.lams.learningdesign.dto.LibraryActivityDTO; +import org.lamsfoundation.lams.learningdesign.dto.ValidationErrorDTO; import org.lamsfoundation.lams.learningdesign.exception.LearningDesignException; import org.lamsfoundation.lams.learningdesign.service.ILearningDesignService; import org.lamsfoundation.lams.tool.Tool; @@ -457,48 +456,41 @@ * It received a WDDX packet from flash, deserializes it * and then finally persists it to the database. * + * Note: it does not validate the design - that must be done + * separately. + * * @param wddxPacket The WDDX packet received from Flash - * @return String The acknowledgement in WDDX format that the design has been - * successfully saved. + * @return Long learning design id * @throws Exception */ - public String storeLearningDesignDetails(String wddxPacket) throws Exception{ - Vector listOfValidationErrorDTOs = null; - boolean valid = true; - + public Long storeLearningDesignDetails(String wddxPacket) throws Exception { + Hashtable table = (Hashtable)WDDXProcessor.deserialize(wddxPacket); IObjectExtractor extractor = (IObjectExtractor) beanFactory.getBean(IObjectExtractor.OBJECT_EXTRACTOR_SPRING_BEANNAME); - - try { - LearningDesign design = extractor.extractSaveLearningDesign(table); - listOfValidationErrorDTOs = (Vector)learningDesignService.validateLearningDesign(design); - - if (listOfValidationErrorDTOs.size() > 0) - { - valid = Boolean.FALSE; - flashMessage = new FlashMessage("storeLearningDesignDetails", new StoreLearningDesignResultsDTO(valid,listOfValidationErrorDTOs, design.getLearningDesignId()), FlashMessage.OBJECT_MESSAGE); - } - else - { - valid = Boolean.TRUE; - flashMessage = new FlashMessage("storeLearningDesignDetails", new StoreLearningDesignResultsDTO(valid, design.getLearningDesignId())); - } + LearningDesign design = extractor.extractSaveLearningDesign(table); + return design.getLearningDesignId(); + } - if(design.getCopyTypeID() != LearningDesign.COPY_TYPE_NONE) - throw new Exception("Unable to save learning design. Learning design is read-only"); - - design.setValidDesign(valid); - learningDesignDAO.insertOrUpdate(design); - - //flashMessage = new FlashMessage(IAuthoringService.STORE_LD_MESSAGE_KEY,design.getLearningDesignId()); - } catch ( Exception e ) { - flashMessage = new FlashMessage(IAuthoringService.STORE_LD_MESSAGE_KEY, - messageService.getMessage("invalid.wddx.packet",new Object[]{e.getMessage()}), - FlashMessage.ERROR); - } - return flashMessage.serializeMessage(); + /** + * Validate the learning design, updating the valid flag appropriately. + * + * This needs to be run in a separate transaction to storeLearningDesignDetails to + * ensure the database is fully updated before the validation occurs (due to some + * quirks we are finding using Hibernate) + * + * @param learningDesignId + * @throws Exception + */ + public Vector validateLearningDesign(Long learningDesignId) { + LearningDesign learningDesign = learningDesignDAO.getLearningDesignById(learningDesignId); + Vector listOfValidationErrorDTOs = learningDesignService.validateLearningDesign(learningDesign); + Boolean valid = listOfValidationErrorDTOs.size() > 0 ? Boolean.FALSE : Boolean.TRUE; + learningDesign.setValidDesign(valid); + learningDesignDAO.insertOrUpdate(learningDesign); + return listOfValidationErrorDTOs; } + /** * (non-Javadoc) * @see org.lamsfoundation.lams.authoring.service.IAuthoringService#getAllLearningDesignDetails() Index: lams_central/src/java/org/lamsfoundation/lams/authoring/service/IAuthoringService.java =================================================================== diff -u -rd98b61a50b1a6f7cbaf419e9904fc4eebe91ac5d -r016db2a9e2faacd0b9d20dd874571756eea79a14 --- lams_central/src/java/org/lamsfoundation/lams/authoring/service/IAuthoringService.java (.../IAuthoringService.java) (revision d98b61a50b1a6f7cbaf419e9904fc4eebe91ac5d) +++ lams_central/src/java/org/lamsfoundation/lams/authoring/service/IAuthoringService.java (.../IAuthoringService.java) (revision 016db2a9e2faacd0b9d20dd874571756eea79a14) @@ -28,6 +28,7 @@ import java.util.Vector; import org.lamsfoundation.lams.learningdesign.LearningDesign; +import org.lamsfoundation.lams.learningdesign.dto.ValidationErrorDTO; import org.lamsfoundation.lams.learningdesign.exception.LearningDesignException; import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.WorkspaceFolder; @@ -94,11 +95,6 @@ * @return List Returns the list of all the available LearningDesign's * */ public List getAllLearningDesigns(); - /** - * Saves the LearningDesign to the database. Will update if already saved. - * @param learningDesign The LearningDesign to be saved - * */ - public void saveLearningDesign(LearningDesign learningDesign); /** * @return List Returns a list of all available Learning Libraries @@ -115,19 +111,31 @@ public String getLearningDesignDetails(Long learningDesignID)throws IOException; /** - * This method saves the information which comes in WDDX format - * into the database. It returns An acknowledgemnet message - * telling FLASH the status of the operation, i.e. whether the - * design has been successfully saved or not. If Yes it returns - * the learning_design_id of the design just saved. This information - * is sent back in a format understood by FLASH, WDDX. + * This method saves a new Learning Design to the database. + * It received a WDDX packet from flash, deserializes it + * and then finally persists it to the database. * + * Note: it does not validate the design - that must be done + * separately. + * * @param wddxPacket The WDDX packet to be stored in the database - * @return String The acknowledgemnet message + * @return Long learning design id * @throws Exception */ - public String storeLearningDesignDetails(String wddxPacket)throws Exception; + public Long storeLearningDesignDetails(String wddxPacket) throws Exception; + /** + * Validate the learning design, updating the valid flag appropriately. + * + * This needs to be run in a separate transaction to storeLearningDesignDetails to + * ensure the database is fully updated before the validation occurs (due to some + * quirks we are finding using Hibernate) + * + * @param learningDesignId + * @throws Exception + */ + public Vector validateLearningDesign(Long learningDesignId); + /** * This method returns a list of all available Learning Designs * in WDDX format. Index: lams_central/src/java/org/lamsfoundation/lams/authoring/web/StoreLDServlet.java =================================================================== diff -u -rde1940e60766f8901436c6ab567c692685d5b35c -r016db2a9e2faacd0b9d20dd874571756eea79a14 --- lams_central/src/java/org/lamsfoundation/lams/authoring/web/StoreLDServlet.java (.../StoreLDServlet.java) (revision de1940e60766f8901436c6ab567c692685d5b35c) +++ lams_central/src/java/org/lamsfoundation/lams/authoring/web/StoreLDServlet.java (.../StoreLDServlet.java) (revision 016db2a9e2faacd0b9d20dd874571756eea79a14) @@ -23,10 +23,15 @@ /* $$Id$$ */ package org.lamsfoundation.lams.authoring.web; +import java.util.Vector; + import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; +import org.lamsfoundation.lams.authoring.dto.StoreLearningDesignResultsDTO; import org.lamsfoundation.lams.authoring.service.IAuthoringService; +import org.lamsfoundation.lams.learningdesign.dto.ValidationErrorDTO; +import org.lamsfoundation.lams.util.wddx.FlashMessage; import org.lamsfoundation.lams.web.servlet.AbstractStoreWDDXPacketServlet; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; @@ -41,6 +46,7 @@ */ public class StoreLDServlet extends AbstractStoreWDDXPacketServlet { + private static final long serialVersionUID = -2298959991408815691L; private static Logger log = Logger.getLogger(AuthoringAction.class); public IAuthoringService getAuthoringService(){ @@ -51,9 +57,31 @@ protected String process(String designDetails, HttpServletRequest request) throws Exception { - + String returnPacket = null; IAuthoringService authoringService = getAuthoringService(); - return authoringService.storeLearningDesignDetails(designDetails); + + try { + + Long learningDesignID = authoringService.storeLearningDesignDetails(designDetails); + Vector validationDTOS = authoringService.validateLearningDesign(learningDesignID); + FlashMessage flashMessage = null; + if ( validationDTOS != null &&validationDTOS.size()>0) { + flashMessage = new FlashMessage(getMessageKey(designDetails, request), + new StoreLearningDesignResultsDTO(Boolean.FALSE,validationDTOS, learningDesignID), FlashMessage.OBJECT_MESSAGE); + } else { + flashMessage = new FlashMessage(getMessageKey(designDetails, request), + new StoreLearningDesignResultsDTO(Boolean.TRUE, learningDesignID)); + } + returnPacket = flashMessage.serializeMessage(); + + } catch ( Exception e) { + log.error("Authoring error. input packet was "+designDetails,e); + FlashMessage flashMessage = new FlashMessage(getMessageKey(designDetails, request), + authoringService.getMessageService().getMessage("invalid.wddx.packet",new Object[]{e.getMessage()}), + FlashMessage.ERROR); + returnPacket = flashMessage.serializeMessage(); + } + return returnPacket; } protected String getMessageKey(String designDetails, HttpServletRequest request) {