Skip to content
Snippets Groups Projects
Commit 4a460153 authored by srosse's avatar srosse
Browse files

OO-535: implements import of QTI 1.2 items

parent 618119c7
No related branches found
No related tags found
No related merge requests found
Showing
with 703 additions and 33 deletions
......@@ -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>
......
......@@ -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
......
......@@ -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;
}
}
}
......@@ -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
/**
* <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;
}
}
}
}
......@@ -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);
......
<?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
<?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
......@@ -37,6 +37,7 @@ public interface QuestionItem extends QuestionItemShort {
public String getLevel();
public String getDirectory();
public String getEditor();
......
......@@ -34,6 +34,8 @@ public interface QuestionItemShort extends OLATResourceable {
public Long getKey();
public String getUuid();
public Date getCreationDate();
public Date getLastModified();
......
......@@ -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);
}
}
}
......@@ -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);
......
/**
* <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);
}
}
......@@ -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);
......
/**
* <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;
}
}
/**
* <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;
}
}
......@@ -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));
}
}
......
......@@ -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();
......
......@@ -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);
......
......@@ -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();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment