diff --git a/src/main/java/org/olat/ims/qti21/QTI21Service.java b/src/main/java/org/olat/ims/qti21/QTI21Service.java
index 05d33efa148bd21b4419fbd9d3525423290c7261..b7de4fd41a358b4fe8ca04d2b9f999db3fd1e6f3 100644
--- a/src/main/java/org/olat/ims/qti21/QTI21Service.java
+++ b/src/main/java/org/olat/ims/qti21/QTI21Service.java
@@ -35,6 +35,7 @@ import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryEntryRef;
 
 import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
+import uk.ac.ed.ph.jqtiplus.node.AssessmentObject;
 import uk.ac.ed.ph.jqtiplus.node.result.AssessmentResult;
 import uk.ac.ed.ph.jqtiplus.notification.NotificationRecorder;
 import uk.ac.ed.ph.jqtiplus.reading.QtiXmlReader;
@@ -85,6 +86,8 @@ public interface QTI21Service {
 	
 	public boolean updateAssesmentObject(File resourceFile, ResolvedAssessmentObject<?> resolvedAssessmentObject);
 	
+	public boolean persistAssessmentObject(File resourceFile, AssessmentObject assessmentObject);
+	
 	
 	public UserTestSession createTestSession(Identity identity, AssessmentEntry assessmentEntry,
 			RepositoryEntry entry, String subIdent, RepositoryEntry testEntry,
diff --git a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
index 8bf959fddfafe0610a8b2a3c1a9529569e6ace32..b1f28fe33f573111ff0e93aa1700cfdd56b3768c 100644
--- a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
+++ b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
@@ -208,7 +208,8 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
 	            throw new OLATRuntimeException("Unexpected branch " + assessmentObjectType, null);
 	        }
 	        
-	        ResolvedAssessmentObject<?> cachedResult = assessmentTestsAndItemsCache.putIfAbsent(resourceDirectory, result);
+	        File resourceFile = new File(assessmentObjectSystemId);
+	        ResolvedAssessmentObject<?> cachedResult = assessmentTestsAndItemsCache.putIfAbsent(resourceFile, result);
 	        if(cachedResult != null) {
 	        	result = cachedResult;
 	        }
@@ -229,6 +230,11 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
 			return false;
 		}
 
+		return persistAssessmentObject(resourceFile, assessmentObject);
+	}
+
+	@Override
+	public boolean persistAssessmentObject(File resourceFile, AssessmentObject assessmentObject) {
 		try(FileOutputStream out = new FileOutputStream(resourceFile)) {
 			qtiSerializer().serializeJqtiObject(assessmentObject, out);
 			//TODO qti
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentBuilderHelper.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentBuilderHelper.java
index a385c2adef2b8862b0ebbf39363ceb89ef23ce70..7384c0fabfac275d2aecd665a973ea3f385de327 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentBuilderHelper.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentBuilderHelper.java
@@ -19,29 +19,13 @@
  */
 package org.olat.ims.qti21.model.xml;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.util.List;
+import org.xml.sax.SAXParseException;
 
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.stream.StreamResult;
-
-import org.olat.core.gui.render.StringOutput;
-import org.olat.core.logging.OLog;
-import org.olat.core.logging.Tracing;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.xml.sax.SAXException;
-
-import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
-import uk.ac.ed.ph.jqtiplus.exception.QtiModelException;
-import uk.ac.ed.ph.jqtiplus.node.LoadingContext;
-import uk.ac.ed.ph.jqtiplus.node.content.ItemBody;
-import uk.ac.ed.ph.jqtiplus.node.content.basic.Block;
-import uk.ac.ed.ph.jqtiplus.node.content.basic.FlowStatic;
-import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
+import uk.ac.ed.ph.jqtiplus.provision.BadResourceException;
+import uk.ac.ed.ph.jqtiplus.reading.QtiModelBuildingError;
+import uk.ac.ed.ph.jqtiplus.reading.QtiXmlInterpretationException;
+import uk.ac.ed.ph.jqtiplus.reading.QtiXmlInterpretationException.InterpretationFailureReason;
+import uk.ac.ed.ph.jqtiplus.xmlutils.XmlParseResult;
 
 /**
  * 
@@ -50,74 +34,54 @@ import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
  *
  */
 public class AssessmentBuilderHelper {
-	
-	private static final OLog log = Tracing.createLoggerFor(AssessmentBuilderHelper.class);
-	
-	private final QtiSerializer qtiSerializer;
-	
-	public AssessmentBuilderHelper() {
-		JqtiExtensionManager jqtiExtensionManager = new JqtiExtensionManager();
-		qtiSerializer = new QtiSerializer(jqtiExtensionManager);
-	}
-	
-	public AssessmentBuilderHelper(QtiSerializer qtiSerializer) {
-		this.qtiSerializer = qtiSerializer;
-	}
-	
-	public String toString(List<FlowStatic> statics) {
-		StringOutput sb = new StringOutput();
-		if(statics != null && statics.size() > 0) {
-			for(FlowStatic flowStatic:statics) {
-				qtiSerializer.serializeJqtiObject(flowStatic, new StreamResult(sb));
-			}
-		}
-		return sb.toString();
-	}
-	
-	public List<Block> parseHtml(String html) {
-		//tinymce bad habits
-		if(html.startsWith("<p>&nbsp;")) {
-			html = html.replace("<p>&nbsp;", "<p>");
-		}
-		Document document = htmlToDOM("<html>" + html + "</html>");
-		LoadingContext context = new HTMLLoadingContext();
-		
-		ItemBody helper = new ItemBody(null);
-		helper.load(document.getDocumentElement(), context);	
-		return helper.getBlocks();
-	}
-	
-	/**
-	 * This method use the standard XML parser. It's not really
-	 * good but QTIWorks want DOM Level 2 elements.
-	 * 
-	 * @param content
-	 * @return
-	 */
-	private Document htmlToDOM(String content) {
-		try {
-			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-			factory.setValidating(false);
-			factory.setNamespaceAware(true);
-			DocumentBuilder builder = factory.newDocumentBuilder();
-			Document doc = builder.parse(new ByteArrayInputStream(content.getBytes()));
-			return doc;
-		} catch (ParserConfigurationException | SAXException | IOException e) {
-			log.error("", e);
-			return null;
-		}
-	}
-	
-	private static final class HTMLLoadingContext implements LoadingContext {
 
-		@Override
-		public JqtiExtensionManager getJqtiExtensionManager() {
-			return null;
-		}
-
-		@Override
-		public void modelBuildingError(QtiModelException exception, Node badNode) {
-			//
-		}
+	public static void extractMessage(BadResourceException e, StringBuilder out) {
+		
+        if(e instanceof QtiXmlInterpretationException) {
+        	QtiXmlInterpretationException qe = (QtiXmlInterpretationException)e;
+        	if(qe.getQtiModelBuildingErrors() != null) {
+	        	for(QtiModelBuildingError error :qe.getQtiModelBuildingErrors()) {
+	        		String localName = error.getElementLocalName();
+	        		String msg = error.getException().getMessage();
+	        		int lineNumber = error.getElementLocation().getLineNumber();
+	        		out.append(lineNumber + " :: " + localName + " :: " + msg + "\n");
+	        	}
+        	}
+        	
+        	if(qe.getInterpretationFailureReason() != null) {
+        		InterpretationFailureReason reason = qe.getInterpretationFailureReason();
+        		out.append("Failure: " + reason + "\n");
+        	}
+        	
+        	if(qe.getXmlParseResult() != null) {
+        		XmlParseResult result = qe.getXmlParseResult();
+        		if(result.getWarnings() != null) {
+        			for(SAXParseException saxex : result.getWarnings()) {
+        				int lineNumber = saxex.getLineNumber();
+        				int columnNumber = saxex.getColumnNumber();
+        				String msg = saxex.getMessage();
+    	        		out.append("Error: " + lineNumber + ":" + columnNumber + " :: " + msg + "\n");
+        			}
+        		}
+        		
+        		if(result.getErrors() != null) {
+        			for(SAXParseException saxex : result.getErrors()) {
+        				int lineNumber = saxex.getLineNumber();
+        				int columnNumber = saxex.getColumnNumber();
+        				String msg = saxex.getMessage();
+    	        		out.append("Error: " + lineNumber + ":" + columnNumber + " :: " + msg + "\n");
+        			}
+        		}
+        		
+        		if(result.getFatalErrors() != null) {
+        			for(SAXParseException saxex : result.getFatalErrors()) {
+        				int lineNumber = saxex.getLineNumber();
+        				int columnNumber = saxex.getColumnNumber();
+        				String msg = saxex.getMessage();
+    	        		out.append("Fatal: " + lineNumber + ":" + columnNumber + " :: " + msg + "\n");
+        			}
+        		}
+        	}
+        }
 	}
 }
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentHtmlBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentHtmlBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..0022d6d93aa5fd1742da3923bea8fd677f42bfeb
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentHtmlBuilder.java
@@ -0,0 +1,127 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.model.xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.stream.StreamResult;
+
+import org.cyberneko.html.parsers.SAXParser;
+import org.olat.core.gui.render.StringOutput;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
+import uk.ac.ed.ph.jqtiplus.exception.QtiModelException;
+import uk.ac.ed.ph.jqtiplus.node.AbstractNode;
+import uk.ac.ed.ph.jqtiplus.node.LoadingContext;
+import uk.ac.ed.ph.jqtiplus.node.content.basic.FlowStatic;
+import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
+import uk.ac.ed.ph.jqtiplus.xmlutils.SimpleDomBuilderHandler;
+
+/**
+ * Do the ugly job to convert the Tiny MCE HTML code to the object model
+ * of QTI Works
+ * 
+ * Initial date: 10.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AssessmentHtmlBuilder {
+	
+	private static final OLog log = Tracing.createLoggerFor(AssessmentHtmlBuilder.class);
+	
+	private final QtiSerializer qtiSerializer;
+	
+	public AssessmentHtmlBuilder() {
+		JqtiExtensionManager jqtiExtensionManager = new JqtiExtensionManager();
+		qtiSerializer = new QtiSerializer(jqtiExtensionManager);
+	}
+	
+	public AssessmentHtmlBuilder(QtiSerializer qtiSerializer) {
+		this.qtiSerializer = qtiSerializer;
+	}
+	
+	public String toString(List<FlowStatic> statics) {
+		StringOutput sb = new StringOutput();
+		if(statics != null && statics.size() > 0) {
+			for(FlowStatic flowStatic:statics) {
+				qtiSerializer.serializeJqtiObject(flowStatic, new StreamResult(sb));
+			}
+		}
+		return sb.toString();
+	}
+	
+	public void appendHtml(AbstractNode parent, String htmlFragment) {
+		//tinymce bad habits
+		if(htmlFragment.startsWith("<p>&nbsp;")) {
+			htmlFragment = htmlFragment.replace("<p>&nbsp;", "<p>");
+		}
+		//wrap around <html> to have a root element
+		Document document = filter("<html>" + htmlFragment + "</html>");
+		parent.getNodeGroups().load(document.getDocumentElement(), new HTMLLoadingContext());
+	}
+
+	private Document filter(String content) {
+		try {
+			SAXParser parser = new SAXParser();
+			parser.setProperty("http://cyberneko.org/html/properties/names/elems", "lower");
+			parser.setFeature("http://cyberneko.org/html/features/balance-tags/document-fragment", true);
+			
+			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+			DocumentBuilder builder = factory.newDocumentBuilder();
+			Document document = builder.newDocument();
+			SimpleDomBuilderHandler contentHandler = new SimpleDomBuilderHandler(document);
+			parser.setContentHandler(contentHandler);
+			parser.parse(new InputSource(new ByteArrayInputStream(content.getBytes())));
+			return document;
+		} catch (SAXException e) {
+			log.error("", e);
+			return null;
+		} catch (IOException e) {
+			log.error("", e);
+			return null;
+		} catch (Exception e) {
+			log.error("", e);
+			return null;
+		}
+	}
+	
+	private static final class HTMLLoadingContext implements LoadingContext {
+
+		@Override
+		public JqtiExtensionManager getJqtiExtensionManager() {
+			return null;
+		}
+
+		@Override
+		public void modelBuildingError(QtiModelException exception, Node badNode) {
+			//
+		}
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilder.java
index c206f27fd8028e174b9bd45957fe5a1abd245ce7..47e2fd8c832179ed6dca16e3330318122d2a83d2 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilder.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilder.java
@@ -50,6 +50,7 @@ public abstract class AssessmentItemBuilder {
 
 	protected final AssessmentItem assessmentItem;
 	protected final QtiSerializer qtiSerializer;
+	protected final AssessmentHtmlBuilder htmlHelper;
 	protected final AssessmentBuilderHelper builderHelper;
 	
 	private ScoreBuilder minScoreBuilder;
@@ -62,10 +63,15 @@ public abstract class AssessmentItemBuilder {
 	public AssessmentItemBuilder(AssessmentItem assessmentItem, QtiSerializer qtiSerializer) {
 		this.assessmentItem = assessmentItem;
 		this.qtiSerializer = qtiSerializer;
-		builderHelper = new AssessmentBuilderHelper(qtiSerializer);
+		builderHelper = new AssessmentBuilderHelper();
+		htmlHelper = new AssessmentHtmlBuilder(qtiSerializer);
 		extract();
 	}
 	
+	public AssessmentItem getAssessmentItem() {
+		return assessmentItem;
+	}
+	
 	protected void extract() {
 		extractMinScore();
 		extractMaxScore();
@@ -164,6 +170,10 @@ public abstract class AssessmentItemBuilder {
 	public AssessmentBuilderHelper getHelper() {
 		return builderHelper;
 	}
+	
+	public AssessmentHtmlBuilder getHtmlHelper() {
+		return htmlHelper;
+	}
 
 	public final void build() {
 		List<OutcomeDeclaration> outcomeDeclarations = assessmentItem.getOutcomeDeclarations();
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java
index 8d3113436dd23b8204a5c88bdab40181a8196975..28d89805ddc3bd86858bccca3f3a94c0ed95c18f 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java
@@ -39,8 +39,6 @@ import uk.ac.ed.ph.jqtiplus.group.item.response.processing.ResponseProcessingGro
 import uk.ac.ed.ph.jqtiplus.group.outcome.declaration.OutcomeDeclarationGroup;
 import uk.ac.ed.ph.jqtiplus.node.QtiNode;
 import uk.ac.ed.ph.jqtiplus.node.content.ItemBody;
-import uk.ac.ed.ph.jqtiplus.node.content.basic.Block;
-import uk.ac.ed.ph.jqtiplus.node.content.basic.FlowStatic;
 import uk.ac.ed.ph.jqtiplus.node.content.basic.TextRun;
 import uk.ac.ed.ph.jqtiplus.node.content.xhtml.text.P;
 import uk.ac.ed.ph.jqtiplus.node.expression.general.BaseValue;
@@ -363,12 +361,14 @@ public class AssessmentItemFactory {
 		modalFeedback.setVisibilityMode(VisibilityMode.parseVisibilityMode("show"));
 		modalFeedback.getAttributes().getStringAttribute(ModalFeedback.ATTR_TITLE_NAME).setValue(title);
 		
-		List<Block> blocks = new AssessmentBuilderHelper().parseHtml(text);
+		new AssessmentHtmlBuilder().appendHtml(modalFeedback, text);
+		
+		/*List<Block> blocks = new AssessmentHTMLBuilder().parseHtml(text);
 		for(Block block:blocks) {
 			if(block instanceof FlowStatic) {
 				modalFeedback.getFlowStatics().add((FlowStatic)block);
 			}
-		}
+		}*/
 
 		return modalFeedback;
 	}
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestPackage.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestPackage.java
deleted file mode 100644
index 6f9b7cfedc4074318c379b0d11b4c1b75abfefdc..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestPackage.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * <a href="http://www.openolat.org">
- * OpenOLAT - Online Learning and Training</a><br>
- * <p>
- * Licensed under the Apache License, Version 2.0 (the "License"); <br>
- * you may not use this file except in compliance with the License.<br>
- * You may obtain a copy of the License at the
- * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
- * <p>
- * Unless required by applicable law or agreed to in writing,<br>
- * software distributed under the License is distributed on an "AS IS" BASIS, <br>
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
- * See the License for the specific language governing permissions and <br>
- * limitations under the License.
- * <p>
- * Initial code contributed and copyrighted by<br>
- * frentix GmbH, http://www.frentix.com
- * <p>
- */
-package org.olat.ims.qti21.model.xml;
-
-/**
- * 
- * Initial date: 04.06.2015<br>
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class AssessmentTestPackage {
-
-}
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/ManifestPackage.java b/src/main/java/org/olat/ims/qti21/model/xml/ManifestPackage.java
index 1d7578708c6b02176e222f7fca08c3760038a847..9105243de03c459eec6db5c4347b10b07b13e4dc 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/ManifestPackage.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/ManifestPackage.java
@@ -19,12 +19,17 @@
  */
 package org.olat.ims.qti21.model.xml;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.OutputStream;
 import java.util.UUID;
 
 import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
 
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
@@ -132,4 +137,31 @@ public class ManifestPackage {
 			log.error("", e);
 		}
 	}
+	
+	public static void write(ManifestType manifest, File manifestFile) {
+        try(OutputStream out = new FileOutputStream(manifestFile)) {
+			JAXBContext context = JAXBContext.newInstance("org.olat.imscp.xml.manifest");
+			Marshaller marshaller = context.createMarshaller();
+			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+			marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.imsglobal.org/xsd/imscp_v1p1 http://www.imsglobal.org/xsd/qti/qtiv2p1/qtiv2p1_imscpv1p2_v1p0.xsd");
+
+			marshaller.marshal(objectFactory.createManifest(manifest), out);
+		} catch (JAXBException | IOException e) {
+			log.error("", e);
+		}
+	}
+	
+	public static ManifestType read(File manifestFile) {
+        try {
+			JAXBContext context = JAXBContext.newInstance("org.olat.imscp.xml.manifest");
+			Unmarshaller marshaller = context.createUnmarshaller();
+			//marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+			//marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.imsglobal.org/xsd/imscp_v1p1 http://www.imsglobal.org/xsd/qti/qtiv2p1/qtiv2p1_imscpv1p2_v1p0.xsd");
+
+			return (ManifestType)((JAXBElement<?>)marshaller.unmarshal(manifestFile)).getValue();
+		} catch (JAXBException e) {
+			log.error("", e);
+			return null;
+		}
+	}
 }
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/ModalFeedbackBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/ModalFeedbackBuilder.java
index 7c5eb63f61203eb1b93b6c303428a49bec066b0b..8a5189d86972ac3ba7057fa84d9484a2a6271422 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/ModalFeedbackBuilder.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/ModalFeedbackBuilder.java
@@ -55,7 +55,7 @@ public class ModalFeedbackBuilder {
 		this.assessmentItem = assessmentItem;
 		this.modalFeedback = modalFeedback;
 		if(modalFeedback != null) {
-			text = new AssessmentBuilderHelper().toString(modalFeedback.getFlowStatics());
+			text = new AssessmentHtmlBuilder().toString(modalFeedback.getFlowStatics());
 			StringAttribute titleAttr = modalFeedback.getAttributes().getStringAttribute(ModalFeedback.ATTR_TITLE_NAME);
 			title = titleAttr == null ? null : titleAttr.getComputedValue();
 			identifier = modalFeedback.getIdentifier();
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/SingleChoiceAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/SingleChoiceAssessmentItemBuilder.java
index 57381148a5a06607c095ed6b2b6a699e29c9ecad..6fa954af0670dc4a2ef79826427af492c0bc2bd1 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/SingleChoiceAssessmentItemBuilder.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/SingleChoiceAssessmentItemBuilder.java
@@ -103,9 +103,12 @@ public class SingleChoiceAssessmentItemBuilder extends ChoiceAssessmentItemBuild
 		List<Block> blocks = assessmentItem.getItemBody().getBlocks();
 		blocks.clear();
 
+		/*
 		//add question
-		List<Block> questionBlocks = getHelper().parseHtml(question);
+		List<Block> questionBlocks = getHtmlHelper().parseHtml(question);
 		blocks.addAll(questionBlocks);
+		*/
+		getHtmlHelper().appendHtml(assessmentItem.getItemBody(), question);
 		
 		//add interaction
 		ChoiceInteraction singleChoiceInteraction = AssessmentItemFactory
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/EditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemBuilderController.java
similarity index 93%
rename from src/main/java/org/olat/ims/qti21/ui/editor/EditorController.java
rename to src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemBuilderController.java
index 0f761f189ecff1cc130a6207dc98d9c37126d2ca..c9e32921016ad575d905730928fe17b6f422bf45 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/EditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemBuilderController.java
@@ -22,7 +22,7 @@ package org.olat.ims.qti21.ui.editor;
 import org.olat.core.gui.control.Controller;
 import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
 
-public interface EditorController extends Controller {
+public interface AssessmentItemBuilderController extends Controller {
 	
 	public void updateFromBuilder();
 	
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
index 1104d95de6ea174d29e02565cccb92913ce42488..d675c9895bf8b2051142e0e40d3f52c17a7db24a 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
@@ -36,6 +36,7 @@ import org.olat.ims.qti21.QTI21Service;
 import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
 import org.olat.ims.qti21.model.xml.SingleChoiceAssessmentItemBuilder;
 import org.olat.ims.qti21.ui.AssessmentItemDisplayController;
+import org.olat.ims.qti21.ui.editor.events.AssessmentItemEvent;
 import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.modules.assessment.AssessmentService;
 import org.olat.repository.RepositoryEntry;
@@ -55,12 +56,13 @@ import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem;
  */
 public class AssessmentItemEditorController extends BasicController {
 
+	private final AssessmentItemRef itemRef;
 	private final ResolvedAssessmentItem resolvedAssessmentItem;
 	
 	private final TabbedPane tabbedPane;
 	private final VelocityContainer mainVC;
 	
-	private EditorController itemEditor, scoreEditor, feedbackEditor;
+	private AssessmentItemBuilderController itemEditor, scoreEditor, feedbackEditor;
 	private AssessmentItemDisplayController displayCtrl;
 	
 	@Autowired
@@ -71,6 +73,7 @@ public class AssessmentItemEditorController extends BasicController {
 	public AssessmentItemEditorController(UserRequest ureq, WindowControl wControl, RepositoryEntry testEntry,
 			ResolvedAssessmentItem resolvedAssessmentItem, AssessmentItemRef itemRef, File unzippedDirectory) {
 		super(ureq, wControl);
+		this.itemRef = itemRef;
 		this.resolvedAssessmentItem = resolvedAssessmentItem;
 		
 		mainVC = createVelocityContainer("assessment_item_editor");
@@ -89,6 +92,10 @@ public class AssessmentItemEditorController extends BasicController {
 		putInitialPanel(mainVC);
 	}
 	
+	public String getTitle() {
+		return resolvedAssessmentItem.getRootNodeLookup().getRootNodeHolder().getRootNode().getTitle();
+	}
+	
 	@Override
 	protected void doDispose() {
 		//
@@ -151,15 +158,19 @@ public class AssessmentItemEditorController extends BasicController {
 	@Override
 	protected void event(UserRequest ureq, Controller source, Event event) {
 		if(event instanceof AssessmentItemEvent) {
-			if(event == AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED) {
-				if(source instanceof EditorController) {
-					EditorController editorCtrl = (EditorController)source;
-					AssessmentItemBuilder builder = editorCtrl.getBuilder();
-					if(builder != null) {
-						builder.build();
+			if(event instanceof AssessmentItemEvent) {
+				AssessmentItemEvent aie = (AssessmentItemEvent)event;
+				if(AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED.equals(aie.getCommand())) {
+					if(source instanceof AssessmentItemBuilderController) {
+						AssessmentItemBuilderController editorCtrl = (AssessmentItemBuilderController)source;
+						AssessmentItemBuilder builder = editorCtrl.getBuilder();
+						if(builder != null) {
+							builder.build();
+						}
 					}
+					doSaveAssessmentItem();
+					fireEvent(ureq, new AssessmentItemEvent(aie.getCommand(), aie.getAssessmentItem(), itemRef));
 				}
-				doSaveAssessmentItem();
 			}
 		}
 		super.event(ureq, source, event);
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentSectionEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentSectionEditorController.java
index 9ed6831e2e1a76801b57b201a3c9eae55cb57315..97a75ffb1dfaed0553c7fd590b249946c6844b3a 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentSectionEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentSectionEditorController.java
@@ -26,6 +26,7 @@ import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.util.StringHelper;
+import org.olat.ims.qti21.ui.editor.events.AssessmentSectionEvent;
 
 import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection;
 
@@ -62,6 +63,10 @@ public class AssessmentSectionEditorController extends ItemSessionControlControl
 		formLayout.add(buttonsCont);
 		uifactory.addFormSubmitButton("save", "save", buttonsCont);
 	}
+	
+	public String getTitle() {
+		return titleEl.getValue();
+	}
 
 	@Override
 	protected void doDispose() {
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java
index 464539964f351e3429a7a93d90862a539a5cfd5d..326d8a5fe7b2ca38184a0cbada4e8ea1b3c59596 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java
@@ -21,10 +21,13 @@ package org.olat.ims.qti21.ui.editor;
 
 import java.io.File;
 import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.UUID;
 
 import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.dropdown.Dropdown;
 import org.olat.core.gui.components.link.Link;
 import org.olat.core.gui.components.link.LinkFactory;
 import org.olat.core.gui.components.panel.Panel;
@@ -43,17 +46,26 @@ import org.olat.core.gui.control.controller.MainLayoutBasicController;
 import org.olat.core.util.Util;
 import org.olat.fileresource.FileResourceManager;
 import org.olat.ims.qti21.QTI21Service;
+import org.olat.ims.qti21.model.xml.AssessmentItemFactory;
+import org.olat.ims.qti21.model.xml.ManifestPackage;
 import org.olat.ims.qti21.ui.AssessmentTestDisplayController;
+import org.olat.ims.qti21.ui.editor.events.AssessmentItemEvent;
+import org.olat.ims.qti21.ui.editor.events.AssessmentSectionEvent;
+import org.olat.ims.qti21.ui.editor.events.AssessmentTestEvent;
+import org.olat.ims.qti21.ui.editor.events.AssessmentTestPartEvent;
+import org.olat.imscp.xml.manifest.ManifestType;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.ui.RepositoryEntryRuntimeController.ToolbarAware;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
 import uk.ac.ed.ph.jqtiplus.node.test.AssessmentItemRef;
 import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection;
 import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest;
 import uk.ac.ed.ph.jqtiplus.node.test.TestPart;
 import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem;
 import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentTest;
+import uk.ac.ed.ph.jqtiplus.types.Identifier;
 
 /**
  * Assessment test editor and composer.
@@ -68,6 +80,8 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 	
 	private final MenuTree menuTree;
 	private final Link saveLink;
+	private final Dropdown addItemTools;
+	private final Link newSectionLink, newSingleChoiceLink;
 	private final TooledStackedPanel toolbar;
 	private final VelocityContainer mainVC;
 
@@ -76,7 +90,8 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 	
 	private final File unzippedDirRoot;
 	private final RepositoryEntry testEntry;
-	private final ResolvedAssessmentTest resolvedAssessmentTest;
+	private ManifestType manifest;
+	private ResolvedAssessmentTest resolvedAssessmentTest;
 	
 	private final boolean restrictedEdit;
 	
@@ -107,10 +122,25 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 		resolvedAssessmentTest = qtiService.loadAndResolveAssessmentObject(unzippedDirRoot);
 		menuTree.setTreeModel(new AssessmentTestEditorAndComposerTreeModel(resolvedAssessmentTest));
 		
+		manifest = ManifestPackage.read(new File(unzippedDirRoot, "imsmanifest.xml"));
+		
 		//default buttons
 		saveLink = LinkFactory.createToolLink("serialize", translate("serialize"), this, "o_icon_save");
 		saveLink.setDomReplacementWrapperRequired(false);
 		
+		//add elements
+		addItemTools = new Dropdown("editTools", "new.elements", false, getTranslator());
+		addItemTools.setIconCSS("o_icon o_icon-fw o_icon_add");
+		toolbar.addTool(addItemTools, Align.left);
+		
+		newSectionLink = LinkFactory.createToolLink("new.section", translate("new.section"), this, "o_mi_qtisection");
+		newSectionLink.setDomReplacementWrapperRequired(false);
+		addItemTools.addComponent(newSectionLink);
+		
+		newSingleChoiceLink = LinkFactory.createToolLink("new.sc", translate("new.sc"), this, "o_mi_qtisc");
+		newSingleChoiceLink.setDomReplacementWrapperRequired(false);
+		addItemTools.addComponent(newSingleChoiceLink);
+
 		// main layout
 		mainVC = createVelocityContainer("assessment_test_composer");
 		columnLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), menuTree, mainVC, "at" + testEntry.getKey());			
@@ -119,7 +149,11 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 		putInitialPanel(columnLayoutCtr.getInitialComponent());
 		
 		// init
-		partEditorFactory(ureq, menuTree.getTreeModel().getRootNode());
+		TreeNode selectedNode = doOpenFirstItem();
+		if(selectedNode == null) {
+			selectedNode = menuTree.getTreeModel().getRootNode();
+		}
+		partEditorFactory(ureq, selectedNode);
 	}
 	
 	@Override
@@ -129,6 +163,7 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 	
 	@Override
 	public void initToolbar() {
+		toolbar.addTool(addItemTools, Align.left);
 		toolbar.addTool(saveLink, Align.left);
 	}
 
@@ -144,11 +179,21 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 			if(ate == AssessmentTestEvent.ASSESSMENT_TEST_CHANGED_EVENT) {
 				doSaveAssessmentTest();
 			}
+		} else if(event instanceof AssessmentTestPartEvent) {
+			AssessmentTestPartEvent atpe = (AssessmentTestPartEvent)event;
+			if(atpe == AssessmentTestPartEvent.ASSESSMENT_TEST_PART_CHANGED_EVENT) {
+				doSaveAssessmentTest();
+			}
 		} else if(event instanceof AssessmentSectionEvent) {
 			AssessmentSectionEvent ase = (AssessmentSectionEvent)event;
 			if(AssessmentSectionEvent.ASSESSMENT_SECTION_CHANGED.equals(ase.getCommand())) {
 				doSaveAssessmentTest();
-				doUpdate(ase.getSection());
+				doUpdate(ase.getSection().getIdentifier(), ase.getSection().getTitle());
+			}
+		} else if(event instanceof AssessmentItemEvent) {
+			AssessmentItemEvent aie = (AssessmentItemEvent)event;
+			if(AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED.equals(aie.getCommand())) {
+				doUpdate(aie.getAssessmentItemRef().getIdentifier(), aie.getAssessmentItem().getTitle());
 			}
 		}
 		super.event(ureq, source, event);
@@ -168,7 +213,90 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 			}
 		} else if(saveLink == source) {
 			doSaveAssessmentTest();
+		} else if(newSectionLink == source) {
+			
+		} else if(newSingleChoiceLink == source) {
+			doNewSingleChoice(ureq, menuTree.getSelectedNode());
+		}
+	}
+	
+	private TreeNode doOpenFirstItem() {
+		TreeNode node = menuTree.getTreeModel().getRootNode();
+		if(node.getChildCount() > 0) {
+			return doOpenFirstItem((TreeNode)node.getChildAt(0));
+		}
+		return node;
+	}
+	
+	private TreeNode doOpenFirstItem(TreeNode node) {
+		if(node.getUserObject() instanceof AssessmentItemRef) {
+			menuTree.setSelectedNode(node);
+			menuTree.open(node);
+			return node;
+		}
+		if(node.getChildCount() > 0) {
+			return doOpenFirstItem((TreeNode)node.getChildAt(0));
 		}
+		return null;
+	}
+	
+	private void doNewSection(UserRequest ureq) {
+		
+	}
+	
+	/**
+	 * The method create a simple single choice, save the assessment item
+	 * and append it to the test, update the manifest file.
+	 * 
+	 * @param ureq
+	 * @param selectedNode
+	 */
+	private void doNewSingleChoice(UserRequest ureq, TreeNode selectedNode) {
+		try {
+			TreeNode sectionNode = getNearestSection(selectedNode);
+			AssessmentSection section = (AssessmentSection)sectionNode.getUserObject();
+			
+			AssessmentItemRef itemRef = new AssessmentItemRef(section);
+			String itemId = "sc" + UUID.randomUUID();
+			itemRef.setIdentifier(Identifier.parseString(itemId));
+			File itemFile = new File(unzippedDirRoot, itemId + ".xml");
+			itemRef.setHref(new URI(itemFile.getName()));
+			section.getSectionParts().add(itemRef);
+			
+			AssessmentItem assessmentItem = AssessmentItemFactory.createSingleChoice();
+			qtiService.persistAssessmentObject(itemFile, assessmentItem);
+			
+			URI testUri = resolvedAssessmentTest.getTestLookup().getSystemId();
+			File testFile = new File(testUri);
+			qtiService.updateAssesmentObject(testFile, resolvedAssessmentTest);
+
+			ManifestPackage.appendAssessmentItem(itemFile.getName(), manifest);
+			ManifestPackage.write(manifest, new File(unzippedDirRoot, "imsmanifest.xml"));
+			
+			resolvedAssessmentTest = qtiService.loadAndResolveAssessmentObject(unzippedDirRoot);
+			menuTree.setTreeModel(new AssessmentTestEditorAndComposerTreeModel(resolvedAssessmentTest));
+			
+			TreeNode newItemNode = menuTree.getTreeModel().getNodeById(itemId);
+			menuTree.setSelectedNode(newItemNode);
+			menuTree.open(newItemNode);
+
+			partEditorFactory(ureq, newItemNode);
+		} catch (URISyntaxException e) {
+			e.printStackTrace();
+		}
+	}
+	
+	private TreeNode getNearestSection(TreeNode node) {
+		if(node.getUserObject() instanceof AssessmentSection) {
+			return node;
+		}
+		if(node.getUserObject() instanceof AssessmentItemRef) {
+			return (TreeNode)node.getParent();
+		}
+		if(node.getUserObject() instanceof TestPart) {
+			return (TreeNode)node.getChildAt(0);
+		}
+		return null;
 	}
 	
 	private void doSaveAssessmentTest() {
@@ -177,13 +305,13 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 		qtiService.updateAssesmentObject(testFile, resolvedAssessmentTest);
 	}
 	
-	private void doUpdate(AssessmentSection section) {
+	private void doUpdate(Identifier identifier, String newTitle) {
 		TreeNode node = menuTree.getTreeModel()
-				.getNodeById(section.getIdentifier().toString());
+				.getNodeById(identifier.toString());
 		if(node instanceof GenericTreeNode) {
-			GenericTreeNode sectionNode = (GenericTreeNode)node;
-			if(!section.getTitle().equals(sectionNode.getTitle())) {
-				sectionNode.setTitle(section.getTitle());
+			GenericTreeNode itemNode = (GenericTreeNode)node;
+			if(!newTitle.equals(itemNode.getTitle())) {
+				itemNode.setTitle(newTitle);
 				menuTree.setDirty(true);
 			}
 		}
@@ -212,7 +340,13 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 		} else if(uobject instanceof AssessmentItemRef) {
 			AssessmentItemRef itemRef = (AssessmentItemRef)uobject;
 			ResolvedAssessmentItem item = resolvedAssessmentTest.getResolvedAssessmentItem(itemRef);
-			currentEditorCtrl = new AssessmentItemEditorController(ureq, getWindowControl(), testEntry, item, itemRef, unzippedDirRoot);
+			if(item.getItemLookup().getBadResourceException() != null) {
+				currentEditorCtrl = new BadResourceController(ureq, getWindowControl(),
+						item.getItemLookup().getBadResourceException(), unzippedDirRoot, itemRef.getHref());
+			} else {
+				currentEditorCtrl = new AssessmentItemEditorController(ureq, getWindowControl(), testEntry,
+						item, itemRef, unzippedDirRoot);
+			}
 		}
 		
 		if(currentEditorCtrl != null) {
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorAndComposerTreeModel.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorAndComposerTreeModel.java
index 26ffff0d3d6e0ad4bf3cfc70bfe3ef0e31456ab5..89ac6a33b81df94532414f41652c9fa3ae7f9e1a 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorAndComposerTreeModel.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorAndComposerTreeModel.java
@@ -32,6 +32,7 @@ import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection;
 import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest;
 import uk.ac.ed.ph.jqtiplus.node.test.SectionPart;
 import uk.ac.ed.ph.jqtiplus.node.test.TestPart;
+import uk.ac.ed.ph.jqtiplus.provision.BadResourceException;
 import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem;
 import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentTest;
 
@@ -65,6 +66,10 @@ public class AssessmentTestEditorAndComposerTreeModel extends GenericTreeModel i
 		}
 	}
 	
+	public TreeNode addItem(AssessmentItemRef itemRef, TreeNode section) {
+		return buildRecursively(itemRef, section);
+	}
+	
 	private void buildRecursively(TestPart part, int pos, TreeNode parentNode) {
 		GenericTreeNode partNode = new GenericTreeNode(part.getIdentifier().toString());
 		partNode.setTitle(pos + ". Test part");
@@ -94,17 +99,26 @@ public class AssessmentTestEditorAndComposerTreeModel extends GenericTreeModel i
 		}
 	}
 	
-
-	private void buildRecursively(AssessmentItemRef itemRef, TreeNode parentNode) {
-		GenericTreeNode sectionNode = new GenericTreeNode(itemRef.getIdentifier().toString());
+	private TreeNode buildRecursively(AssessmentItemRef itemRef, TreeNode parentNode) {
+		GenericTreeNode itemNode = new GenericTreeNode(itemRef.getIdentifier().toString());
 		
 		ResolvedAssessmentItem resolvedAssessmentItem = resolvedAssessmentTest.getResolvedAssessmentItem(itemRef);
-		AssessmentItem assessmentItem = resolvedAssessmentItem.getItemLookup().getRootNodeHolder().getRootNode();
-		sectionNode.setTitle(assessmentItem.getTitle());
-		sectionNode.setIconCssClass("o_icon o_mi_qtisc");
-		sectionNode.setUserObject(itemRef);
-		parentNode.addChild(sectionNode);
+		BadResourceException ex = resolvedAssessmentItem.getItemLookup().getBadResourceException();
+		if(ex != null) {
+			itemNode.setTitle("ERROR");
+			itemNode.setIconCssClass("o_icon o_icon_error");
+			itemNode.setUserObject(itemRef);
+			parentNode.addChild(itemNode);
+		} else {
+			AssessmentItem assessmentItem = resolvedAssessmentItem.getItemLookup().getRootNodeHolder().getRootNode();
+			itemNode.setTitle(assessmentItem.getTitle());
+			itemNode.setIconCssClass("o_icon o_mi_qtisc");
+			itemNode.setUserObject(itemRef);
+			parentNode.addChild(itemNode);
+		}
+		
 		
+		return itemNode;
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java
index a370c0ece4416364f9a0fef9da0ca36a5eaef94d..ef5c165f4d46fb646e552edef31eafe69f90362c 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java
@@ -29,6 +29,7 @@ import org.olat.core.gui.control.WindowControl;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
 import org.olat.ims.qti21.ui.AssessmentTestDisplayController;
+import org.olat.ims.qti21.ui.editor.events.AssessmentTestEvent;
 
 import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest;
 
@@ -70,6 +71,9 @@ public class AssessmentTestEditorController extends FormBasicController {
 		//
 	}
 
+	public String getTitle() {
+		return titleEl.getValue();
+	}
 
 	@Override
 	protected boolean validateFormLogic(UserRequest ureq) {
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestPartEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestPartEditorController.java
index 9756ae9aec0cda9bdb0ae61e1bdad7c8bd033fc5..521f20f0ffadb6ef44bcdf572548182a697b0bb5 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestPartEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestPartEditorController.java
@@ -24,6 +24,7 @@ import org.olat.core.gui.components.form.flexible.FormItemContainer;
 import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.ims.qti21.ui.editor.events.AssessmentTestEvent;
 
 import uk.ac.ed.ph.jqtiplus.node.test.TestPart;
 
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/BadResourceController.java b/src/main/java/org/olat/ims/qti21/ui/editor/BadResourceController.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2a402aa85d72a35abed502d35c80cb4b9b6ad03
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/BadResourceController.java
@@ -0,0 +1,78 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.ui.editor;
+
+import java.io.File;
+import java.net.URI;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.ims.qti21.model.xml.AssessmentBuilderHelper;
+
+import uk.ac.ed.ph.jqtiplus.provision.BadResourceException;
+
+/**
+ * 
+ * Initial date: 10.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class BadResourceController extends FormBasicController {
+	
+	private final URI resourceURI;
+	private final File unzippedDirectory;
+	private final BadResourceException resourceException;
+	
+	public BadResourceController(UserRequest ureq, WindowControl wControl,
+			BadResourceException resourceException, File unzippedDirectory, URI resourceURI) {
+		super(ureq, wControl, "bad_resource");
+		this.resourceException = resourceException;
+		this.resourceURI = resourceURI;
+		this.unzippedDirectory = unzippedDirectory;
+		initForm(ureq);
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		if(formLayout instanceof FormLayoutContainer) {
+			FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout;
+
+			StringBuilder out = new StringBuilder();
+			AssessmentBuilderHelper.extractMessage(resourceException, out);
+			layoutCont.contextPut("message", out.toString());
+			layoutCont.contextPut("directory", unzippedDirectory.toString());
+			layoutCont.contextPut("uri", resourceURI.toASCIIString());
+		}
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/FeedbackEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/FeedbackEditorController.java
index 5673318c3af825627b6c3499914d90a5c7d069fb..41b213737a9e8feac9c16200c454560843e74e99 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/FeedbackEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/FeedbackEditorController.java
@@ -33,6 +33,7 @@ import org.olat.core.util.filter.FilterFactory;
 import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
 import org.olat.ims.qti21.model.xml.ModalFeedbackBuilder;
 import org.olat.ims.qti21.model.xml.SingleChoiceAssessmentItemBuilder;
+import org.olat.ims.qti21.ui.editor.events.AssessmentItemEvent;
 
 /**
  * 
@@ -40,7 +41,7 @@ import org.olat.ims.qti21.model.xml.SingleChoiceAssessmentItemBuilder;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class FeedbackEditorController extends FormBasicController implements EditorController {
+public class FeedbackEditorController extends FormBasicController implements AssessmentItemBuilderController {
 	
 	private TextElement feedbackCorrectTitleEl, feedbackIncorrectTitleEl;
 	private RichTextElement feedbackCorrectTextEl, feedbackIncorrectTextEl;
@@ -115,7 +116,7 @@ public class FeedbackEditorController extends FormBasicController implements Edi
 			incorrectBuilder.setText(incorrectText);
 		}
 
-		fireEvent(ureq, AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED);
+		fireEvent(ureq, new AssessmentItemEvent(AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED, itemBuilder.getAssessmentItem()));
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java
index c35cdadc940afc84f83ed0a129fc0948cc190ecf..605da726b83a8f732717427909ac1a2b86c33721 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java
@@ -41,9 +41,8 @@ import org.olat.ims.qti21.model.IdentifierGenerator;
 import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
 import org.olat.ims.qti21.model.xml.AssessmentItemFactory;
 import org.olat.ims.qti21.model.xml.SingleChoiceAssessmentItemBuilder;
+import org.olat.ims.qti21.ui.editor.events.AssessmentItemEvent;
 
-import uk.ac.ed.ph.jqtiplus.node.content.basic.Block;
-import uk.ac.ed.ph.jqtiplus.node.content.basic.FlowStatic;
 import uk.ac.ed.ph.jqtiplus.node.content.xhtml.text.P;
 import uk.ac.ed.ph.jqtiplus.node.item.interaction.ChoiceInteraction;
 import uk.ac.ed.ph.jqtiplus.node.item.interaction.choice.SimpleChoice;
@@ -55,7 +54,7 @@ import uk.ac.ed.ph.jqtiplus.types.Identifier;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class SingleChoiceEditorController extends FormBasicController implements EditorController {
+public class SingleChoiceEditorController extends FormBasicController implements AssessmentItemBuilderController {
 
 	private TextElement titleEl;
 	private RichTextElement textEl;
@@ -141,7 +140,7 @@ public class SingleChoiceEditorController extends FormBasicController implements
 	}
 
 	private void wrapAnswer(UserRequest ureq, SimpleChoice choice) {
-		String choiceContent =  itemBuilder.getHelper().toString(choice.getFlowStatics());
+		String choiceContent =  itemBuilder.getHtmlHelper().toString(choice.getFlowStatics());
 		String choiceId = "answer" + count++;
 		RichTextElement choiceEl = uifactory.addRichTextElementForStringData(choiceId, "form.imd.answer", choiceContent, 8, -1, true, null, null,
 				answersCont, ureq.getUserSession(), getWindowControl());
@@ -213,19 +212,22 @@ public class SingleChoiceEditorController extends FormBasicController implements
 			choiceWrapper.setCorrect(correctAnswerIdentifier.equals(choiceWrapper.getIdentifier()));
 			//text
 			String answer = choiceWrapper.getAnswer().getValue();
-			List<Block> blocks = itemBuilder.getHelper().parseHtml(answer);
+			itemBuilder.getHtmlHelper().appendHtml(choice, answer);
+			
+			
+			/*List<Block> blocks = itemBuilder.getHtmlHelper().parseHtml(answer);
 			choice.getFlowStatics().clear();
 			for(Block block:blocks) {
 				if(block instanceof FlowStatic) {
 					choice.getFlowStatics().add((FlowStatic)block);
 				}
-			}
+			}*/
 			
 			choiceList.add(choice);
 		}
 		itemBuilder.setSimpleChoices(choiceList);
 
-		fireEvent(ureq, AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED);
+		fireEvent(ureq, new AssessmentItemEvent(AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED, itemBuilder.getAssessmentItem()));
 	}
 	
 	@Override
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceScoreController.java b/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceScoreController.java
index 9c47e8a1582dc7000758476f8e34fdb4891f558e..59030be5b4f4cf3c85631dedbbcdb57cfe57bee5 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceScoreController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceScoreController.java
@@ -29,6 +29,7 @@ import org.olat.core.gui.control.WindowControl;
 import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
 import org.olat.ims.qti21.model.xml.ScoreBuilder;
 import org.olat.ims.qti21.model.xml.SingleChoiceAssessmentItemBuilder;
+import org.olat.ims.qti21.ui.editor.events.AssessmentItemEvent;
 
 /**
  * 
@@ -36,7 +37,7 @@ import org.olat.ims.qti21.model.xml.SingleChoiceAssessmentItemBuilder;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class SingleChoiceScoreController extends FormBasicController implements EditorController {
+public class SingleChoiceScoreController extends FormBasicController implements AssessmentItemBuilderController {
 	
 	private TextElement minScoreEl;
 	private TextElement maxScoreEl;
@@ -81,6 +82,8 @@ public class SingleChoiceScoreController extends FormBasicController implements
 		Double maxScore = Double.parseDouble(maxScoreValue);
 		itemBuilder.setMaxScore(maxScore);
 		itemBuilder.setMinScore(new Double(0d));
+
+		fireEvent(ureq, new AssessmentItemEvent(AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED, itemBuilder.getAssessmentItem()));
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java
index 9184c7a241e0905cb1d9d5b523ab80a18dd223a4..7cae5dd8c4a8d4a61a75331a2b60d8d92c78e291 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java
@@ -32,7 +32,7 @@ import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class UnkownItemEditorController extends FormBasicController implements EditorController {
+public class UnkownItemEditorController extends FormBasicController implements AssessmentItemBuilderController {
 	
 	public UnkownItemEditorController(UserRequest ureq, WindowControl wControl) {
 		super(ureq, wControl);
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_content/bad_resource.html b/src/main/java/org/olat/ims/qti21/ui/editor/_content/bad_resource.html
new file mode 100644
index 0000000000000000000000000000000000000000..fd8683f69585f8aa0c3081e22df1a9761cba5262
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/_content/bad_resource.html
@@ -0,0 +1,5 @@
+<div class="alert alert-danger">
+	<p>Directory: $directory</p>
+	<p>File: $uri</p>
+	<p>$message</p>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties
index f38be912ca9aa039e8a94ee58c28d87bc7117c4d..701301da5b97a44e8a220eb88a0874d8aa526730 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties
@@ -11,4 +11,7 @@ form.imd.incorrect.title=Incorrect title
 form.imd.correct.text=Correct feedback
 form.imd.incorrect.text=Incorrect feedback
 min.score=Min. score
-max.score=Max. score
\ No newline at end of file
+max.score=Max. score
+new.elements=New elements
+new.sc=Single choice
+new.section=Section
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemPackage.java b/src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentItemEvent.java
similarity index 51%
rename from src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemPackage.java
rename to src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentItemEvent.java
index 4c648b2b4bf7e97a9f6712cee3adc1607a16166e..deb26732646c0543245633ea7dff78ad729d33f2 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemPackage.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentItemEvent.java
@@ -17,23 +17,44 @@
  * frentix GmbH, http://www.frentix.com
  * <p>
  */
-package org.olat.ims.qti21.model.xml;
+package org.olat.ims.qti21.ui.editor.events;
 
-import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem;
+import org.olat.core.gui.control.Event;
+
+import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
+import uk.ac.ed.ph.jqtiplus.node.test.AssessmentItemRef;
 
 /**
  * 
- * Initial date: 04.06.2015<br>
+ * Initial date: 03.07.2015<br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class AssessmentItemPackage {
+public class AssessmentItemEvent extends Event {
 	
-	public ResolvedAssessmentItem createSingleChoice() {
-		
-		ResolvedAssessmentItem item = new ResolvedAssessmentItem(null, null);
-		return item;
-		
+	private static final long serialVersionUID = -1768118856227595311L;
+	
+	public static final String ASSESSMENT_ITEM_CHANGED = "assessment-item-changed";
+	
+	private AssessmentItem item;
+	private AssessmentItemRef itemRef;
+
+	public AssessmentItemEvent(String cmd, AssessmentItem item) {
+		super(cmd);
+		this.item = item;
+	}
+	
+	public AssessmentItemEvent(String cmd, AssessmentItem item, AssessmentItemRef itemRef) {
+		super(cmd);
+		this.item = item;
+		this.itemRef = itemRef;
 	}
 
+	public AssessmentItem getAssessmentItem() {
+		return item;
+	}
+	
+	public AssessmentItemRef getAssessmentItemRef() {
+		return itemRef;
+	}
 }
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentSectionEvent.java b/src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentSectionEvent.java
similarity index 97%
rename from src/main/java/org/olat/ims/qti21/ui/editor/AssessmentSectionEvent.java
rename to src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentSectionEvent.java
index 3c1096da2c75acacca5913a29fc6dfd0e2bf6fd1..9bce2c778fb1a1909942d28c9246d74149caf994 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentSectionEvent.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentSectionEvent.java
@@ -17,7 +17,7 @@
  * frentix GmbH, http://www.frentix.com
  * <p>
  */
-package org.olat.ims.qti21.ui.editor;
+package org.olat.ims.qti21.ui.editor.events;
 
 import org.olat.core.gui.control.Event;
 
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEvent.java b/src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentTestEvent.java
similarity index 96%
rename from src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEvent.java
rename to src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentTestEvent.java
index 5efe507cbd40a89df5c847b97d11c51092d75410..f249b27173b0c2fd2f88be9cc7b7415ac8f56fca 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEvent.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentTestEvent.java
@@ -17,7 +17,7 @@
  * frentix GmbH, http://www.frentix.com
  * <p>
  */
-package org.olat.ims.qti21.ui.editor;
+package org.olat.ims.qti21.ui.editor.events;
 
 import org.olat.core.gui.control.Event;
 
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEvent.java b/src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentTestPartEvent.java
similarity index 72%
rename from src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEvent.java
rename to src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentTestPartEvent.java
index 1999143c62900a7cb6439c4b94effa2afd06a8e9..4c59cc579cd98ac2263ba15544fba2ffa8c032b9 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEvent.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/events/AssessmentTestPartEvent.java
@@ -17,23 +17,22 @@
  * frentix GmbH, http://www.frentix.com
  * <p>
  */
-package org.olat.ims.qti21.ui.editor;
+package org.olat.ims.qti21.ui.editor.events;
 
 import org.olat.core.gui.control.Event;
 
 /**
  * 
- * Initial date: 03.07.2015<br>
+ * Initial date: 10.12.2015<br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class AssessmentItemEvent extends Event {
-	
-	private static final long serialVersionUID = -1768118856227595311L;
-	
-	public static final AssessmentItemEvent ASSESSMENT_ITEM_CHANGED = new AssessmentItemEvent("changed");
+public class AssessmentTestPartEvent extends Event {
 
-	public AssessmentItemEvent(String cmd) {
+	private static final long serialVersionUID = 2930881002425927366L;
+	public static final AssessmentTestPartEvent ASSESSMENT_TEST_PART_CHANGED_EVENT = new AssessmentTestPartEvent("changed");
+	
+	public AssessmentTestPartEvent(String cmd) {
 		super(cmd);
 	}
 
diff --git a/src/test/java/org/olat/ims/qti21/model/xml/AssessmentHtmlBuilderTest.java b/src/test/java/org/olat/ims/qti21/model/xml/AssessmentHtmlBuilderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e69c2fd85e945e062173bc258991f2a664a98eee
--- /dev/null
+++ b/src/test/java/org/olat/ims/qti21/model/xml/AssessmentHtmlBuilderTest.java
@@ -0,0 +1,70 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.model.xml;
+
+import java.util.List;
+
+import javax.xml.transform.stream.StreamResult;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.olat.core.gui.render.StringOutput;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+
+import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
+import uk.ac.ed.ph.jqtiplus.node.content.ItemBody;
+import uk.ac.ed.ph.jqtiplus.node.content.basic.Block;
+import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
+import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
+
+/**
+ * Test the conversion from TinyMCE HTML code to the QTI Works object
+ * model.
+ * 
+ * 
+ * Initial date: 10.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AssessmentHtmlBuilderTest {
+	
+	private static final OLog log = Tracing.createLoggerFor(AssessmentHtmlBuilderTest.class);
+
+	@Test
+	public void filter() {
+		String content = "<html><p>Test \u00EA<strong><span><img = src='img.jpg'></span></strong></p><p>Test 2</p></html>";
+
+		AssessmentItem item = new AssessmentItem();
+		ItemBody helper = new ItemBody(item);
+		new AssessmentHtmlBuilder().appendHtml(helper, content);
+
+		List<Block> paragraphs = helper.getBlocks();
+		Assert.assertNotNull(paragraphs);
+		Assert.assertEquals(2, paragraphs.size());
+		
+		// The serializer can throw some exceptions if it doens't like the model
+		// we want to serialize.
+		StringOutput sb = new StringOutput();
+		QtiSerializer qtiSerializer = new QtiSerializer(new JqtiExtensionManager());
+		qtiSerializer.serializeJqtiObject(helper, new StreamResult(sb));
+		log.info(sb.toString());
+	}
+}
diff --git a/src/test/java/org/olat/ims/qti21/model/xml/AssessmentTestPackageTest.java b/src/test/java/org/olat/ims/qti21/model/xml/AssessmentTestPackageTest.java
index 6d7d353552382e71d5ead7891bb746498ba9796f..734e5c0890919276424b73d60d0628f22f6c3bac 100644
--- a/src/test/java/org/olat/ims/qti21/model/xml/AssessmentTestPackageTest.java
+++ b/src/test/java/org/olat/ims/qti21/model/xml/AssessmentTestPackageTest.java
@@ -32,7 +32,6 @@ import org.junit.Test;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.fileresource.types.ImsQTI21Resource.PathResourceLocator;
-import org.xml.sax.SAXParseException;
 
 import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
 import uk.ac.ed.ph.jqtiplus.node.content.variable.RubricBlock;
@@ -55,16 +54,12 @@ import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.SetOutcomeValue;
 import uk.ac.ed.ph.jqtiplus.notification.Notification;
 import uk.ac.ed.ph.jqtiplus.provision.BadResourceException;
 import uk.ac.ed.ph.jqtiplus.reading.AssessmentObjectXmlLoader;
-import uk.ac.ed.ph.jqtiplus.reading.QtiModelBuildingError;
-import uk.ac.ed.ph.jqtiplus.reading.QtiXmlInterpretationException;
-import uk.ac.ed.ph.jqtiplus.reading.QtiXmlInterpretationException.InterpretationFailureReason;
 import uk.ac.ed.ph.jqtiplus.reading.QtiXmlReader;
 import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
 import uk.ac.ed.ph.jqtiplus.types.Identifier;
 import uk.ac.ed.ph.jqtiplus.validation.TestValidationResult;
 import uk.ac.ed.ph.jqtiplus.value.BaseType;
 import uk.ac.ed.ph.jqtiplus.value.Cardinality;
-import uk.ac.ed.ph.jqtiplus.xmlutils.XmlParseResult;
 import uk.ac.ed.ph.jqtiplus.xmlutils.locators.ResourceLocator;
 
 /**
@@ -198,53 +193,10 @@ public class AssessmentTestPackageTest {
         }
         
         BadResourceException e = test.getResolvedAssessmentTest().getTestLookup().getBadResourceException();
-        if(e instanceof QtiXmlInterpretationException) {
-        	QtiXmlInterpretationException qe = (QtiXmlInterpretationException)e;
-        	if(qe.getQtiModelBuildingErrors() != null) {
-	        	for(QtiModelBuildingError error :qe.getQtiModelBuildingErrors()) {
-	        		String localName = error.getElementLocalName();
-	        		String msg = error.getException().getMessage();
-	        		int lineNumber = error.getElementLocation().getLineNumber();
-	        		System.out.println(lineNumber + " :: " + localName + " :: " + msg);
-	        	}
-        	}
-        	
-        	if(qe.getInterpretationFailureReason() != null) {
-        		InterpretationFailureReason reason = qe.getInterpretationFailureReason();
-        		System.out.println("Failure: " + reason);
-        	}
-        	
-        	if(qe.getXmlParseResult() != null) {
-        		XmlParseResult result = qe.getXmlParseResult();
-        		if(result.getWarnings() != null) {
-        			for(SAXParseException saxex : result.getWarnings()) {
-        				int lineNumber = saxex.getLineNumber();
-        				int columnNumber = saxex.getColumnNumber();
-        				String msg = saxex.getMessage();
-    	        		System.out.println("Error: " + lineNumber + ":" + columnNumber + " :: " + msg);
-        			}
-        		}
-        		
-        		if(result.getErrors() != null) {
-        			for(SAXParseException saxex : result.getErrors()) {
-        				int lineNumber = saxex.getLineNumber();
-        				int columnNumber = saxex.getColumnNumber();
-        				String msg = saxex.getMessage();
-    	        		System.out.println("Error: " + lineNumber + ":" + columnNumber + " :: " + msg);
-        			}
-        		}
-        		
-        		if(result.getFatalErrors() != null) {
-        			for(SAXParseException saxex : result.getFatalErrors()) {
-        				int lineNumber = saxex.getLineNumber();
-        				int columnNumber = saxex.getColumnNumber();
-        				String msg = saxex.getMessage();
-    	        		System.out.println("Fatal: " + lineNumber + ":" + columnNumber + " :: " + msg);
-        			}
-        		}
-        	}
-        }
-        
+        StringBuilder out = new StringBuilder();
+        AssessmentBuilderHelper.extractMessage(e, out);
+        log.info(out.toString());
+
         Assert.assertTrue(test.getModelValidationErrors().isEmpty());
 	}
 }
\ No newline at end of file
diff --git a/src/test/java/org/olat/ims/qti21/model/xml/ConvertHTMLTestTest.java b/src/test/java/org/olat/ims/qti21/model/xml/ConvertHTMLTestTest.java
deleted file mode 100644
index 15138d0b51c7543e7756ba905f5e28eb4a8a2f6b..0000000000000000000000000000000000000000
--- a/src/test/java/org/olat/ims/qti21/model/xml/ConvertHTMLTestTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/**
- * <a href="http://www.openolat.org">
- * OpenOLAT - Online Learning and Training</a><br>
- * <p>
- * Licensed under the Apache License, Version 2.0 (the "License"); <br>
- * you may not use this file except in compliance with the License.<br>
- * You may obtain a copy of the License at the
- * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
- * <p>
- * Unless required by applicable law or agreed to in writing,<br>
- * software distributed under the License is distributed on an "AS IS" BASIS, <br>
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
- * See the License for the specific language governing permissions and <br>
- * limitations under the License.
- * <p>
- * Initial code contributed and copyrighted by<br>
- * frentix GmbH, http://www.frentix.com
- * <p>
- */
-package org.olat.ims.qti21.model.xml;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.cyberneko.html.parsers.DOMParser;
-import org.jcodec.common.Assert;
-import org.junit.Test;
-import org.olat.core.util.filter.FilterFactory;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
-import uk.ac.ed.ph.jqtiplus.exception.QtiModelException;
-import uk.ac.ed.ph.jqtiplus.node.LoadingContext;
-import uk.ac.ed.ph.jqtiplus.node.QtiNode;
-import uk.ac.ed.ph.jqtiplus.node.content.ItemBody;
-import uk.ac.ed.ph.jqtiplus.node.content.xhtml.text.P;
-
-/**
- * 
- * Initial date: 07.12.2015<br>
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class ConvertHTMLTestTest {
-	
-	@Test
-	public void convert() {
-		String content = "<html><p>Test</p><p>Test 2</p></html>";
-		Document partialDocument = getDoc(content);
-		Element rootElement = partialDocument.getDocumentElement();
-	
-
-		
-		MyLoadingContext context = new MyLoadingContext();
-		
-		Element paragraphEl = (Element)rootElement.getFirstChild();
-		String tagName = paragraphEl.getTagName();
-		String localName = paragraphEl.getLocalName();
-
-		ItemBody itemBody = new ItemBody(null);
-		itemBody.load(rootElement, context);
-		
-		Assert.assertNotNull(localName);
-		Assert.assertEquals(2, itemBody.getBlocks().size());
-
-		
-		
-	}
-	
-	private Document getDoc(String content) {
-		try {
-			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-			factory.setValidating(false);
-			factory.setNamespaceAware(true);
-			DocumentBuilder builder = factory.newDocumentBuilder();
-			Document doc = builder.parse(new ByteArrayInputStream(content.getBytes()));
-			return doc;
-		} catch (ParserConfigurationException | SAXException | IOException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-			return null;
-		}
-	}
-	
-	private Document getDocument(String content) {
-		try {
-			DOMParser parser = new DOMParser();
-	        parser.setFeature("http://xml.org/sax/features/validation", false);
-	       
-			parser.setFeature( "http://cyberneko.org/html/features/override-namespaces", true);
-			parser.setFeature ( "http://xml.org/sax/features/namespaces", true );
-			parser.setProperty("http://cyberneko.org/html/properties/names/elems", "upper" ); // has no effect, cannot override xerces configuration
-			parser.setProperty( "http://cyberneko.org/html/properties/names/attrs", "upper" ); // has no effect, cannot override xerces configuration
-			parser.setFeature("http://cyberneko.org/html/features/balance-tags/document-fragment",true);
-			parser.parse(new InputSource(new StringReader(content)));
-			return parser.getDocument();
-		} catch (Exception e) {
-			e.printStackTrace();
-			return null;
-		}
-	}
-	
-	
-	private List<P> getText(QtiNode choice, String htmlContent) {
-		String text = FilterFactory.getHtmlTagsFilter().filter(htmlContent);
-		P firstChoiceText = AssessmentItemFactory.getParagraph(choice, text);
-		List<P> blocks = new ArrayList<>();
-		blocks.add(firstChoiceText);
-		return blocks;
-	}
-	
-	private static final class MyLoadingContext implements LoadingContext {
-
-		@Override
-		public JqtiExtensionManager getJqtiExtensionManager() {
-			//
-			return null;
-		}
-
-		@Override
-		public void modelBuildingError(QtiModelException exception, Node badNode) {
-			//
-		}
-		
-	}
-
-}
diff --git a/src/test/java/org/olat/ims/qti21/model/xml/ManifestPackageTest.java b/src/test/java/org/olat/ims/qti21/model/xml/ManifestPackageTest.java
index 60493fe43fddc97a23aaa180dea4b49198e4530d..fc846744221ff75e2df067d67373a4d77f51d028 100644
--- a/src/test/java/org/olat/ims/qti21/model/xml/ManifestPackageTest.java
+++ b/src/test/java/org/olat/ims/qti21/model/xml/ManifestPackageTest.java
@@ -55,8 +55,8 @@ public class ManifestPackageTest {
         	tmpDir.mkdirs();
         }
         
-        
-        FileOutputStream out = new FileOutputStream(new File(tmpDir, "imsmanifest.xml"));
+        File manifestFile = new File(tmpDir, "imsmanifest.xml");
+        FileOutputStream out = new FileOutputStream(manifestFile);
         ManifestPackage.write(manifestType, out);
         out.flush();
         out.close(); 
@@ -67,5 +67,9 @@ public class ManifestPackageTest {
         List<ContentPackageResource> tests = summary.getTestResources();
         Assert.assertEquals(1, items.size());
         Assert.assertEquals(1, tests.size());
+        
+        ManifestType reloadManifestType = ManifestPackage.read(manifestFile);
+        Assert.assertNotNull(reloadManifestType);
+        
 	}
 }