From 4a460153c68fb78bc6b3dd8a46c9cbe5cb2e45df Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Wed, 27 Feb 2013 10:50:37 +0100
Subject: [PATCH] OO-535: implements import of QTI 1.2 items

---
 pom.xml                                       |   2 +-
 src/main/java/org/olat/core/util/ZipUtil.java |  29 +++
 .../olat/ims/qti/QTI12PreviewController.java  |  67 +++++--
 .../qti/QTIQuestionPoolServiceProvider.java   |  22 +-
 .../ItemFileResourceValidator.java            | 189 ++++++++++++++++++
 .../qti/repository/handlers/QTIHandler.java   |   2 +-
 .../olat/ims/resources/dtd/ims_qtiasiv1p2.dtd |   1 +
 .../olat/ims/resources/dtd/ims_qtiresv1p2.dtd |   1 +
 .../org/olat/modules/qpool/QuestionItem.java  |   1 +
 .../olat/modules/qpool/QuestionItemShort.java |   2 +
 .../modules/qpool/QuestionPoolModule.java     |  43 +++-
 .../olat/modules/qpool/QuestionPoolSPI.java   |   9 +
 .../qpool/QuestionPoolSPIComparator.java      |  38 ++++
 .../modules/qpool/QuestionPoolService.java    |   9 +
 .../impl/PdfQuestionPoolServiceProvider.java  |  76 +++++++
 .../impl/TextQuestionPoolServiceProvider.java |  70 +++++++
 .../qpool/manager/NullPoolService.java        |   2 +-
 .../qpool/manager/QuestionItemDAO.java        |  41 +++-
 .../manager/QuestionPoolServiceImpl.java      | 128 ++++++++++++
 .../modules/qpool/manager/StudyFieldDAO.java  |   4 +
 .../modules/qpool/model/QuestionItemImpl.java |  35 +++-
 .../ui/ImportQuestionItemController.java      |  84 ++++++++
 .../modules/qpool/ui/QuestionItemRow.java     |   5 +
 .../qpool/ui/QuestionListController.java      |  25 ++-
 .../modules/qpool/ui/_content/item_list.html  |   1 +
 .../qpool/ui/_i18n/LocalStrings_de.properties |   3 +-
 .../database/mysql/alter_8_4_0_to_9_0_0.sql   |   7 +-
 .../FileResourceValidatorTest.java            |  46 +++++
 .../ims/qti/fileresource/mchc_i_002.zip       | Bin 0 -> 13846 bytes
 .../ims/qti/fileresource/mchc_ir_005.xml      |   1 +
 .../qpool/manager/CollectionDAOTest.java      |   6 +-
 .../modules/qpool/manager/PoolDAOTest.java    |   6 +-
 .../qpool/manager/QuestionDAOTest.java        |  27 +--
 .../manager/QuestionPoolServiceTest.java      |  31 ++-
 .../olat/modules/qpool/manager/mchc_i_001.xml |   1 +
 .../java/org/olat/test/AllTestsJunit4.java    |   1 +
 36 files changed, 952 insertions(+), 63 deletions(-)
 create mode 100644 src/main/java/org/olat/ims/qti/fileresource/ItemFileResourceValidator.java
 create mode 100644 src/main/java/org/olat/ims/resources/dtd/ims_qtiasiv1p2.dtd
 create mode 100644 src/main/java/org/olat/ims/resources/dtd/ims_qtiresv1p2.dtd
 create mode 100644 src/main/java/org/olat/modules/qpool/QuestionPoolSPIComparator.java
 create mode 100644 src/main/java/org/olat/modules/qpool/impl/PdfQuestionPoolServiceProvider.java
 create mode 100644 src/main/java/org/olat/modules/qpool/impl/TextQuestionPoolServiceProvider.java
 create mode 100644 src/main/java/org/olat/modules/qpool/ui/ImportQuestionItemController.java
 create mode 100644 src/test/java/org/olat/modules/ims/qti/fileresource/FileResourceValidatorTest.java
 create mode 100644 src/test/java/org/olat/modules/ims/qti/fileresource/mchc_i_002.zip
 create mode 100644 src/test/java/org/olat/modules/ims/qti/fileresource/mchc_ir_005.xml
 create mode 100644 src/test/java/org/olat/modules/qpool/manager/mchc_i_001.xml

diff --git a/pom.xml b/pom.xml
index 272c5dc91be..1a20a051b55 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1528,7 +1528,7 @@
 		<dependency>
 			<groupId>commons-io</groupId>
 			<artifactId>commons-io</artifactId>
-			<version>1.4</version>
+			<version>2.4</version>
 		</dependency>
 		<dependency>
 			<groupId>org.mnode.ical4j</groupId>
diff --git a/src/main/java/org/olat/core/util/ZipUtil.java b/src/main/java/org/olat/core/util/ZipUtil.java
index 36f4e3c851f..c64e6a4ad33 100644
--- a/src/main/java/org/olat/core/util/ZipUtil.java
+++ b/src/main/java/org/olat/core/util/ZipUtil.java
@@ -30,6 +30,7 @@ import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -45,6 +46,7 @@ import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
 
+import org.apache.commons.io.IOUtils;
 import org.olat.core.commons.modules.bc.meta.MetaInfo;
 import org.olat.core.commons.modules.bc.meta.MetaInfoHelper;
 import org.olat.core.commons.modules.bc.meta.tagged.MetaTagged;
@@ -125,6 +127,33 @@ public class ZipUtil {
 		return unzip(zipLeaf, targetDir, null, false);
 	}
 	
+	/**
+	 * Unzip a file in the target dir with the restricted version
+	 * @param zipFile
+	 * @param targetDir
+	 * @return
+	 */
+	public static boolean unzipStrict(File zipFile, VFSContainer targetDir) {
+		if (targetDir instanceof LocalFolderImpl) {
+			String outdir = ((LocalFolderImpl) targetDir).getBasefile().getAbsolutePath();
+			InputStream in = null;
+			try {
+				long s = System.currentTimeMillis();
+				in = new FileInputStream(zipFile);
+				xxunzip (in, outdir);
+				log.info("unzip file="+zipFile.getName()+" to="+outdir +" t="+Long.toString(System.currentTimeMillis()-s));
+				return true;
+			} catch (IOException e) {
+				log.error("I/O failure while unzipping "+zipFile.getName()+" to "+outdir);
+				return false;
+			} finally {
+				IOUtils.closeQuietly(in);
+			}
+		}
+		return false;
+	}
+	
+	
 	/**
 	 * Unzip a file to a directory using the versioning system of VFS
 	 * @param zipLeaf	The file to unzip
diff --git a/src/main/java/org/olat/ims/qti/QTI12PreviewController.java b/src/main/java/org/olat/ims/qti/QTI12PreviewController.java
index 2196fcdfec5..89b87e97ae7 100644
--- a/src/main/java/org/olat/ims/qti/QTI12PreviewController.java
+++ b/src/main/java/org/olat/ims/qti/QTI12PreviewController.java
@@ -19,27 +19,36 @@
  */
 package org.olat.ims.qti;
 
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.InputStream;
 
+import javax.servlet.http.HttpServletRequest;
+
 import org.dom4j.Document;
 import org.dom4j.Element;
+import org.olat.core.CoreSpringFactory;
+import org.olat.core.dispatcher.mapper.Mapper;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.panel.Panel;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
+import org.olat.core.gui.media.MediaResource;
+import org.olat.core.gui.media.NotFoundMediaResource;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.util.Util;
+import org.olat.core.util.vfs.VFSContainer;
+import org.olat.core.util.vfs.VFSItem;
+import org.olat.core.util.vfs.VFSLeaf;
+import org.olat.core.util.vfs.VFSMediaResource;
 import org.olat.core.util.xml.XMLParser;
 import org.olat.ims.qti.editor.ItemPreviewController;
 import org.olat.ims.qti.editor.QTIEditorPackage;
 import org.olat.ims.qti.editor.beecom.objects.Item;
 import org.olat.ims.qti.editor.beecom.parser.ParserManager;
 import org.olat.ims.resources.IMSEntityResolver;
-
+import org.olat.modules.qpool.QuestionItem;
+import org.olat.modules.qpool.QuestionPoolService;
 /**
  * 
  * Initial date: 21.02.2013<br>
@@ -50,30 +59,36 @@ public class QTI12PreviewController extends BasicController {
 	
 	private final Panel mainPanel;
 	private ItemPreviewController previewCtrl;
+	private final QuestionPoolService qpoolService;
 
-	public QTI12PreviewController(UserRequest ureq, WindowControl wControl) {
+	public QTI12PreviewController(UserRequest ureq, WindowControl wControl, QuestionItem qitem) {
 		super(ureq, wControl);
-		
+		qpoolService = CoreSpringFactory.getImpl(QuestionPoolService.class);
 		mainPanel = new Panel("qti12preview");
 		
-		Item item = readItemXml();
-		if(item != null) {
-			Translator translator = Util.createPackageTranslator(QTIEditorPackage.class, getLocale());
-			previewCtrl = new ItemPreviewController(wControl, item, "/Users/srosse", translator);
-			listenTo(previewCtrl);
-			mainPanel.setContent(previewCtrl.getInitialComponent());
+		VFSLeaf leaf = qpoolService.getRootFile(qitem);
+		if(leaf == null) {
+			//no data to preview
+		} else {
+			Item item = readItemXml(leaf);
+			if(item != null) {
+				Translator translator = Util.createPackageTranslator(QTIEditorPackage.class, getLocale());
+				VFSContainer directory = qpoolService.getRootDirectory(qitem);
+				String mapperUrl = registerMapper(ureq, new QItemDirectoryMapper(directory));
+				previewCtrl = new ItemPreviewController(wControl, item, mapperUrl, translator);
+				listenTo(previewCtrl);
+				mainPanel.setContent(previewCtrl.getInitialComponent());
+			}
 		}
 		
 		putInitialPanel(mainPanel);
 	}
 	
-	private Item readItemXml() {
-
-		XMLParser xmlParser = new XMLParser(new IMSEntityResolver());
+	private Item readItemXml(VFSLeaf leaf) {
 		Document doc = null;
 		try {
-			File itemXml = new File("/Users/srosse/Desktop/mchc_i_001.xml");
-			InputStream is = new FileInputStream(itemXml);
+			InputStream is = leaf.getInputStream();
+			XMLParser xmlParser = new XMLParser(new IMSEntityResolver());
 			doc = xmlParser.parse(is, false);
 			
 			Element item = (Element)doc.selectSingleNode("questestinterop/item");
@@ -81,7 +96,6 @@ public class QTI12PreviewController extends BasicController {
 		  Item qtiItem = (Item)parser.parse(item);
 
 			is.close();
-			
 			return qtiItem;
 		} catch (Exception e) {
 			logError("", e);
@@ -98,4 +112,23 @@ public class QTI12PreviewController extends BasicController {
 	protected void event(UserRequest ureq, Component source, Event event) {
 		//
 	}
+	
+	private class QItemDirectoryMapper implements Mapper {
+		private final VFSContainer itemBaseContainer;
+		
+		private QItemDirectoryMapper(VFSContainer container) {
+			itemBaseContainer = container;
+		}
+		
+		public MediaResource handle(String relPath, HttpServletRequest request) {
+			VFSItem vfsItem = itemBaseContainer.resolve(relPath);
+			MediaResource mr;
+			if (vfsItem == null || !(vfsItem instanceof VFSLeaf)) {
+				mr = new NotFoundMediaResource(relPath);
+			} else {
+				mr = new VFSMediaResource((VFSLeaf) vfsItem);
+			}
+			return mr;
+		}
+	}
 }
diff --git a/src/main/java/org/olat/ims/qti/QTIQuestionPoolServiceProvider.java b/src/main/java/org/olat/ims/qti/QTIQuestionPoolServiceProvider.java
index f91d11c5b4a..807ea9e54b8 100644
--- a/src/main/java/org/olat/ims/qti/QTIQuestionPoolServiceProvider.java
+++ b/src/main/java/org/olat/ims/qti/QTIQuestionPoolServiceProvider.java
@@ -19,9 +19,13 @@
  */
 package org.olat.ims.qti;
 
+import java.io.File;
+
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.core.util.vfs.VFSLeaf;
+import org.olat.ims.qti.fileresource.ItemFileResourceValidator;
 import org.olat.modules.qpool.QuestionItem;
 import org.olat.modules.qpool.QuestionPoolSPI;
 
@@ -33,20 +37,34 @@ import org.olat.modules.qpool.QuestionPoolSPI;
  */
 public class QTIQuestionPoolServiceProvider implements QuestionPoolSPI {
 
+	@Override
+	public int getPriority() {
+		return 10;
+	}
+
 	@Override
 	public String getFormat() {
 		return QTIConstants.QTI_12_FORMAT;
 	}
 
+	@Override
+	public boolean isCompatible(String filename, File file) {
+		return new ItemFileResourceValidator().validate(filename, file);
+	}
+	@Override
+	public boolean isCompatible(String filename, VFSLeaf file) {
+		return new ItemFileResourceValidator().validate(filename, file);
+	}
+
 	@Override
 	public Controller getPreviewController(UserRequest ureq, WindowControl wControl, QuestionItem item) {
-		QTI12PreviewController previewCtrl = new QTI12PreviewController(ureq, wControl);
+		QTI12PreviewController previewCtrl = new QTI12PreviewController(ureq, wControl, item);
 		return previewCtrl;
 	}
 
 	@Override
 	public Controller getEditableController(UserRequest ureq, WindowControl wControl, QuestionItem item) {
-		QTI12PreviewController previewCtrl = new QTI12PreviewController(ureq, wControl);
+		QTI12PreviewController previewCtrl = new QTI12PreviewController(ureq, wControl, item);
 		return previewCtrl;
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/fileresource/ItemFileResourceValidator.java b/src/main/java/org/olat/ims/qti/fileresource/ItemFileResourceValidator.java
new file mode 100644
index 00000000000..afd27c2a787
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/fileresource/ItemFileResourceValidator.java
@@ -0,0 +1,189 @@
+/**
+ * <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.qti.fileresource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.commons.io.IOUtils;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.vfs.VFSLeaf;
+import org.olat.ims.resources.IMSEntityResolver;
+import org.olat.search.service.document.file.utils.ShieldInputStream;
+import org.xml.sax.Attributes;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * 
+ * Initial date: 27.02.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class ItemFileResourceValidator {
+	
+	private static final OLog log = Tracing.createLoggerFor(ItemFileResourceValidator.class);
+
+	public boolean validate(String filename, File file) {
+		InputStream in = null;
+		try {
+			in = new FileInputStream(file);
+			return validate(filename, in);
+		} catch (FileNotFoundException e) {
+			return false;
+		} finally {
+			IOUtils.closeQuietly(in);
+		}
+	}
+
+	public boolean validate(String filename, VFSLeaf file) {
+		InputStream in = null;
+		try {
+			in = file.getInputStream();
+			return validate(filename, in);
+		} catch (Exception e) {
+			return false;
+		} finally {
+			IOUtils.closeQuietly(in);
+		}
+	}
+
+	public boolean validate(String filename, InputStream in) {
+		boolean valid = false;
+
+		if(filename.toLowerCase().endsWith(".xml")) {
+			valid = validateXml(in);
+			IOUtils.closeQuietly(in);
+		} else if(filename.toLowerCase().endsWith(".zip")) {
+			ZipInputStream oZip = new ZipInputStream(in);
+			try {
+				ZipEntry oEntr = oZip.getNextEntry();
+				while (oEntr != null) {
+					if (!oEntr.isDirectory()) {
+						if(validateXml(new ShieldInputStream(oZip))) {
+							valid = true;
+						}
+					}
+					oZip.closeEntry();
+					oEntr = oZip.getNextEntry();
+				}
+			} catch(Exception e) {
+				log.error("", e);
+				valid = false;
+			} finally {
+				IOUtils.closeQuietly(oZip);
+				IOUtils.closeQuietly(in);
+			}
+		}
+		
+		return valid;
+	}
+	
+	private boolean validateXml(InputStream in) {
+		try {
+			SAXParserFactory factory = SAXParserFactory.newInstance();
+			factory.setValidating(true);
+			factory.setNamespaceAware(true);
+			
+			SimpleErrorHandler errorHandler = new SimpleErrorHandler();
+			ItemContentHandler contentHandler = new ItemContentHandler();
+			
+			SAXParser parser = factory.newSAXParser();
+			XMLReader reader = parser.getXMLReader();
+			reader.setEntityResolver(new IMSEntityResolver());
+			reader.setErrorHandler(errorHandler);
+			reader.setContentHandler(contentHandler);
+			reader.parse(new InputSource(in));
+			return errorHandler.isValid() && contentHandler.isItem();
+		} catch (ParserConfigurationException e) {
+			return false;
+		} catch (SAXException e) {
+			return false;
+		} catch (Exception e) {
+			return false;
+		}
+	}
+	
+	private static class SimpleErrorHandler implements ErrorHandler {
+		private int error = 0;
+		
+		public boolean isValid() {
+			return error == 0;
+		}
+
+		@Override
+		public void warning(SAXParseException exception) throws SAXException {
+			//
+		}
+
+		@Override
+		public void error(SAXParseException exception) throws SAXException {
+			error++;
+		}
+
+		@Override
+		public void fatalError(SAXParseException exception) throws SAXException {
+			error++;
+		}
+	}
+	
+	private static class ItemContentHandler extends DefaultHandler {
+		private boolean interop;
+		private boolean item;
+		
+		public boolean isItem() {
+			return item;
+		}
+
+		@Override
+		public void startElement(String uri, String localName, String qName, Attributes attributes)
+		throws SAXException {	
+			
+			if("questestinterop".equals(qName)) {
+				interop = true;
+			} else if("item".equals(localName)) {
+				if(interop) {
+					item = true;
+				}
+			}
+		}
+
+		@Override
+		public void endElement(String uri, String localName, String qName)
+		throws SAXException {
+			if("questestinterop".equals(qName)) {
+				interop = false;
+			}
+		}
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti/repository/handlers/QTIHandler.java b/src/main/java/org/olat/ims/qti/repository/handlers/QTIHandler.java
index 637036c3e85..8732dcc45d0 100644
--- a/src/main/java/org/olat/ims/qti/repository/handlers/QTIHandler.java
+++ b/src/main/java/org/olat/ims/qti/repository/handlers/QTIHandler.java
@@ -154,7 +154,7 @@ public abstract class QTIHandler extends FileHandler implements RepositoryHandle
 	  File fUnzippedDir = FileResourceManager.getInstance().unzipFileResource(tempFr);
 	  File changeLogDir = new File(fUnzippedDir, "changelog");
 	  if(changeLogDir.exists()) {	  	
-	  	boolean changeLogDeleted = FileUtils.deleteDirsAndFiles(changeLogDir, true, true);	  	
+	  	FileUtils.deleteDirsAndFiles(changeLogDir, true, true);	  	
 	  }
 	  File targetZipFile = sourceFile;
 	  FileUtils.deleteDirsAndFiles(targetZipFile.getParentFile(), true, false);
diff --git a/src/main/java/org/olat/ims/resources/dtd/ims_qtiasiv1p2.dtd b/src/main/java/org/olat/ims/resources/dtd/ims_qtiasiv1p2.dtd
new file mode 100644
index 00000000000..3ba16b4cd93
--- /dev/null
+++ b/src/main/java/org/olat/ims/resources/dtd/ims_qtiasiv1p2.dtd
@@ -0,0 +1 @@
+<?xml version='1.0' encoding='UTF-8' ?>

<!--Generated by XML Authority-->

<!--	*******************************************************		-->
<!--																-->
<!--	TITLE:		ims_qtiasiv1p2.dtd								-->
<!--	TYPE:		IMS Question and Test Interoperability			-->
<!--				Assessment, Section, Item structure	and			-->
<!--				Objects-bank.									-->
<!--																-->
<!--	REVISION HISTORY:											-->
<!--	Date	        Author										-->
<!--	====	        ======										-->
<!--	22nd Jan 2002	Colin Smythe								-->
<!--																-->
<!--	This specification has been approved as a PDS release.		-->
<!--																-->
<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--					ROOT DEFINITION								-->
<!--	*******************************************************		-->
<!ELEMENT questestinterop (qticomment? , (objectbank | assessment | (section | item)+))>

<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--					ENTITY DEFINITIONS							-->
<!--	*******************************************************		-->
<!ENTITY % I_Testoperator " testoperator  (EQ | NEQ | LT | LTE | GT | GTE )  #REQUIRED">

<!ENTITY % I_Pname " pname CDATA  #REQUIRED">

<!ENTITY % I_Class " class CDATA  'Block'">

<!ENTITY % I_Mdoperator " mdoperator  (EQ | NEQ | LT | LTE | GT | GTE )  #REQUIRED">

<!ENTITY % I_Mdname " mdname CDATA  #REQUIRED">

<!ENTITY % I_Title " title CDATA  #IMPLIED">

<!ENTITY % I_Label " label CDATA  #IMPLIED">

<!ENTITY % I_Ident " ident CDATA  #REQUIRED">

<!ENTITY % I_View " view  (All | 
          Administrator | 
          AdminAuthority | 
          Assessor | 
          Author | 
          Candidate | 
          InvigilatorProctor | 
          Psychometrician | 
          Scorer | 
          Tutor )  'All'">

<!ENTITY % I_FeedbackSwitch " feedbackswitch  (Yes | No )  'Yes'">

<!ENTITY % I_HintSwitch " hintswitch  (Yes | No )  'Yes'">

<!ENTITY % I_SolutionSwitch " solutionswitch  (Yes | No )  'Yes'">

<!ENTITY % I_Rcardinality " rcardinality  (Single | Multiple | Ordered )  'Single'">

<!ENTITY % I_Rtiming " rtiming  (Yes | No )  'No'">

<!ENTITY % I_Uri " uri CDATA  #IMPLIED">

<!ENTITY % I_X0 " x0 CDATA  #IMPLIED">

<!ENTITY % I_Y0 " y0 CDATA  #IMPLIED">

<!ENTITY % I_Height " height CDATA  #IMPLIED">

<!ENTITY % I_Width " width CDATA  #IMPLIED">

<!ENTITY % I_Embedded " embedded CDATA  'base64'">

<!ENTITY % I_LinkRefId " linkrefid CDATA  #REQUIRED">

<!ENTITY % I_VarName " varname CDATA  'SCORE'">

<!ENTITY % I_RespIdent " respident CDATA  #REQUIRED">

<!ENTITY % I_Continue " continue  (Yes | No )  'No'">

<!ENTITY % I_CharSet " charset CDATA  'ascii-us'">

<!ENTITY % I_ScoreModel " scoremodel CDATA  #IMPLIED">

<!ENTITY % I_MinNumber " minnumber CDATA  #IMPLIED">

<!ENTITY % I_MaxNumber " maxnumber CDATA  #IMPLIED">

<!ENTITY % I_FeedbackStyle " feedbackstyle  (Complete | Incremental | Multilevel | Proprietary )  'Complete'">

<!ENTITY % I_Case " case  (Yes | No )  'No'">

<!ENTITY % I_EntityRef " entityref ENTITY  #IMPLIED">

<!ENTITY % I_Index " index CDATA  #IMPLIED">

<!ELEMENT qmd_computerscored (#PCDATA)>

<!ELEMENT qmd_feedbackpermitted (#PCDATA)>

<!ELEMENT qmd_hintspermitted (#PCDATA)>

<!ELEMENT qmd_itemtype (#PCDATA)>

<!ELEMENT qmd_maximumscore (#PCDATA)>

<!ELEMENT qmd_renderingtype (#PCDATA)>

<!ELEMENT qmd_responsetype (#PCDATA)>

<!ELEMENT qmd_scoringpermitted (#PCDATA)>

<!ELEMENT qmd_solutionspermitted (#PCDATA)>

<!ELEMENT qmd_status (#PCDATA)>

<!ELEMENT qmd_timedependence (#PCDATA)>

<!ELEMENT qmd_timelimit (#PCDATA)>

<!ELEMENT qmd_toolvendor (#PCDATA)>

<!ELEMENT qmd_topic (#PCDATA)>

<!ELEMENT qmd_material (#PCDATA)>

<!ELEMENT qmd_typeofsolution (#PCDATA)>

<!ELEMENT qmd_levelofdifficulty (#PCDATA)>

<!ELEMENT qmd_weighting (#PCDATA)>

<!ELEMENT qtimetadata (vocabulary? , qtimetadatafield+)>

<!ELEMENT vocabulary (#PCDATA)>

<!ATTLIST vocabulary  %I_Uri;
                       %I_EntityRef;
                       vocab_type  CDATA  #IMPLIED >
<!ELEMENT qtimetadatafield (fieldlabel , fieldentry)>

<!ATTLIST qtimetadatafield  xml:lang CDATA  #IMPLIED >
<!ELEMENT fieldlabel (#PCDATA)>

<!ELEMENT fieldentry (#PCDATA)>

<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--				COMMON OBJECT DEFINITIONS						-->
<!--	*******************************************************		-->
<!ELEMENT qticomment (#PCDATA)>

<!ATTLIST qticomment  xml:lang CDATA    #IMPLIED>
<!ELEMENT material (qticomment? , (mattext | matemtext | matimage | mataudio | matvideo | matapplet | matapplication | matref | matbreak | mat_extension)+ , altmaterial*)>

<!ATTLIST material  %I_Label;
                     xml:lang CDATA  #IMPLIED >
<!ELEMENT mattext (#PCDATA)>

<!ATTLIST mattext  texttype    CDATA  'text/plain'
                    %I_Label;
                    %I_CharSet;
                    %I_Uri;
                    xml:space    (preserve | default )  'default'
                    xml:lang    CDATA  #IMPLIED
                    %I_EntityRef;
                    %I_Width;
                    %I_Height;
                    %I_Y0;
                    %I_X0; >
<!ELEMENT matemtext (#PCDATA)>

<!ATTLIST matemtext  texttype    CDATA  'text/plain'
                      %I_Label;
                      %I_CharSet;
                      %I_Uri;
                      xml:space    (preserve | default )  'default'
                      xml:lang    CDATA  #IMPLIED
                      %I_EntityRef;
                      %I_Width;
                      %I_Height;
                      %I_Y0;
                      %I_X0; >
<!ELEMENT matimage (#PCDATA)>

<!ATTLIST matimage  imagtype    CDATA  'image/jpeg'
                     %I_Label;
                     %I_Height;
                     %I_Uri;
                     %I_Embedded;
                     %I_Width;
                     %I_Y0;
                     %I_X0;
                     %I_EntityRef; >
<!ELEMENT mataudio (#PCDATA)>

<!ATTLIST mataudio  audiotype   CDATA  'audio/base'
                     %I_Label;
                     %I_Uri;
                     %I_Embedded;
                     %I_EntityRef; >
<!ELEMENT matvideo (#PCDATA)>

<!ATTLIST matvideo  videotype   CDATA  'video/avi'
                     %I_Label;
                     %I_Uri;
                     %I_Width;
                     %I_Height;
                     %I_Y0;
                     %I_X0;
                     %I_Embedded;
                     %I_EntityRef; >
<!ELEMENT matapplet (#PCDATA)>

<!ATTLIST matapplet  %I_Label;
                      %I_Uri;
                      %I_Y0;
                      %I_Height;
                      %I_Width;
                      %I_X0;
                      %I_Embedded;
                      %I_EntityRef; >
<!ELEMENT matapplication (#PCDATA)>

<!ATTLIST matapplication  apptype     CDATA  #IMPLIED
                           %I_Label;
                           %I_Uri;
                           %I_Embedded;
                           %I_EntityRef; >
<!ELEMENT matbreak EMPTY>

<!ELEMENT matref EMPTY>

<!ATTLIST matref  %I_LinkRefId; >

<!ELEMENT material_ref EMPTY>

<!ATTLIST material_ref  %I_LinkRefId; >

<!ELEMENT altmaterial (qticomment? , (mattext | matemtext | matimage | mataudio | matvideo | matapplet | matapplication | matref | matbreak | mat_extension)+)>

<!ATTLIST altmaterial  xml:lang CDATA  #IMPLIED >
<!ELEMENT decvar (#PCDATA)>

<!ATTLIST decvar  %I_VarName;
                   vartype     (Integer | 
                                String | 
                                Decimal | 
                                Scientific | 
                                Boolean | 
                                Enumerated | 
                                Set )  'Integer'
                   defaultval CDATA  #IMPLIED
                   minvalue   CDATA  #IMPLIED
                   maxvalue   CDATA  #IMPLIED
                   members    CDATA  #IMPLIED
                   cutvalue   CDATA  #IMPLIED >
<!ELEMENT setvar (#PCDATA)>

<!ATTLIST setvar  %I_VarName;
                   action     (Set | Add | Subtract | Multiply | Divide )  'Set' >
<!ELEMENT interpretvar (material | material_ref)>

<!ATTLIST interpretvar  %I_View;
                         %I_VarName; >
<!ELEMENT conditionvar (not | and | or | unanswered | other | varequal | varlt | varlte | vargt | vargte | varsubset | varinside | varsubstring | durequal | durlt | durlte | durgt | durgte | var_extension)+>

<!ELEMENT not (and | or | not | unanswered | other | varequal | varlt | varlte | vargt | vargte | varsubset | varinside | varsubstring | durequal | durlt | durlte | durgt | durgte)>

<!ELEMENT and (not | and | or | unanswered | other | varequal | varlt | varlte | vargt | vargte | varsubset | varinside | varsubstring | durequal | durlt | durlte | durgt | durgte)+>

<!ELEMENT or (not | and | or | unanswered | other | varequal | varlt | varlte | vargt | vargte | varsubset | varinside | varsubstring | durequal | durlt | durlte | durgt | durgte)+>

<!ELEMENT varequal (#PCDATA)>

<!ATTLIST varequal  %I_Case;
                     %I_RespIdent;
                     %I_Index; >
<!ELEMENT varlt (#PCDATA)>

<!ATTLIST varlt  %I_RespIdent;
                  %I_Index; >
<!ELEMENT varlte (#PCDATA)>

<!ATTLIST varlte  %I_RespIdent;
                   %I_Index; >
<!ELEMENT vargt (#PCDATA)>

<!ATTLIST vargt  %I_RespIdent;
                  %I_Index; >
<!ELEMENT vargte (#PCDATA)>

<!ATTLIST vargte  %I_RespIdent;
                   %I_Index; >
<!ELEMENT varsubset (#PCDATA)>

<!ATTLIST varsubset  %I_RespIdent;
                      setmatch     (Exact | Partial )  'Exact'
                      %I_Index; >
<!ELEMENT varinside (#PCDATA)>

<!ATTLIST varinside  areatype     (Ellipse | Rectangle | Bounded )  #REQUIRED
                      %I_RespIdent;
                      %I_Index; >
                      
<!ELEMENT varsubstring (#PCDATA)>

<!ATTLIST varsubstring  %I_Index;
                         %I_RespIdent;
                         %I_Case; >
<!ELEMENT durequal (#PCDATA)>

<!ATTLIST durequal  %I_Index;
                     %I_RespIdent; >
<!ELEMENT durlt (#PCDATA)>

<!ATTLIST durlt  %I_Index;
                  %I_RespIdent; >
<!ELEMENT durlte (#PCDATA)>

<!ATTLIST durlte  %I_Index;
                   %I_RespIdent; >
<!ELEMENT durgt (#PCDATA)>

<!ATTLIST durgt  %I_Index;
                  %I_RespIdent; >
<!ELEMENT durgte (#PCDATA)>

<!ATTLIST durgte  %I_Index;
                   %I_RespIdent; >
<!ELEMENT unanswered (#PCDATA)>

<!ATTLIST unanswered  %I_RespIdent; >
<!ELEMENT other (#PCDATA)>

<!ELEMENT duration (#PCDATA)>

<!ELEMENT displayfeedback (#PCDATA)>

<!ATTLIST displayfeedback  feedbacktype  (Response | Solution | Hint )  'Response'
                            %I_LinkRefId; >
<!ELEMENT objectives (qticomment? , (material+ | flow_mat+))>

<!ATTLIST objectives  %I_View; >
<!ELEMENT rubric (qticomment? , (material+ | flow_mat+))>

<!ATTLIST rubric  %I_View; >
<!ELEMENT flow_mat (flow_mat | material | material_ref)+>

<!ATTLIST flow_mat  %I_Class; >
<!ELEMENT presentation_material (qticomment? , flow_mat+)>

<!ELEMENT reference (qticomment | material | mattext | matemtext | matimage | mataudio | matvideo | matapplet | matapplication | matbreak | mat_extension)+>

<!ELEMENT selection_ordering (qticomment? , sequence_parameter? , selection* , order?)>

<!ATTLIST selection_ordering  sequence_type CDATA  #IMPLIED >
<!ELEMENT outcomes_processing (qticomment? , outcomes , objects_condition* , processing_parameter* , map_output* , outcomes_feedback_test*)>

<!ATTLIST outcomes_processing  %I_ScoreModel; >
<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--					EXTENSION DEFINITIONS						-->
<!--	*******************************************************		-->
<!ELEMENT mat_extension ANY>

<!ELEMENT var_extension ANY>

<!ELEMENT response_extension ANY>

<!ELEMENT render_extension ANY>

<!ELEMENT assessproc_extension ANY>

<!ELEMENT sectionproc_extension ANY>

<!ELEMENT itemproc_extension ANY>

<!ELEMENT respcond_extension ANY>

<!ELEMENT selection_extension ANY>

<!ELEMENT objectscond_extension (#PCDATA)>

<!ELEMENT order_extension ANY>

<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--				OBJECT-BANK OBJECT DEFINITIONS					-->
<!--	*******************************************************		-->

<!ELEMENT objectbank (qticomment? , qtimetadata* , (section | item)+)>

<!ATTLIST objectbank  %I_Ident; >

<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--				ASSESSMENT OBJECT DEFINITIONS					-->
<!--	*******************************************************		-->
<!ELEMENT assessment (qticomment? , duration? , qtimetadata* , objectives* , assessmentcontrol* , rubric* , presentation_material? , outcomes_processing* , assessproc_extension? , assessfeedback* , selection_ordering? , reference? , (sectionref | section)+)>

<!ATTLIST assessment  %I_Ident;
                       %I_Title;
                       xml:lang CDATA  #IMPLIED >
<!ELEMENT assessmentcontrol (qticomment?)>

<!ATTLIST assessmentcontrol  %I_HintSwitch;
                              %I_SolutionSwitch;
                              %I_View;
                              %I_FeedbackSwitch; >
<!ELEMENT assessfeedback (qticomment? , (material+ | flow_mat+))>

<!ATTLIST assessfeedback  %I_View;
                           %I_Ident;
                           %I_Title; >
<!ELEMENT sectionref (#PCDATA)>

<!ATTLIST sectionref  %I_LinkRefId; >
<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--				SECTION OBJECT DEFINITIONS						-->
<!--	*******************************************************		-->
<!ELEMENT section (qticomment? , duration? , qtimetadata* , objectives* , sectioncontrol* , sectionprecondition* , sectionpostcondition* , rubric* , presentation_material? , outcomes_processing* , sectionproc_extension? , sectionfeedback* , selection_ordering? , reference? , (itemref | item | sectionref | section)*)>

<!ATTLIST section  %I_Ident;
                    %I_Title;
                    xml:lang CDATA  #IMPLIED >
<!ELEMENT sectionprecondition (#PCDATA)>

<!ELEMENT sectionpostcondition (#PCDATA)>

<!ELEMENT sectioncontrol (qticomment?)>

<!ATTLIST sectioncontrol  %I_FeedbackSwitch;
                           %I_HintSwitch;
                           %I_SolutionSwitch;
                           %I_View; >
<!ELEMENT itemref (#PCDATA)>

<!ATTLIST itemref  %I_LinkRefId; >
<!ELEMENT sectionfeedback (qticomment? , (material+ | flow_mat+))>

<!ATTLIST sectionfeedback  %I_View;
                            %I_Ident;
                            %I_Title; >
<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--					ITEM OBJECT DEFINITIONS						-->
<!--	*******************************************************		-->
<!ELEMENT item (qticomment? , duration? , itemmetadata? , objectives* , itemcontrol* , itemprecondition* , itempostcondition* , (itemrubric | rubric)* , presentation? , resprocessing* , itemproc_extension? , itemfeedback* , reference?)>

<!ATTLIST item  maxattempts CDATA  #IMPLIED
                 %I_Label;
                 %I_Ident;
                 %I_Title;
                 xml:lang    CDATA  #IMPLIED >
<!ELEMENT itemmetadata (qtimetadata* , qmd_computerscored? , qmd_feedbackpermitted? , qmd_hintspermitted? , qmd_itemtype? , qmd_levelofdifficulty? , qmd_maximumscore? , qmd_renderingtype* , qmd_responsetype* , qmd_scoringpermitted? , qmd_solutionspermitted? , qmd_status? , qmd_timedependence? , qmd_timelimit? , qmd_toolvendor? , qmd_topic? , qmd_weighting? , qmd_material* , qmd_typeofsolution?)>

<!ELEMENT itemcontrol (qticomment?)>

<!ATTLIST itemcontrol  %I_FeedbackSwitch;
                        %I_HintSwitch;
                        %I_SolutionSwitch;
                        %I_View; >
<!ELEMENT itemprecondition (#PCDATA)>

<!ELEMENT itempostcondition (#PCDATA)>

<!ELEMENT itemrubric (material)>

<!ATTLIST itemrubric  %I_View; >
<!ELEMENT presentation (qticomment? , (flow | (material | response_lid | response_xy | response_str | response_num | response_grp | response_extension)+))>

<!ATTLIST presentation  %I_Label;
                         xml:lang CDATA  #IMPLIED
                         %I_Y0;
                         %I_X0;
                         %I_Width;
                         %I_Height; >
<!ELEMENT flow (flow | material | material_ref | response_lid | response_xy | response_str | response_num | response_grp | response_extension)+>

<!ATTLIST flow  %I_Class; >
<!ELEMENT response_lid ((material | material_ref)? , (render_choice | render_hotspot | render_slider | render_fib | render_extension) , (material | material_ref)?)>

<!ATTLIST response_lid  %I_Rcardinality;
                         %I_Rtiming;
                         %I_Ident; >
<!ELEMENT response_xy ((material | material_ref)? , (render_choice | render_hotspot | render_slider | render_fib | render_extension) , (material | material_ref)?)>

<!ATTLIST response_xy  %I_Rcardinality;
                        %I_Rtiming;
                        %I_Ident; >
<!ELEMENT response_str ((material | material_ref)? , (render_choice | render_hotspot | render_slider | render_fib | render_extension) , (material | material_ref)?)>

<!ATTLIST response_str  %I_Rcardinality;
                         %I_Ident;
                         %I_Rtiming; >
<!ELEMENT response_num ((material | material_ref)? , (render_choice | render_hotspot | render_slider | render_fib | render_extension) , (material | material_ref)?)>

<!ATTLIST response_num  numtype         (Integer | Decimal | Scientific )  'Integer'
                         %I_Rcardinality;
                         %I_Ident;
                         %I_Rtiming; >
<!ELEMENT response_grp ((material | material_ref)? , (render_choice | render_hotspot | render_slider | render_fib | render_extension) , (material | material_ref)?)>

<!ATTLIST response_grp  %I_Rcardinality;
                         %I_Ident;
                         %I_Rtiming; >
<!ELEMENT response_label (#PCDATA | qticomment | material | material_ref | flow_mat)*>

<!ATTLIST response_label  rshuffle     (Yes | No )  'Yes'
                           rarea        (Ellipse | Rectangle | Bounded )  'Ellipse'
                           rrange       (Exact | Range )  'Exact'
                           labelrefid  CDATA  #IMPLIED
                           %I_Ident;
                           match_group CDATA  #IMPLIED
                           match_max   CDATA  #IMPLIED >
<!ELEMENT flow_label (flow_label | response_label)+>

<!ATTLIST flow_label  %I_Class; >
<!ELEMENT response_na ANY>

<!ELEMENT render_choice ((material | material_ref | response_label | flow_label)* , response_na?)>

<!ATTLIST render_choice  shuffle      (Yes | No )  'No'
                          %I_MinNumber;
                          %I_MaxNumber; >
<!ELEMENT render_hotspot ((material | material_ref | response_label | flow_label)* , response_na?)>

<!ATTLIST render_hotspot  %I_MaxNumber;
                           %I_MinNumber;
                           showdraw     (Yes | No )  'No' >
<!ELEMENT render_slider ((material | material_ref | response_label | flow_label)* , response_na?)>

<!ATTLIST render_slider  orientation  (Horizontal | Vertical )  'Horizontal'
                          lowerbound  CDATA  #REQUIRED
                          upperbound  CDATA  #REQUIRED
                          step        CDATA  #IMPLIED
                          startval    CDATA  #IMPLIED
                          steplabel    (Yes | No )  'No'
                          %I_MaxNumber;
                          %I_MinNumber; >
<!ELEMENT render_fib ((material | material_ref | response_label | flow_label)* , response_na?)>

<!ATTLIST render_fib  encoding    CDATA  'UTF_8'
                       fibtype      (String | Integer | Decimal | Scientific )  'String'
                       rows        CDATA  #IMPLIED
                       maxchars    CDATA  #IMPLIED
                       prompt       (Box | Dashline | Asterisk | Underline )  #IMPLIED
                       columns     CDATA  #IMPLIED
                       %I_CharSet;
                       %I_MaxNumber;
                       %I_MinNumber; >
<!ELEMENT resprocessing (qticomment? , outcomes , (respcondition | itemproc_extension)+)>

<!ATTLIST resprocessing  %I_ScoreModel; >
<!ELEMENT outcomes (qticomment? , (decvar , interpretvar*)+)>

<!ELEMENT respcondition (qticomment? , conditionvar , setvar* , displayfeedback* , respcond_extension?)>

<!ATTLIST respcondition  %I_Continue;
                          %I_Title; >
<!ELEMENT itemfeedback ((flow_mat | material) | solution | hint)+>

<!ATTLIST itemfeedback  %I_View;
                         %I_Ident;
                         %I_Title; >
<!ELEMENT solution (qticomment? , solutionmaterial+)>

<!ATTLIST solution  %I_FeedbackStyle; >
<!ELEMENT solutionmaterial (material+ | flow_mat+)>

<!ELEMENT hint (qticomment? , hintmaterial+)>

<!ATTLIST hint  %I_FeedbackStyle; >
<!ELEMENT hintmaterial (material+ | flow_mat+)>

<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--		SELECTION AND ORDERING OBJECT DEFINITIONS				-->
<!--	*******************************************************		-->
<!ELEMENT selection (sourcebank_ref? , selection_number? , selection_metadata? , (and_selection | or_selection | not_selection | selection_extension)?)>

<!ELEMENT order (order_extension?)>

<!ATTLIST order  order_type CDATA  #REQUIRED >
<!ELEMENT selection_number (#PCDATA)>

<!ELEMENT selection_metadata (#PCDATA)>

<!ATTLIST selection_metadata  %I_Mdname;
                               %I_Mdoperator;>
<!ELEMENT sequence_parameter (#PCDATA)>

<!ELEMENT sourcebank_ref (#PCDATA)>

<!ATTLIST sequence_parameter  %I_Pname;>
<!ELEMENT and_selection (selection_metadata | and_selection | or_selection | not_selection)+>

<!ELEMENT or_selection (selection_metadata | and_selection | or_selection | not_selection)+>

<!ELEMENT not_selection (selection_metadata | and_selection | or_selection | not_selection)>

<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--			OUTCOMES PREOCESSING OBJECT DEFINITIONS				-->
<!--	*******************************************************		-->
<!ELEMENT objects_condition (qticomment? , (outcomes_metadata | and_objects | or_objects | not_objects)? , objects_parameter* , map_input* , objectscond_extension?)>

<!ELEMENT map_output (#PCDATA)>

<!ATTLIST map_output  %I_VarName;>
<!ELEMENT map_input (#PCDATA)>

<!ATTLIST map_input  %I_VarName;>
<!ELEMENT outcomes_feedback_test (test_variable , displayfeedback+)>

<!ATTLIST outcomes_feedback_test  %I_Title; >
<!ELEMENT outcomes_metadata (#PCDATA)>

<!ATTLIST outcomes_metadata  %I_Mdname;
                              %I_Mdoperator;>
<!ELEMENT and_objects (outcomes_metadata | and_objects | or_objects | not_objects)+>

<!ELEMENT or_objects (outcomes_metadata | and_objects | or_objects | not_objects)+>

<!ELEMENT not_objects (outcomes_metadata | and_objects | or_objects | not_objects)>

<!ELEMENT test_variable (variable_test | and_test | or_test | not_test)>

<!ELEMENT processing_parameter (#PCDATA)>

<!ATTLIST processing_parameter  %I_Pname;>

<!ELEMENT and_test (variable_test | and_test | or_test | not_test)+>

<!ELEMENT or_test (variable_test | and_test | or_test | not_test)+>

<!ELEMENT not_test (variable_test | and_test | or_test | not_test)>

<!ELEMENT variable_test (#PCDATA)>

<!ATTLIST variable_test  %I_VarName;
                          %I_Testoperator;>
<!ELEMENT objects_parameter (#PCDATA)>

<!ATTLIST objects_parameter  %I_Pname;>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/resources/dtd/ims_qtiresv1p2.dtd b/src/main/java/org/olat/ims/resources/dtd/ims_qtiresv1p2.dtd
new file mode 100644
index 00000000000..3c6fa40423d
--- /dev/null
+++ b/src/main/java/org/olat/ims/resources/dtd/ims_qtiresv1p2.dtd
@@ -0,0 +1 @@
+<?xml version='1.0' encoding='UTF-8' ?>

<!--Generated by XML Authority-->

<!--	*******************************************************		-->
<!--																-->
<!--	TITLE:		ims_qtiresv1p2.dtd								-->
<!--	TYPE:		IMS Question and Test Interoperability			-->
<!--				Results Reporting								-->
<!--																-->
<!--	REVISION HISTORY:											-->
<!--	Date	        Author										-->
<!--	====	        ======										-->
<!--	922th Jan 2002	Colin Smythe								-->
<!--																-->
<!--	This specification has been approved as a Final release.	-->
<!--																-->
<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--					ENTITY DEFINITIONS							-->
<!--	*******************************************************		-->
<!ENTITY % I_Vartype " vartype  (Integer | Decimal | Scientific | String | Boolean | Set | Enumerated )  'Integer'">

<!ENTITY % I_Varname " varname CDATA  'SCORE'">

<!ENTITY % I_Identref " ident_ref CDATA  #IMPLIED">

<!ENTITY % I_Evaltitle " asi_title CDATA  #IMPLIED">

<!ENTITY % I_Presented " presented  (Yes | No | Unknown )  'Yes'">

<!ENTITY % I_Status " status  (Valid | Noanswer | Error )  'Valid'">

<!ENTITY % I_Uri " uri CDATA  #IMPLIED">

<!ENTITY % I_Entityref " entityref ENTITY  #IMPLIED">

<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->
<!--	*******************************************************		-->
<!--					ROOT DEFINITION								-->
<!--	*******************************************************		-->
<!ELEMENT qti_result_report (qti_comment? , result+)>

<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->

<!ELEMENT qti_comment (#PCDATA)>

<!ATTLIST qti_comment  xml:lang CDATA  #IMPLIED >

<!--	*******************************************************		-->
<!--				CORE OBJECT DEFINITIONS							-->
<!--	*******************************************************		-->
<!ELEMENT result (qti_comment? , context , (summary_result | assessment_result | section_result | item_result) , extension_result?)>

<!ELEMENT context (qti_comment? , name? , generic_identifier* , date* , extension_context?)>

<!ELEMENT summary_result (qti_comment? , type_label? , generic_identifier* , date* , status? , duration? , score? , grade? , outcomes? , extension_summary_result?)>

<!ATTLIST summary_result  %I_Evaltitle; >
<!ELEMENT assessment_result (qti_comment? , asi_metadata* , asi_description? , date* , duration? , objective* , control? , outcomes? , feedback_displayed* , num_items? , num_sections? , num_items_presented? , num_items_attempted? , num_sections_presented? , section_result* , extension_assessment_result?)>

<!ATTLIST assessment_result  %I_Identref;
                              %I_Evaltitle; >
<!ELEMENT section_result (qti_comment? , asi_metadata* , asi_description? , date* , duration? , objective* , control? , outcomes? , feedback_displayed* , num_items? , num_sections? , num_items_presented? , num_items_attempted? , num_sections_presented? , (section_result | item_result)* , extension_section_result?)>

<!ATTLIST section_result  %I_Evaltitle;
                           %I_Identref;
                           %I_Presented; >
<!ELEMENT item_result (qti_comment? , asi_metadata* , asi_description? , date* , duration? , objective* , control? , response* , outcomes? , feedback_displayed* , extension_item_result?)>

<!ATTLIST item_result  %I_Evaltitle;
                        %I_Identref;
                        %I_Presented; >
<!--	+++++++++++++++++++++++++++++++++++++++++++++++++++++++		-->

<!--	*******************************************************		-->
<!--				SUPPORT OBJECT DEFINITIONS						-->
<!--	*******************************************************		-->

<!ELEMENT name (#PCDATA)>

<!ELEMENT generic_identifier (type_label? , identifier_string)>

<!ELEMENT date (type_label? , datetime)>

<!ELEMENT score_min (#PCDATA)>

<!ELEMENT score_max (#PCDATA)>

<!ELEMENT score_value (#PCDATA)>

<!ELEMENT grade (grade_value , grade_cut? , extension_grade?)>

<!ATTLIST grade  %I_Varname;
                  %I_Status;
                  members   CDATA  #IMPLIED >
<!ELEMENT duration (#PCDATA)>

<!ELEMENT type_label (#PCDATA)>

<!ATTLIST type_label  source CDATA  #IMPLIED >
<!ELEMENT status (type_label? , datetime? , status_value)>

<!ELEMENT objective (#PCDATA)>

<!ATTLIST objective  view         (All | 
                                    Administrator | 
                                    AdminAuthority | 
                                    Assessor | 
                                    Author | 
                                    Candidate | 
                                    InvigilatorProctor | 
                                    Psychometrician | 
                                    Scorer | 
                                    Tutor )  'All'
                      %I_Uri;
                      %I_Entityref; >
<!ELEMENT control (#PCDATA)>

<!ATTLIST control  hint_switch      (Yes | No )  'No'
                    solution_switch  (Yes | No )  'No'
                    feedback_switch  (Yes | No )  'No' >
<!ELEMENT asi_metadata (vocabulary? , asi_metadatafield+)>

<!ELEMENT response (qti_comment? , response_form? , num_attempts? , response_value* , extension_response?)>

<!ATTLIST response  %I_Identref; >
<!ELEMENT response_value (#PCDATA)>

<!ATTLIST response_value  %I_Uri;
                           %I_Entityref;
                           response_status  (Null | Valid | NA | Invalid )  'Valid'
                           response_time   CDATA  #IMPLIED >
<!ELEMENT feedback_displayed (#PCDATA)>

<!ATTLIST feedback_displayed  %I_Uri;
                               %I_Entityref;
                               %I_Identref;
                               %I_Evaltitle; >
<!ELEMENT num_items (#PCDATA)>

<!ELEMENT num_sections (#PCDATA)>

<!ELEMENT num_sections_presented (#PCDATA)>

<!ELEMENT num_items_attempted (#PCDATA)>

<!ELEMENT num_items_presented (#PCDATA)>

<!ELEMENT response_form (correct_response* , extension_responseform?)>

<!ATTLIST response_form  cardinality    (single | multiple | ordered | extension )  #IMPLIED
                          render_type    (choice | hotspot | slider | fib | extension )  #IMPLIED
                          timing         (Yes | No )  #IMPLIED
                          response_type  (lid | xy | str | num | grp | extension )  #IMPLIED >
<!ELEMENT correct_response (#PCDATA)>

<!ELEMENT outcomes (status? , (score | grade)*)>

<!ELEMENT score (score_value , score_interpretation? , score_min? , score_max? , score_normalized? , score_average? , score_std_error? , score_reliability? , score_cut? , extension_score?)>

<!ATTLIST score  %I_Varname;
                  %I_Vartype;
                  %I_Status; >
<!ELEMENT score_cut (#PCDATA)>

<!ELEMENT grade_value (#PCDATA)>

<!ELEMENT grade_cut (#PCDATA)>

<!ELEMENT score_average (#PCDATA)>

<!ELEMENT score_std_error (#PCDATA)>

<!ELEMENT num_attempts (#PCDATA)>

<!ELEMENT asi_description (#PCDATA)>

<!ATTLIST asi_description  %I_Uri;
                            %I_Entityref; >
<!ELEMENT identifier_string (#PCDATA)>

<!ELEMENT datetime (#PCDATA)>

<!ELEMENT status_value (#PCDATA)>

<!ELEMENT vocabulary (#PCDATA)>

<!ATTLIST vocabulary  %I_Uri;
                       %I_Entityref;
                       vocab_type  CDATA  #IMPLIED >
                       
<!ELEMENT asi_metadatafield (field_name , field_value)>

<!ATTLIST asi_metadatafield  xml:lang CDATA  #IMPLIED >
<!ELEMENT field_name (#PCDATA)>

<!ELEMENT field_value (#PCDATA)>

<!ELEMENT score_reliability (#PCDATA)>

<!ELEMENT score_interpretation (#PCDATA)>

<!ATTLIST score_interpretation  %I_Uri;
                                 %I_Entityref; >
                                 
<!--	*******************************************************		-->
<!--					EXTENSION DEFINITIONS						-->
<!--	*******************************************************		-->
<!ELEMENT extension_context ANY>

<!ELEMENT extension_assessment_result ANY>

<!ELEMENT extension_section_result ANY>

<!ELEMENT extension_score ANY>

<!ELEMENT extension_summary_result ANY>

<!ELEMENT extension_item_result ANY>

<!ELEMENT extension_response ANY>

<!ELEMENT extension_responseform ANY>

<!ELEMENT extension_grade ANY>

<!ELEMENT extension_result ANY>

<!ELEMENT score_normalized (#PCDATA)>

\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/qpool/QuestionItem.java b/src/main/java/org/olat/modules/qpool/QuestionItem.java
index 3fe974ff063..ed6057940ed 100644
--- a/src/main/java/org/olat/modules/qpool/QuestionItem.java
+++ b/src/main/java/org/olat/modules/qpool/QuestionItem.java
@@ -37,6 +37,7 @@ public interface QuestionItem extends QuestionItemShort {
 	
 	public String getLevel();
 	
+	public String getDirectory();
 	
 	public String getEditor();
 	
diff --git a/src/main/java/org/olat/modules/qpool/QuestionItemShort.java b/src/main/java/org/olat/modules/qpool/QuestionItemShort.java
index fe8aad00a21..b53b5c8090a 100644
--- a/src/main/java/org/olat/modules/qpool/QuestionItemShort.java
+++ b/src/main/java/org/olat/modules/qpool/QuestionItemShort.java
@@ -34,6 +34,8 @@ public interface QuestionItemShort extends OLATResourceable {
 	
 	public Long getKey();
 	
+	public String getUuid();
+	
 	public Date getCreationDate();
 	
 	public Date getLastModified();
diff --git a/src/main/java/org/olat/modules/qpool/QuestionPoolModule.java b/src/main/java/org/olat/modules/qpool/QuestionPoolModule.java
index 15b9e5e2278..9f4485b891f 100644
--- a/src/main/java/org/olat/modules/qpool/QuestionPoolModule.java
+++ b/src/main/java/org/olat/modules/qpool/QuestionPoolModule.java
@@ -19,12 +19,18 @@
  */
 package org.olat.modules.qpool;
 
+import java.io.File;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
+import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
 import org.olat.core.configuration.AbstractOLATModule;
 import org.olat.core.configuration.ConfigOnOff;
 import org.olat.core.configuration.PersistedProperties;
+import org.olat.core.util.vfs.VFSContainer;
+import org.olat.modules.qpool.impl.PdfQuestionPoolServiceProvider;
+import org.olat.modules.qpool.impl.TextQuestionPoolServiceProvider;
 
 /**
  * 
@@ -36,9 +42,12 @@ public class QuestionPoolModule extends AbstractOLATModule implements ConfigOnOf
 	
 	private final List<QuestionPoolSPI> questionPoolProviders = new ArrayList<QuestionPoolSPI>();
 
+	private VFSContainer rootContainer;
+
 	@Override
 	public void init() {
-		//
+		addQuestionPoolProvider(new TextQuestionPoolServiceProvider());
+		addQuestionPoolProvider(new PdfQuestionPoolServiceProvider());
 	}
 
 	@Override
@@ -60,14 +69,25 @@ public class QuestionPoolModule extends AbstractOLATModule implements ConfigOnOf
 	public boolean isEnabled() {
 		return true;
 	}
+	
+	public VFSContainer getRootContainer() {
+		if(rootContainer == null) {
+			rootContainer = new OlatRootFolderImpl(File.separator + "qpool", null);
+		}
+		return rootContainer;
+	}
 
 	public List<QuestionPoolSPI> getQuestionPoolProviders() {
-		return new ArrayList<QuestionPoolSPI>(questionPoolProviders);
+		List<QuestionPoolSPI> providers = new ArrayList<QuestionPoolSPI>(questionPoolProviders);
+		Collections.sort(providers, new QuestionPoolSPIComparator());
+		return providers;
 	}
 
 	public void setQuestionPoolProviders(List<QuestionPoolSPI> providers) {
 		if(providers != null) {
-			questionPoolProviders.addAll(providers);
+			for(QuestionPoolSPI provider:providers) {
+				addQuestionPoolProvider(provider);
+			}
 		}
 	}
 	
@@ -81,8 +101,19 @@ public class QuestionPoolModule extends AbstractOLATModule implements ConfigOnOf
 	}
 	
 	public void addQuestionPoolProvider(QuestionPoolSPI provider) {
-		questionPoolProviders.add(provider);
+		int currentIndex = -1;
+		for(int i=questionPoolProviders.size(); i-->0; ) {
+			QuestionPoolSPI currentProvider = questionPoolProviders.get(i);
+			if(provider.getFormat() != null &&
+					provider.getFormat().equals(currentProvider.getFormat())) {
+				currentIndex = i;
+			}
+		}
+		
+		if(currentIndex >= 0) {
+			questionPoolProviders.set(currentIndex, provider);
+		} else {
+			questionPoolProviders.add(provider);
+		}
 	}
-
-
 }
diff --git a/src/main/java/org/olat/modules/qpool/QuestionPoolSPI.java b/src/main/java/org/olat/modules/qpool/QuestionPoolSPI.java
index 82099f5bfe9..877e85b0667 100644
--- a/src/main/java/org/olat/modules/qpool/QuestionPoolSPI.java
+++ b/src/main/java/org/olat/modules/qpool/QuestionPoolSPI.java
@@ -19,9 +19,12 @@
  */
 package org.olat.modules.qpool;
 
+import java.io.File;
+
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.core.util.vfs.VFSLeaf;
 
 /**
  * 
@@ -31,8 +34,14 @@ import org.olat.core.gui.control.WindowControl;
  */
 public interface QuestionPoolSPI {
 	
+	public int getPriority();
+	
 	public String getFormat();
 	
+	public boolean isCompatible(String filename, File file);
+	
+	public boolean isCompatible(String filename, VFSLeaf file);
+	
 	public Controller getPreviewController(UserRequest ureq, WindowControl wControl, QuestionItem item);
 
 	public Controller getEditableController(UserRequest ureq, WindowControl wControl, QuestionItem item);
diff --git a/src/main/java/org/olat/modules/qpool/QuestionPoolSPIComparator.java b/src/main/java/org/olat/modules/qpool/QuestionPoolSPIComparator.java
new file mode 100644
index 00000000000..69f7f0f5890
--- /dev/null
+++ b/src/main/java/org/olat/modules/qpool/QuestionPoolSPIComparator.java
@@ -0,0 +1,38 @@
+/**
+ * <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.modules.qpool;
+
+import java.util.Comparator;
+
+/**
+ * Sort by priority
+ * Initial date: 27.02.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class QuestionPoolSPIComparator implements Comparator<QuestionPoolSPI> {
+
+	@Override
+	public int compare(QuestionPoolSPI o1, QuestionPoolSPI o2) {
+		int p1 = o1.getPriority();
+		int p2 = o2.getPriority();
+		return p1 < p2 ? 1 : (p1==p2 ? 0 : -1);
+	}
+}
diff --git a/src/main/java/org/olat/modules/qpool/QuestionPoolService.java b/src/main/java/org/olat/modules/qpool/QuestionPoolService.java
index 45a4c105d8c..43819bf5310 100644
--- a/src/main/java/org/olat/modules/qpool/QuestionPoolService.java
+++ b/src/main/java/org/olat/modules/qpool/QuestionPoolService.java
@@ -19,10 +19,13 @@
  */
 package org.olat.modules.qpool;
 
+import java.io.File;
 import java.util.List;
 
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
+import org.olat.core.util.vfs.VFSContainer;
+import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.group.BusinessGroup;
 import org.olat.resource.OLATResource;
 
@@ -40,6 +43,12 @@ public interface QuestionPoolService {
 	
 	public void addAuthors(List<Identity> authors, List<QuestionItem> items);
 	
+	public QuestionItem importItem(Identity owner, String filename, File file);
+	
+	public VFSLeaf getRootFile(QuestionItem item);
+	
+	public VFSContainer getRootDirectory(QuestionItem item);
+	
 
 	public int countItems(Identity author);
 
diff --git a/src/main/java/org/olat/modules/qpool/impl/PdfQuestionPoolServiceProvider.java b/src/main/java/org/olat/modules/qpool/impl/PdfQuestionPoolServiceProvider.java
new file mode 100644
index 00000000000..87e64c5631b
--- /dev/null
+++ b/src/main/java/org/olat/modules/qpool/impl/PdfQuestionPoolServiceProvider.java
@@ -0,0 +1,76 @@
+/**
+ * <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.modules.qpool.impl;
+
+import java.io.File;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.util.vfs.VFSLeaf;
+import org.olat.modules.qpool.QuestionItem;
+import org.olat.modules.qpool.QuestionPoolSPI;
+
+/**
+ * 
+ * Initial date: 26.02.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class PdfQuestionPoolServiceProvider implements QuestionPoolSPI {
+	
+	public static final String PDF_FORMAT = "pdf";
+
+	@Override
+	public int getPriority() {
+		return 0;
+	}
+
+	@Override
+	public String getFormat() {
+		return PDF_FORMAT;
+	}
+
+	@Override
+	public boolean isCompatible(String filename, File file) {
+		return !filename.toLowerCase().endsWith(".xml")
+				&& !filename.toLowerCase().endsWith(".txt")
+				&& !filename.toLowerCase().endsWith(".zip");
+	}
+
+	@Override
+	public boolean isCompatible(String filename, VFSLeaf file) {
+		return isCompatible(filename, (File)null);
+	}
+
+	@Override
+	public Controller getPreviewController(UserRequest ureq, WindowControl wControl, QuestionItem item) {
+		return null;
+	}
+
+	@Override
+	public Controller getEditableController(UserRequest ureq,	WindowControl wControl, QuestionItem item) {
+		return null;
+	}
+
+
+	
+
+}
diff --git a/src/main/java/org/olat/modules/qpool/impl/TextQuestionPoolServiceProvider.java b/src/main/java/org/olat/modules/qpool/impl/TextQuestionPoolServiceProvider.java
new file mode 100644
index 00000000000..efd6b386755
--- /dev/null
+++ b/src/main/java/org/olat/modules/qpool/impl/TextQuestionPoolServiceProvider.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.modules.qpool.impl;
+
+import java.io.File;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.util.vfs.VFSLeaf;
+import org.olat.modules.qpool.QuestionItem;
+import org.olat.modules.qpool.QuestionPoolSPI;
+
+/**
+ * 
+ * Initial date: 26.02.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class TextQuestionPoolServiceProvider implements QuestionPoolSPI {
+
+	public static final String TXT_FORMAT = "txt";
+	
+	@Override
+	public int getPriority() {
+		return 1;
+	}
+
+	@Override
+	public String getFormat() {
+		return TXT_FORMAT;
+	}
+	
+	@Override
+	public boolean isCompatible(String filename, File file) {
+		return filename.toLowerCase().endsWith(".txt");
+	}
+
+	@Override
+	public boolean isCompatible(String filename, VFSLeaf file) {
+		return isCompatible(filename, (File)null);
+	}
+
+	@Override
+	public Controller getPreviewController(UserRequest ureq, WindowControl wControl, QuestionItem item) {
+		return null;
+	}
+
+	@Override
+	public Controller getEditableController(UserRequest ureq, WindowControl wControl, QuestionItem item) {
+		return null;
+	}
+}
diff --git a/src/main/java/org/olat/modules/qpool/manager/NullPoolService.java b/src/main/java/org/olat/modules/qpool/manager/NullPoolService.java
index a1be17f554d..685691003ba 100644
--- a/src/main/java/org/olat/modules/qpool/manager/NullPoolService.java
+++ b/src/main/java/org/olat/modules/qpool/manager/NullPoolService.java
@@ -94,7 +94,7 @@ public class NullPoolService implements ApplicationListener<ContextRefreshedEven
 			for(int i=0; i<200; i++) {
 				long randomIndex = Math.round(Math.random() * (fields.size() - 1));
 				StudyField field = fields.get((int)randomIndex);
-				QuestionItem item = questionItemDao.create(null, "NGC " + i, QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), field, randomType());
+				QuestionItem item = questionItemDao.create(null, "NGC " + i, QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), field, null, randomType());
 				poolDao.addItemToPool(item, pools.get(0));
 			}
 		}
diff --git a/src/main/java/org/olat/modules/qpool/manager/QuestionItemDAO.java b/src/main/java/org/olat/modules/qpool/manager/QuestionItemDAO.java
index 2375e92a533..935de6092c2 100644
--- a/src/main/java/org/olat/modules/qpool/manager/QuestionItemDAO.java
+++ b/src/main/java/org/olat/modules/qpool/manager/QuestionItemDAO.java
@@ -22,6 +22,7 @@ package org.olat.modules.qpool.manager;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.UUID;
 
 import javax.persistence.EntityManager;
 import javax.persistence.LockModeType;
@@ -59,18 +60,33 @@ public class QuestionItemDAO {
 	@Autowired
 	private BaseSecurity securityManager;
 	
+	
 	public QuestionItem create(Identity owner, String subject, String format, String language,
-			StudyField field, QuestionType type) {
+			StudyField field, String rootFilename, QuestionType type) {
+
+		String uuid = UUID.randomUUID().toString();
+		return create(owner, subject, format, language, field, uuid, rootFilename, type) ;
+	}
+	
+	public QuestionItem create(Identity owner, String subject, String format, String language,
+			StudyField field, String uuid, String rootFilename, QuestionType type) {
 		QuestionItemImpl item = new QuestionItemImpl();
+		
+		
+		item.setUuid(uuid);
 		item.setCreationDate(new Date());
 		item.setLastModified(new Date());
 		item.setSubject(subject);
 		item.setStatus(QuestionStatus.inWork.name());
 		item.setUsage(0);
-		item.setType(type.name());
+		if(type != null) {
+			item.setType(type.name());
+		}
 		item.setFormat(format);
 		item.setLanguage(language);
 		item.setStudyField(field);
+		item.setDirectory(generateDir(uuid));
+		item.setRootFilename(rootFilename);
 		SecurityGroup authorGroup = securityManager.createAndPersistSecurityGroup();
 		item.setAuthorGroup(authorGroup);
 		dbInstance.getCurrentEntityManager().persist(item);
@@ -80,6 +96,21 @@ public class QuestionItemDAO {
 		return item;
 	}
 	
+	public String generateDir(String uuid) {
+		String cleanUuid = uuid.replace("-", "");
+		String firstToken = cleanUuid.substring(0, 2);
+		String secondToken = cleanUuid.substring(2, 4);
+		String thirdToken = cleanUuid.substring(4, 6);
+		String forthToken = cleanUuid.substring(6, 8);
+		StringBuilder sb = new StringBuilder();
+		sb.append(firstToken).append("/")
+		  .append(secondToken).append("/")
+		  .append(thirdToken).append("/")
+		  .append(forthToken).append("/");
+		return sb.toString();
+	}
+	
+	
 	public void addAuthors(List<Identity> authors, QuestionItem item) {
 		QuestionItemImpl lockedItem = loadForUpdate(item.getKey());
 		SecurityGroup secGroup = lockedItem.getAuthorGroup();
@@ -149,11 +180,11 @@ public class QuestionItemDAO {
 		}
 	}
 	
-	public QuestionItem loadById(Long key) {
+	public QuestionItemImpl loadById(Long key) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select item from questionitem item where item.key=:key");
-		List<QuestionItem> items = dbInstance.getCurrentEntityManager()
-				.createQuery(sb.toString(), QuestionItem.class)
+		List<QuestionItemImpl> items = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), QuestionItemImpl.class)
 				.setParameter("key", key)
 				.getResultList();
 		
diff --git a/src/main/java/org/olat/modules/qpool/manager/QuestionPoolServiceImpl.java b/src/main/java/org/olat/modules/qpool/manager/QuestionPoolServiceImpl.java
index c7cb6c619c2..aea658ec407 100644
--- a/src/main/java/org/olat/modules/qpool/manager/QuestionPoolServiceImpl.java
+++ b/src/main/java/org/olat/modules/qpool/manager/QuestionPoolServiceImpl.java
@@ -19,16 +19,32 @@
  */
 package org.olat.modules.qpool.manager;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.UUID;
 
+import org.apache.commons.io.IOUtils;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.ZipUtil;
+import org.olat.core.util.vfs.VFSContainer;
+import org.olat.core.util.vfs.VFSItem;
+import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.group.BusinessGroup;
 import org.olat.modules.qpool.Pool;
 import org.olat.modules.qpool.QuestionItem;
 import org.olat.modules.qpool.QuestionItemCollection;
+import org.olat.modules.qpool.QuestionPoolModule;
+import org.olat.modules.qpool.QuestionPoolSPI;
 import org.olat.modules.qpool.QuestionPoolService;
 import org.olat.modules.qpool.model.QuestionItemImpl;
 import org.olat.resource.OLATResource;
@@ -44,6 +60,8 @@ import org.springframework.stereotype.Service;
 @Service("qpoolService")
 public class QuestionPoolServiceImpl implements QuestionPoolService {
 	
+	private static final OLog log = Tracing.createLoggerFor(QuestionPoolServiceImpl.class);
+	
 	@Autowired
 	private DB dbInstance;
 	@Autowired
@@ -54,6 +72,9 @@ public class QuestionPoolServiceImpl implements QuestionPoolService {
 	private StudyFieldDAO studyFieldDao;
 	@Autowired
 	private QuestionItemDAO questionItemDao;
+	@Autowired
+	private QuestionPoolModule qpoolModule;
+	
 
 	@Override
 	public String getMateriliazedPathOfStudyFields(QuestionItem item) {
@@ -84,6 +105,113 @@ public class QuestionPoolServiceImpl implements QuestionPoolService {
 		}
 	}
 
+	@Override
+	public QuestionItem importItem(Identity owner, String filename, File file) {
+		QuestionItem importedItem = null;
+		List<QuestionPoolSPI> providers = qpoolModule.getQuestionPoolProviders();
+		for(QuestionPoolSPI provider:providers) {
+			if(provider.isCompatible(filename, file)) {
+				importedItem = importItem(owner, filename, file, provider);
+			}	
+		}
+		return importedItem;
+	}
+	
+	@Override
+	public VFSLeaf getRootFile(QuestionItem item) {
+		QuestionItemImpl reloadedItem = questionItemDao.loadById(item.getKey());
+		VFSContainer root = qpoolModule.getRootContainer();
+		VFSItem dir = root.resolve(reloadedItem.getDirectory());
+		if(dir instanceof VFSContainer) {
+			VFSContainer itemContainer = (VFSContainer)dir;
+			VFSItem rootLeaf = itemContainer.resolve(reloadedItem.getRootFilename());
+			if(rootLeaf instanceof VFSLeaf) {
+				return (VFSLeaf)rootLeaf;
+			}
+		}
+		return null;
+	}
+
+	@Override
+	public VFSContainer getRootDirectory(QuestionItem item) {
+		QuestionItemImpl reloadedItem = questionItemDao.loadById(item.getKey());
+		VFSContainer root = qpoolModule.getRootContainer();
+		VFSItem dir = root.resolve(reloadedItem.getDirectory());
+		if(dir instanceof VFSContainer) {
+			return (VFSContainer)dir;
+		}
+		return null;
+	}
+
+	private QuestionItem importItem(Identity owner, String filename, File file, QuestionPoolSPI provider) {
+		String uuid = UUID.randomUUID().toString();
+		VFSContainer root = qpoolModule.getRootContainer();
+		VFSContainer itemDir = getDirectory(root, uuid);
+
+		String rootFilename = filename;
+		if(filename.toLowerCase().endsWith(".zip")) {
+			ZipUtil.unzipStrict(file, itemDir);
+			rootFilename = searchRootFilename("", itemDir, provider);
+		} else {
+			//copy
+			VFSLeaf leaf = itemDir.createChildLeaf(filename);
+			OutputStream out = leaf.getOutputStream(false);
+			InputStream in = null;
+			try {
+				in = new FileInputStream(file);
+				IOUtils.copy(in, out);
+			} catch (FileNotFoundException e) {
+				log.error("", e);
+			} catch (IOException e) {
+				log.error("", e);
+			} finally {
+				IOUtils.closeQuietly(in);
+				IOUtils.closeQuietly(out);
+			}
+		}
+		
+		return questionItemDao.create(owner, filename, provider.getFormat(), "de", null, uuid, rootFilename, null);
+	}
+	
+	private String searchRootFilename(String path, VFSContainer dir, QuestionPoolSPI provider) {
+		for(VFSItem item:dir.getItems()) {
+			if(item instanceof VFSContainer) {
+				String root = searchRootFilename(path + "/" + item.getName(), (VFSContainer)item, provider);
+				if(root != null) {
+					return root;
+				}
+			} else if(item instanceof VFSLeaf) {
+				if(provider.isCompatible(item.getName(), (VFSLeaf)item)) {
+					return path + item.getName();
+				}
+			}
+		}
+		return null;
+	}
+	
+	private VFSContainer getDirectory(VFSContainer rootContainer, String uuid) {
+		String cleanUuid = uuid.replace("-", "");
+		String firstToken = cleanUuid.substring(0, 2);
+		VFSContainer firstContainer = getNextDirectory(rootContainer, firstToken);
+		String secondToken = cleanUuid.substring(2, 4);
+		VFSContainer secondContainer = getNextDirectory(firstContainer, secondToken);
+		String thirdToken = cleanUuid.substring(4, 6);
+		VFSContainer thridContainer = getNextDirectory(secondContainer, thirdToken);
+		String forthToken = cleanUuid.substring(6, 8);
+		return getNextDirectory(thridContainer, forthToken);
+	}
+	
+	private VFSContainer getNextDirectory(VFSContainer container, String token) {
+		VFSItem nextContainer = container.resolve(token);
+		if(nextContainer instanceof VFSContainer) {
+			return (VFSContainer)nextContainer;
+		} else if (nextContainer instanceof VFSLeaf) {
+			log.error("");
+			return null;
+		}
+		return container.createChildContainer(token);
+	}
+
 	@Override
 	public int countItems(Identity author) {
 		return questionItemDao.countItems(author);
diff --git a/src/main/java/org/olat/modules/qpool/manager/StudyFieldDAO.java b/src/main/java/org/olat/modules/qpool/manager/StudyFieldDAO.java
index 6d89ea04122..160ce247c7b 100644
--- a/src/main/java/org/olat/modules/qpool/manager/StudyFieldDAO.java
+++ b/src/main/java/org/olat/modules/qpool/manager/StudyFieldDAO.java
@@ -87,6 +87,10 @@ public class StudyFieldDAO {
 	}
 
 	public String getMaterializedPath(StudyField field) {
+		if(field == null) {
+			return "";
+		}
+		
 		List<StudyField> parentLine = new ArrayList<StudyField>();
 		
 		StringBuilder sb = new StringBuilder();
diff --git a/src/main/java/org/olat/modules/qpool/model/QuestionItemImpl.java b/src/main/java/org/olat/modules/qpool/model/QuestionItemImpl.java
index 7d499ccb7f7..af13a894d17 100644
--- a/src/main/java/org/olat/modules/qpool/model/QuestionItemImpl.java
+++ b/src/main/java/org/olat/modules/qpool/model/QuestionItemImpl.java
@@ -67,6 +67,8 @@ public class QuestionItemImpl implements QuestionItem, CreateInfo, ModifiedInfo,
   @GenericGenerator(name = "system-uuid", strategy = "hilo")
 	@Column(name="id", nullable=false, unique=true, insertable=true, updatable=false)
 	private Long key;
+	@Column(name="q_uuid", nullable=false, insertable=true, updatable=false)
+	private String uuid;
 
 	//general
 	@Column(name="q_subject", nullable=false, insertable=true, updatable=true)
@@ -119,8 +121,12 @@ public class QuestionItemImpl implements QuestionItem, CreateInfo, ModifiedInfo,
 	private Date lastModified;
 	@Column(name="q_version", nullable=true, insertable=true, updatable=true)
 	private String itemVersion;
-
-
+	
+	@Column(name="q_dir", nullable=true, insertable=true, updatable=false)
+	private String directory;
+	@Column(name="q_root_filename", nullable=true, insertable=true, updatable=false)
+	private String rootFilename;
+	
 
 	@Override
 	public Long getKey() {
@@ -131,6 +137,15 @@ public class QuestionItemImpl implements QuestionItem, CreateInfo, ModifiedInfo,
 		this.key = key;
 	}
 
+	@Override
+	public String getUuid() {
+		return uuid;
+	}
+
+	public void setUuid(String uuid) {
+		this.uuid = uuid;
+	}
+
 	@Override
 	public String getResourceableTypeName() {
 		return "QuestionItem";
@@ -320,6 +335,22 @@ public class QuestionItemImpl implements QuestionItem, CreateInfo, ModifiedInfo,
 		this.itemVersion = itemVersion;
 	}
 
+	public String getDirectory() {
+		return directory;
+	}
+
+	public void setDirectory(String directory) {
+		this.directory = directory;
+	}
+
+	public String getRootFilename() {
+		return rootFilename;
+	}
+
+	public void setRootFilename(String rootFilename) {
+		this.rootFilename = rootFilename;
+	}
+
 	@Override
 	public int hashCode() {
 		return key == null ? 97489 : key.hashCode();
diff --git a/src/main/java/org/olat/modules/qpool/ui/ImportQuestionItemController.java b/src/main/java/org/olat/modules/qpool/ui/ImportQuestionItemController.java
new file mode 100644
index 00000000000..1e305b301a1
--- /dev/null
+++ b/src/main/java/org/olat/modules/qpool/ui/ImportQuestionItemController.java
@@ -0,0 +1,84 @@
+/**
+ * <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.modules.qpool.ui;
+
+import java.io.File;
+
+import org.olat.core.CoreSpringFactory;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.FileElement;
+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.Event;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.modules.qpool.QuestionPoolService;
+
+/**
+ * 
+ * Initial date: 26.02.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class ImportQuestionItemController extends FormBasicController {
+	
+	private FileElement fileEl;
+	private final QuestionPoolService qpoolservice;
+	
+	public ImportQuestionItemController(UserRequest ureq, WindowControl wControl) {
+		super(ureq, wControl);
+		qpoolservice = CoreSpringFactory.getImpl(QuestionPoolService.class);
+		initForm(ureq);
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		fileEl = uifactory.addFileElement("item", "import.item", formLayout);
+		
+		FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
+		buttonsCont.setRootForm(mainForm);
+		formLayout.add(buttonsCont);
+		uifactory.addFormSubmitButton("ok", "ok", buttonsCont);
+		uifactory.addFormCancelButton("cancel", buttonsCont, ureq, getWindowControl());
+	}
+	
+	@Override
+	protected void doDispose() {
+		//
+	}
+	
+	public File getFile() {
+		return fileEl.getUploadFile();
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		String filename = fileEl.getUploadFileName();
+		File file = fileEl.getUploadFile();
+		qpoolservice.importItem(getIdentity(), filename, file);
+		fireEvent(ureq, Event.DONE_EVENT);
+	}
+
+	@Override
+	protected void formCancelled(UserRequest ureq) {
+		fireEvent(ureq, Event.CANCELLED_EVENT);
+	}
+}
diff --git a/src/main/java/org/olat/modules/qpool/ui/QuestionItemRow.java b/src/main/java/org/olat/modules/qpool/ui/QuestionItemRow.java
index 48ae0df36c4..026b4f3cd10 100644
--- a/src/main/java/org/olat/modules/qpool/ui/QuestionItemRow.java
+++ b/src/main/java/org/olat/modules/qpool/ui/QuestionItemRow.java
@@ -48,6 +48,11 @@ public class QuestionItemRow implements QuestionItemShort {
 		return delegate.getKey();
 	}
 
+	@Override
+	public String getUuid() {
+		return delegate.getUuid();
+	}
+
 	@Override
 	public String getResourceableTypeName() {
 		return delegate.getResourceableTypeName();
diff --git a/src/main/java/org/olat/modules/qpool/ui/QuestionListController.java b/src/main/java/org/olat/modules/qpool/ui/QuestionListController.java
index 3d859aefc49..142661ae58e 100644
--- a/src/main/java/org/olat/modules/qpool/ui/QuestionListController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/QuestionListController.java
@@ -69,7 +69,7 @@ import org.olat.modules.qpool.ui.QuestionItemDataModel.Cols;
  */
 public class QuestionListController extends FormBasicController implements StackedControllerAware, ItemRowsSource {
 
-	private FormLink createList, shareItem, deleteItem, authorItem;
+	private FormLink createList, shareItem, deleteItem, authorItem, importItem;
 	
 	private FlexiTableElement itemsTable;
 	private QuestionItemDataModel model;
@@ -80,6 +80,7 @@ public class QuestionListController extends FormBasicController implements Stack
 	private SelectBusinessGroupController selectGroupCtrl;
 	private CreateCollectionController createCollectionCtrl;
 	private StepsMainRunController importAuthorsWizard;
+	private ImportQuestionItemController importItemCtrl;
 	
 	private final MarkManager markManager;
 	private final QuestionPoolService qpoolService;
@@ -121,6 +122,7 @@ public class QuestionListController extends FormBasicController implements Stack
 		
 		createList = uifactory.addFormLink("create.list", formLayout, Link.BUTTON);
 		shareItem = uifactory.addFormLink("share.item", formLayout, Link.BUTTON);
+		importItem = uifactory.addFormLink("import.item", formLayout, Link.BUTTON);
 		authorItem = uifactory.addFormLink("author.item", formLayout, Link.BUTTON);
 		deleteItem = uifactory.addFormLink("delete.item", formLayout, Link.BUTTON);
 	}
@@ -170,6 +172,8 @@ public class QuestionListController extends FormBasicController implements Stack
 					List<QuestionItem> items = getQuestionItems(selections);
 					doChooseAuthoren(ureq, items);
 				}
+			} else if(link == importItem) {
+				doOpenImport(ureq);
 			} else if("select".equals(link.getCmd())) {
 				QuestionItemRow row = (QuestionItemRow)link.getUserObject();
 				doSelect(ureq, row.getItem());
@@ -226,6 +230,12 @@ public class QuestionListController extends FormBasicController implements Stack
 				removeAsListenerAndDispose(importAuthorsWizard);
 				importAuthorsWizard = null;
 			}
+		} else if(source == importItemCtrl) {
+			if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
+				//
+			}
+			cmc.deactivate();
+			cleanUp();
 		} else if(source == confirmDeleteBox) {
 			boolean delete = DialogBoxUIFactory.isYesEvent(event) || DialogBoxUIFactory.isOkEvent(event);
 			if(delete) {
@@ -241,9 +251,11 @@ public class QuestionListController extends FormBasicController implements Stack
 	
 	private void cleanUp() {
 		removeAsListenerAndDispose(cmc);
+		removeAsListenerAndDispose(importItemCtrl);
 		removeAsListenerAndDispose(selectGroupCtrl);
 		removeAsListenerAndDispose(createCollectionCtrl);
 		cmc = null;
+		importItemCtrl = null;
 		selectGroupCtrl = null;
 		createCollectionCtrl = null;
 	}
@@ -267,6 +279,17 @@ public class QuestionListController extends FormBasicController implements Stack
 		return null;
 	}
 	
+	private void doOpenImport(UserRequest ureq) {
+		removeAsListenerAndDispose(importItemCtrl);
+		importItemCtrl = new ImportQuestionItemController(ureq, getWindowControl());
+		listenTo(importItemCtrl);
+		
+		cmc = new CloseableModalController(getWindowControl(), translate("close"),
+				importItemCtrl.getInitialComponent(), true, translate("import.item"));
+		cmc.activate();
+		listenTo(cmc);
+	}
+	
 	private void doAskCollectionName(UserRequest ureq, List<QuestionItem> items) {
 		removeAsListenerAndDispose(createCollectionCtrl);
 		createCollectionCtrl = new CreateCollectionController(ureq, getWindowControl());
diff --git a/src/main/java/org/olat/modules/qpool/ui/_content/item_list.html b/src/main/java/org/olat/modules/qpool/ui/_content/item_list.html
index 5037cd65133..964db69037e 100644
--- a/src/main/java/org/olat/modules/qpool/ui/_content/item_list.html
+++ b/src/main/java/org/olat/modules/qpool/ui/_content/item_list.html
@@ -3,6 +3,7 @@ $r.render("items")
 	<div class="o_qpool_button_bar">
 	$r.render("create.list")
 	$r.render("share.item")
+	$r.render("import.item")
 	$r.render("author.item")
 	$r.render("delete.item")
 	</div>
diff --git a/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_de.properties
index b7cbb05ecb1..ae0b8e6fb11 100644
--- a/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/qpool/ui/_i18n/LocalStrings_de.properties
@@ -66,4 +66,5 @@ author.confirm.title=Best
 table.user.login=$org.olat.admin.user\:table.user.login
 share.item=Freigeben
 select.group=Freigeben
-select.item=Bearbeiten
\ No newline at end of file
+select.item=Bearbeiten
+import.item=Import
\ No newline at end of file
diff --git a/src/main/resources/database/mysql/alter_8_4_0_to_9_0_0.sql b/src/main/resources/database/mysql/alter_8_4_0_to_9_0_0.sql
index bdeb81e0ec9..308df5e13cb 100644
--- a/src/main/resources/database/mysql/alter_8_4_0_to_9_0_0.sql
+++ b/src/main/resources/database/mysql/alter_8_4_0_to_9_0_0.sql
@@ -26,10 +26,11 @@ create table if not exists o_qp_study_field (
 
 create table if not exists o_qp_item (
    id bigint not null,
+   q_uuid varchar(36) not null,
    q_subject varchar(255) not null,
    q_keywords varchar(2048),
-   q_type varchar(64) not null,
-   q_language varchar(16) not null,
+   q_type varchar(64),
+   q_language varchar(16),
    q_status varchar(32) not null,
    q_description varchar(4000),
    q_copyright varchar(2048),
@@ -44,6 +45,8 @@ create table if not exists o_qp_item (
    creationdate datetime not null,
    lastmodified datetime not null,
    q_version varchar(32),
+   q_dir varchar(32),
+   q_root_filename varchar(255),
    fk_study_field bigint,
    fk_author_grp_id bigint,
    primary key (id)
diff --git a/src/test/java/org/olat/modules/ims/qti/fileresource/FileResourceValidatorTest.java b/src/test/java/org/olat/modules/ims/qti/fileresource/FileResourceValidatorTest.java
new file mode 100644
index 00000000000..c28b7e1eb55
--- /dev/null
+++ b/src/test/java/org/olat/modules/ims/qti/fileresource/FileResourceValidatorTest.java
@@ -0,0 +1,46 @@
+package org.olat.modules.ims.qti.fileresource;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.olat.ims.qti.fileresource.ItemFileResourceValidator;
+
+/**
+ * 
+ * Initial date: 27.02.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class FileResourceValidatorTest {
+	
+	@Test
+	public void testItemValidation_xml() throws IOException, URISyntaxException {
+		URL itemUrl = FileResourceValidatorTest.class.getResource("mchc_ir_005.xml");
+		assertNotNull(itemUrl);
+		File itemFile = new File(itemUrl.toURI());
+
+		ItemFileResourceValidator validator = new ItemFileResourceValidator();
+		boolean valid = validator.validate(itemFile.getName(), itemFile);
+		Assert.assertTrue(valid);
+	}
+
+	
+	@Test
+	public void testItemValidation_zip() throws IOException, URISyntaxException {
+		URL itemUrl = FileResourceValidatorTest.class.getResource("mchc_i_002.zip");
+		assertNotNull(itemUrl);
+		File itemFile = new File(itemUrl.toURI());
+
+		ItemFileResourceValidator validator = new ItemFileResourceValidator();
+		boolean valid = validator.validate(itemFile.getName(), itemFile);
+		Assert.assertTrue(valid);
+	}
+
+}
diff --git a/src/test/java/org/olat/modules/ims/qti/fileresource/mchc_i_002.zip b/src/test/java/org/olat/modules/ims/qti/fileresource/mchc_i_002.zip
new file mode 100644
index 0000000000000000000000000000000000000000..48e31fd9ff28948e18f0bcd256acbbaa318f8533
GIT binary patch
literal 13846
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%0D;
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

literal 0
HcmV?d00001

diff --git a/src/test/java/org/olat/modules/ims/qti/fileresource/mchc_ir_005.xml b/src/test/java/org/olat/modules/ims/qti/fileresource/mchc_ir_005.xml
new file mode 100644
index 00000000000..b285c730163
--- /dev/null
+++ b/src/test/java/org/olat/modules/ims/qti/fileresource/mchc_ir_005.xml
@@ -0,0 +1 @@
+<?xml version = "1.0" encoding = "UTF-8" standalone = "no"?>
<!DOCTYPE questestinterop SYSTEM "ims_qtiasiv1p2.dtd">

<!-- Author:	Colin Smythe     						-->

<!-- Date:		22nd January, 2002     					-->

<!-- Version 1.2 Compliant Example: BasicExample008b	-->

<!-- Basic Example with response processing				-->
<questestinterop>
	<qticomment>This is a multiple-choice with slider rendering.</qticomment>
	<item title = "Multiple Choice with Slider rendering Item" ident = "IMS_V01_I_mchc_ir_005">    
		<presentation label = "BasicExample008b">
			<flow>      
				<material>        
					<mattext>What is the value of 2 * 3 ?</mattext>      
				</material>
				<flow>      
					<response_lid ident = "MC05" rcardinality = "Single" rtiming = "No">        
						<render_slider lowerbound = "2" upperbound = "10" step = "2" startval = "4" steplabel = "Yes">        
							<response_label ident = "A" rrange = "Exact">2</response_label>        
							<response_label ident = "B" rrange = "Exact">4</response_label>        
							<response_label ident = "C" rrange = "Exact">6</response_label>        
							<response_label ident = "D" rrange = "Exact">8</response_label>        
							<response_label ident = "E" rrange = "Exact">10</response_label>      
						</render_slider>
					</response_lid>
				</flow>
			</flow>
		</presentation>
		<resprocessing>
			<outcomes>
				<decvar varname = "SLIDECHOICE" vartype = "Integer" defaultval = "0"/>
			</outcomes>
			<respcondition>
				<qticomment>Scoring for the correct answer.</qticomment>
				<conditionvar>
					<varequal respident = "MC05">C</varequal>
				</conditionvar>
				<setvar action = "Add" varname = "SLIDECHOICE">5</setvar>
				<displayfeedback feedbacktype = "Response" linkrefid = "Correct"/>
			</respcondition>
			<respcondition>
				<qticomment>Detecting the worng answer.</qticomment>
				<conditionvar>
					<or>
						<varequal respident = "MC05">A</varequal>
						<varequal respident = "MC05">B</varequal>
						<varequal respident = "MC05">D</varequal>
						<varequal respident = "MC05">E</varequal>
					</or>
				</conditionvar>
				<displayfeedback feedbacktype = "Response" linkrefid = "Incorrect"/>
			</respcondition>
		</resprocessing>
		<itemfeedback ident = "Correct" view = "Candidate">
			<flow_mat>
				<material>
					<mattext>Correct.</mattext>
				</material>
			</flow_mat>
		</itemfeedback>
		<itemfeedback ident = "Incorrect" view = "Candidate">
			<flow_mat>
				<material>
					<mattext>The correct answer is 6.</mattext>
				</material>
			</flow_mat>
		</itemfeedback>
		<itemfeedback ident = "Incorrect" view = "Tutor">
			<flow_mat>
				<material>
					<mattext>The student chose the wrong answer.</mattext>
				</material>
			</flow_mat>
		</itemfeedback>
	</item>
</questestinterop>
\ No newline at end of file
diff --git a/src/test/java/org/olat/modules/qpool/manager/CollectionDAOTest.java b/src/test/java/org/olat/modules/qpool/manager/CollectionDAOTest.java
index 6e1f7440f42..a10b1336d4a 100644
--- a/src/test/java/org/olat/modules/qpool/manager/CollectionDAOTest.java
+++ b/src/test/java/org/olat/modules/qpool/manager/CollectionDAOTest.java
@@ -79,7 +79,7 @@ public class CollectionDAOTest extends OlatTestCase {
 	public void addItemToCollectionById() {
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Coll-Onwer-2-" + UUID.randomUUID().toString());
 		QuestionItemCollection coll = collectionDao.createCollection("NGC collection 2", id);
-		QuestionItem item = questionDao.create(null, "NGC 89", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, QuestionType.FIB);
+		QuestionItem item = questionDao.create(null, "NGC 89", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, null, QuestionType.FIB);
 		dbInstance.commitAndCloseSession();
 		
 		//add the item to the collection
@@ -92,8 +92,8 @@ public class CollectionDAOTest extends OlatTestCase {
 		//create a collection with 2 items
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Coll-Onwer-3-" + UUID.randomUUID().toString());
 		QuestionItemCollection coll = collectionDao.createCollection("NGC collection 3", id);
-		QuestionItem item1 = questionDao.create(null, "NGC 92", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, QuestionType.FIB);
-		QuestionItem item2 = questionDao.create(null, "NGC 97", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, QuestionType.FIB);
+		QuestionItem item1 = questionDao.create(null, "NGC 92", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, null, QuestionType.FIB);
+		QuestionItem item2 = questionDao.create(null, "NGC 97", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, null, QuestionType.FIB);
 		collectionDao.addItemToCollection(item1, coll);
 		collectionDao.addItemToCollection(item2, coll);
 		dbInstance.commit();//check if it's alright
diff --git a/src/test/java/org/olat/modules/qpool/manager/PoolDAOTest.java b/src/test/java/org/olat/modules/qpool/manager/PoolDAOTest.java
index 22fc57739fb..72f243c78ab 100644
--- a/src/test/java/org/olat/modules/qpool/manager/PoolDAOTest.java
+++ b/src/test/java/org/olat/modules/qpool/manager/PoolDAOTest.java
@@ -92,7 +92,7 @@ public class PoolDAOTest extends OlatTestCase {
 		String name = "NGC-" + UUID.randomUUID().toString();
 		Pool pool = poolDao.createPool(name);
 		Assert.assertNotNull(pool);
-		QuestionItem item = questionItemDao.create(null, "Galaxy", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
+		QuestionItem item = questionItemDao.create(null, "Galaxy", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
 		Assert.assertNotNull(item);
 		dbInstance.commitAndCloseSession();
 		
@@ -106,7 +106,7 @@ public class PoolDAOTest extends OlatTestCase {
 		//create a pool
 		String name = "NGC-" + UUID.randomUUID().toString();
 		Pool pool = poolDao.createPool(name);
-		QuestionItem item = questionItemDao.create(null, "Galaxy", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
+		QuestionItem item = questionItemDao.create(null, "Galaxy", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
 		poolDao.addItemToPool(item, pool);
 		dbInstance.commitAndCloseSession();
 		
@@ -121,7 +121,7 @@ public class PoolDAOTest extends OlatTestCase {
 		//create a pool with an item
 		String name = "NGC-" + UUID.randomUUID().toString();
 		Pool pool = poolDao.createPool(name);
-		QuestionItem item = questionItemDao.create(null, "Galaxy", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
+		QuestionItem item = questionItemDao.create(null, "Galaxy", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
 		poolDao.addItemToPool(item, pool);
 		dbInstance.commitAndCloseSession();
 		
diff --git a/src/test/java/org/olat/modules/qpool/manager/QuestionDAOTest.java b/src/test/java/org/olat/modules/qpool/manager/QuestionDAOTest.java
index 3a90b5090a3..5efa4c10288 100644
--- a/src/test/java/org/olat/modules/qpool/manager/QuestionDAOTest.java
+++ b/src/test/java/org/olat/modules/qpool/manager/QuestionDAOTest.java
@@ -57,9 +57,10 @@ public class QuestionDAOTest extends OlatTestCase {
 	
 	@Test
 	public void createQuestion() {
-		QuestionItem item = questionDao.create(null, "Stars", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.FIB);
+		QuestionItem item = questionDao.create(null, "Stars", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.FIB);
 		Assert.assertNotNull(item);
 		Assert.assertNotNull(item.getKey());
+		Assert.assertNotNull(item.getUuid());
 		Assert.assertNotNull(item.getCreationDate());
 		Assert.assertNotNull(item.getLastModified());
 		Assert.assertNotNull(item.getQuestionType());
@@ -71,7 +72,7 @@ public class QuestionDAOTest extends OlatTestCase {
 	@Test
 	public void createQuestion_withOwner() {
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("QOwn-1-" + UUID.randomUUID().toString());
-		QuestionItem item = questionDao.create(id, "My fav. stars", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.FIB);
+		QuestionItem item = questionDao.create(id, "My fav. stars", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.FIB);
 		Assert.assertNotNull(item);
 		Assert.assertNotNull(item.getKey());
 		Assert.assertNotNull(item.getCreationDate());
@@ -86,8 +87,8 @@ public class QuestionDAOTest extends OlatTestCase {
 	public void getItems_byAuthor() {
 		//create an author with 2 items
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("QOwn-2-" + UUID.randomUUID().toString());
-		QuestionItem item1 = questionDao.create(id, "NGC 2171", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.FIB);
-		QuestionItem item2 = questionDao.create(id, "NGC 2172", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.FIB);
+		QuestionItem item1 = questionDao.create(id, "NGC 2171", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.FIB);
+		QuestionItem item2 = questionDao.create(id, "NGC 2172", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.FIB);
 		dbInstance.commitAndCloseSession();
 		
 		//count the items of the author
@@ -103,7 +104,7 @@ public class QuestionDAOTest extends OlatTestCase {
 
 	@Test
 	public void getNumOfQuestions() {
-		QuestionItem item = questionDao.create(null, "NGC 1277", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
+		QuestionItem item = questionDao.create(null, "NGC 1277", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
 		Assert.assertNotNull(item);
 		dbInstance.commitAndCloseSession();
 		
@@ -115,9 +116,9 @@ public class QuestionDAOTest extends OlatTestCase {
 	@Test
 	public void getFavoritItems() {
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("fav-item-" + UUID.randomUUID().toString());
-		QuestionItem item1 = questionDao.create(id, "NGC 55", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
-		QuestionItem item2 = questionDao.create(id, "NGC 253", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
-		QuestionItem item3 = questionDao.create(id, "NGC 292", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
+		QuestionItem item1 = questionDao.create(id, "NGC 55", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		QuestionItem item2 = questionDao.create(id, "NGC 253", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		QuestionItem item3 = questionDao.create(id, "NGC 292", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
 		markManager.setMark(item1, id, null, "[QuestionItem:" + item1 + "]");
 		markManager.setMark(item2, id, null, "[QuestionItem:" + item2 + "]");
 		dbInstance.commitAndCloseSession();
@@ -134,8 +135,8 @@ public class QuestionDAOTest extends OlatTestCase {
 	public void shareItems() {
 		//create a group to share 2 items
 		BusinessGroup group = businessGroupDao.createAndPersist(null, "gdao", "gdao-desc", -1, -1, false, false, false, false, false);
-		QuestionItem item1 = questionDao.create(null, "Share-Item-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
-		QuestionItem item2 = questionDao.create(null, "Share-Item-2", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
+		QuestionItem item1 = questionDao.create(null, "Share-Item-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		QuestionItem item2 = questionDao.create(null, "Share-Item-2", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
 		dbInstance.commit();
 		
 		//share them
@@ -154,7 +155,7 @@ public class QuestionDAOTest extends OlatTestCase {
 	public void shareItems_avoidDuplicates() {
 		//create a group to share 2 items
 		BusinessGroup group = businessGroupDao.createAndPersist(null, "gdao", "gdao-desc", -1, -1, false, false, false, false, false);
-		QuestionItem item = questionDao.create(null, "Share-Item-Dup-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
+		QuestionItem item = questionDao.create(null, "Share-Item-Dup-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
 		dbInstance.commit();
 		
 		//share them
@@ -174,7 +175,7 @@ public class QuestionDAOTest extends OlatTestCase {
 		//create a group to share 2 items
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Share-item-" + UUID.randomUUID().toString());
 		BusinessGroup group = businessGroupDao.createAndPersist(id, "gdao", "gdao-desc", -1, -1, false, false, false, false, false);
-		QuestionItem item = questionDao.create(id, "Share-Item-Dup-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
+		QuestionItem item = questionDao.create(id, "Share-Item-Dup-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
 		dbInstance.commit();
 		
 		//share them
@@ -194,7 +195,7 @@ public class QuestionDAOTest extends OlatTestCase {
 		//create a group to share 2 items
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Share-rm-" + UUID.randomUUID().toString());
 		BusinessGroup group = businessGroupDao.createAndPersist(id, "gdrm", "gdrm-desc", -1, -1, false, false, false, false, false);
-		QuestionItem item = questionDao.create(id, "Share-item-rm-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
+		QuestionItem item = questionDao.create(id, "Share-item-rm-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
 		dbInstance.commit();
 		//share them
 		questionDao.share(item, group.getResource());
diff --git a/src/test/java/org/olat/modules/qpool/manager/QuestionPoolServiceTest.java b/src/test/java/org/olat/modules/qpool/manager/QuestionPoolServiceTest.java
index bd1876ddb92..4028907f9eb 100644
--- a/src/test/java/org/olat/modules/qpool/manager/QuestionPoolServiceTest.java
+++ b/src/test/java/org/olat/modules/qpool/manager/QuestionPoolServiceTest.java
@@ -19,6 +19,12 @@
  */
 package org.olat.modules.qpool.manager;
 
+import static org.junit.Assert.assertNotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
@@ -62,8 +68,8 @@ public class QuestionPoolServiceTest extends OlatTestCase {
 		//create a group to share 2 items
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Share-rm-" + UUID.randomUUID().toString());
 		BusinessGroup group = businessGroupDao.createAndPersist(id, "gdrm", "gdrm-desc", -1, -1, false, false, false, false, false);
-		QuestionItem item1 = questionDao.create(id, "Share-item-rm-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
-		QuestionItem item2 = questionDao.create(id, "Share-item-rm-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, QuestionType.MC);
+		QuestionItem item1 = questionDao.create(id, "Share-item-rm-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		QuestionItem item2 = questionDao.create(id, "Share-item-rm-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
 		dbInstance.commit();
 		//share them
 		questionDao.share(item1, group.getResource());
@@ -86,9 +92,9 @@ public class QuestionPoolServiceTest extends OlatTestCase {
 	@Test
 	public void createCollection() {
 		//create an user with 2 items
-		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Coll-Onwer-3-" + UUID.randomUUID().toString());
-		QuestionItem item1 = questionDao.create(id, "NGC 92", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, QuestionType.FIB);
-		QuestionItem item2 = questionDao.create(id, "NGC 97", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, QuestionType.FIB);
+		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Coll-Owner-3-" + UUID.randomUUID().toString());
+		QuestionItem item1 = questionDao.create(id, "NGC 92", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, null, QuestionType.FIB);
+		QuestionItem item2 = questionDao.create(id, "NGC 97", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, null, QuestionType.FIB);
 		dbInstance.commit();
 		
 		//load the items of the collection
@@ -109,4 +115,19 @@ public class QuestionPoolServiceTest extends OlatTestCase {
 		Assert.assertTrue(itemsOfCollection.contains(item1));
 		Assert.assertTrue(itemsOfCollection.contains(item2));
 	}
+
+	@Test
+	public void importItem_qti12xml() throws IOException, URISyntaxException {
+		Identity owner = JunitTestHelper.createAndPersistIdentityAsUser("Imp-Owner-1-" + UUID.randomUUID().toString());
+		dbInstance.commit();
+		URL itemUrl = QuestionPoolServiceTest.class.getResource("mchc_i_001.xml");
+		assertNotNull(itemUrl);
+		File itemFile = new File(itemUrl.toURI());
+		
+		
+		qpoolService.importItem(owner, "mchc_i_001.xml", itemFile);
+		
+		
+	}
+	
 }
diff --git a/src/test/java/org/olat/modules/qpool/manager/mchc_i_001.xml b/src/test/java/org/olat/modules/qpool/manager/mchc_i_001.xml
new file mode 100644
index 00000000000..e865fa086bd
--- /dev/null
+++ b/src/test/java/org/olat/modules/qpool/manager/mchc_i_001.xml
@@ -0,0 +1 @@
+<?xml version = "1.0" encoding = "UTF-8" standalone = "no"?>
<!-- Author:	Colin Smythe     			   			-->
<!-- Date:	22nd January    					-->
<!-- Version 1.2 Compliant Example: BasicExample002a 	-->
<questestinterop>
	<qticomment>This is a simple multiple choice example. The rendering is a standard radio button style. No response processing is incorporated.</qticomment>  
	<item title = "Standard Multiple Choice Item" ident = "IMS_V01_I_mchc_i_001">    
		<presentation label = "BasicExample002a">
			<flow>
				<material>        
					<mattext>Which one of the listed standards committees is responsible for developing the token ring specification ?</mattext>      
				</material>
				<response_lid ident = "MCa_01" rcardinality = "Single" rtiming = "No">        
					<render_choice shuffle = "Yes">
						<flow_label>        
							<response_label ident = "A">
								<material>
									<mattext>IEEE 802.3</mattext>
								</material>
							</response_label>
						</flow_label>
						<flow_label>        
							<response_label ident = "B"> 
								<material>
									<mattext>IEEE 802.5</mattext>
								</material>        
							</response_label>
						</flow_label>
						<flow_label>        
							<response_label ident = "C"> 
								<material>
									<mattext>IEEE 802.6</mattext>
								</material>        
							</response_label>
						</flow_label>
						<flow_label>        
							<response_label ident = "D"> 
								<material>
									<mattext>IEEE 802.11</mattext>
								</material>        
							</response_label>
						</flow_label>
						<flow_label>        
							<response_label ident = "E" rshuffle = "No"> 
								<material>
									<mattext>None of the above.</mattext>
								</material>        
							</response_label>
						</flow_label>
					</render_choice>      
				</response_lid>
			</flow>                
		</presentation>  
	</item>
</questestinterop>
\ No newline at end of file
diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java
index 5e4647c8216..465854c0355 100644
--- a/src/test/java/org/olat/test/AllTestsJunit4.java
+++ b/src/test/java/org/olat/test/AllTestsJunit4.java
@@ -117,6 +117,7 @@ import org.junit.runners.Suite;
 	org.olat.course.nodes.projectbroker.ProjectBrokerManagerTest.class,
 	org.olat.core.commons.persistence.DBTest.class,
 	org.olat.modules.ims.cp.CPManagerTest.class,
+	org.olat.modules.ims.qti.fileresource.FileResourceValidatorTest.class,
 	org.olat.modules.webFeed.FeedManagerImplTest.class,
 	org.olat.modules.qpool.manager.QuestionDAOTest.class,
 	org.olat.modules.qpool.manager.CollectionDAOTest.class,
-- 
GitLab