diff --git a/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java b/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java index c2197765c4c54a4cc0656c19a81f20ee28bf29cd..d74298c4f722e36eef4cb9b90a495addaf8efdb4 100644 --- a/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java +++ b/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java @@ -50,8 +50,9 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { private Element currentParagraph; private Table currentTable; - public HTMLToOpenXMLHandler(OpenXMLDocument document) { + public HTMLToOpenXMLHandler(OpenXMLDocument document, Element paragraph) { this.factory = document; + this.currentParagraph = paragraph; } /** @@ -67,7 +68,7 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { flushText(); addContent(currentParagraph); } - currentParagraph = factory.createParagraphEl((Element)null); + currentParagraph = factory.createParagraphEl(); } return currentParagraph; } @@ -124,7 +125,7 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { private Element getCurrentRun() { Element paragraphEl; if(currentParagraph == null) { - paragraphEl = currentParagraph = factory.createParagraphEl((Element)null); + paragraphEl = currentParagraph = factory.createParagraphEl(); } else { paragraphEl = currentParagraph; } @@ -179,7 +180,7 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { } if(underline && !OpenXMLUtils.contains(runPrefs, "w:u")) { Element underlinePrefs = (Element)runPrefs.appendChild(runPrefs.getOwnerDocument().createElement("w:u")); - underlinePrefs.setAttribute("val", "single"); + underlinePrefs.setAttribute("w:val", "single"); } if(strike && !OpenXMLUtils.contains(runPrefs, "w:strike")) { runPrefs.appendChild(runPrefs.getOwnerDocument().createElement("w:strike")); @@ -223,9 +224,11 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { closeParagraph(); currentTable = new Table(); } else if("tr".equals(tag)) { - currentTable.addRowEl(); + currentTable.addRowEl(); } else if("td".equals(tag) || "th".equals(tag)) { - currentTable.addCellEl(); + int colspan = OpenXMLUtils.getSpanAttribute("colspan", attributes); + int rowspan = OpenXMLUtils.getSpanAttribute("rowspan", attributes); + currentTable.addCellEl(colspan, rowspan); } } @@ -253,6 +256,9 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { flushText(); currentParagraph = addContent(currentParagraph); } else if("tr".equals(tag)) { + if(currentTable != null) { + currentTable.closeRow(); + } textBuffer = null; latex = false; currentParagraph = null; @@ -272,9 +278,13 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { public class Table { private final Element tableEl; private final Element gridEl; + + private int nextCol; private Node currentRowEl; private Element currentCellEl; + private Span[] rowSpans = new Span[128]; + public Table() { tableEl = factory.createTable(); NodeList gridPrefs = tableEl.getElementsByTagName("w:tblGrid"); @@ -290,14 +300,79 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { } public Node addRowEl() { + for(int i=rowSpans.length; i-->0; ) { + if(rowSpans[i] != null) { + rowSpans[i].unDone(); + } + } + + nextCol = 0; currentRowEl = tableEl.getOwnerDocument().createElement("w:tr"); return tableEl.appendChild(currentRowEl); } - public Node addCellEl() { + public void closeRow() { + closeCell(rowSpans.length-1); + } + + /* +<w:tc> + <w:tcPr> + <w:gridSpan w:val="2" /> + <w:vMerge w:val="restart" /> + */ + public Node addCellEl(int colSpan, int rowSpan) { + nextCol += closeCell(nextCol); + currentCellEl = currentRowEl.getOwnerDocument().createElement("w:tc"); + + Node prefs = null; + if(colSpan > 1) { + prefs = currentCellEl.appendChild(currentCellEl.getOwnerDocument().createElement("w:tcPr")); + Element gridSpan = (Element)prefs.appendChild(prefs.getOwnerDocument().createElement("w:gridSpan")); + gridSpan.setAttribute("w:val", Integer.toString(colSpan)); + } + + if(rowSpan > 1) { + prefs = prefs != null ? prefs : currentCellEl.appendChild(currentCellEl.getOwnerDocument().createElement("w:tcPr")); + Element vMerge = (Element)prefs.appendChild(prefs.getOwnerDocument().createElement("w:vMerge")); + vMerge.setAttribute("w:val", "restart"); + } + + if(colSpan == 1 && rowSpan == 1) { + rowSpans[nextCol] = Span.OneOnOne; + } else { + rowSpans[nextCol] = new Span(colSpan, rowSpan); + } + + nextCol += (colSpan <= 1 ? 1 : colSpan); return currentRowEl.appendChild(currentCellEl); } + + public int closeCell(int lastIndex) { + for(int i=lastIndex+1; i-->0; ) { + Span span = rowSpans[i]; + if(span != null) { + if(span.getRowSpan() > 1 && !span.isDone()) { + currentCellEl = (Element)currentRowEl.appendChild(currentRowEl.getOwnerDocument().createElement("w:tc")); + Node prefs = currentCellEl.appendChild(currentCellEl.getOwnerDocument().createElement("w:tcPr")); + + if(span.getColSpan() > 1) { + Element gridSpan = (Element)prefs.appendChild(prefs.getOwnerDocument().createElement("w:gridSpan")); + gridSpan.setAttribute("w:val", Integer.toString(span.getColSpan())); + } + prefs.appendChild(prefs.getOwnerDocument().createElement("w:vMerge")); + + currentCellEl.appendChild(currentCellEl.getOwnerDocument().createElement("w:p")); + span.decrementRowSpan(); + return span.getColSpan(); + } else { + break; + } + } + } + return 0; + } public Element getCurrentCell() { return currentCellEl; @@ -307,4 +382,39 @@ public class HTMLToOpenXMLHandler extends DefaultHandler { this.currentCellEl = currentCell; } } + + private static class Span { + + public static final Span OneOnOne = new Span(1,1); + + private int colspan; + private int rowspan; + private boolean done = true; + + private Span(int colspan, int rowspan) { + this.colspan = colspan; + this.rowspan = rowspan; + } + + public int getColSpan() { + return colspan; + } + + public int getRowSpan() { + return rowspan; + } + + public void decrementRowSpan() { + rowspan--; + } + + public boolean isDone() { + return done; + } + + public void unDone() { + done = false; + } + + } } \ No newline at end of file diff --git a/src/main/java/org/olat/core/util/openxml/OpenXMLDocument.java b/src/main/java/org/olat/core/util/openxml/OpenXMLDocument.java index 5d1df2e09a46dbf7b357b714bda825fa77fe17b9..4abea2fe7ae4ad89ce69af544ce600f7f5ad4e1b 100644 --- a/src/main/java/org/olat/core/util/openxml/OpenXMLDocument.java +++ b/src/main/java/org/olat/core/util/openxml/OpenXMLDocument.java @@ -72,6 +72,7 @@ public class OpenXMLDocument { private final Document document; private final Element rootElement; + private final Element bodyElement; private final OpenXMLStyles styles; @@ -85,7 +86,7 @@ public class OpenXMLDocument { public OpenXMLDocument() { document = OpenXMLUtils.createDocument(); rootElement = createRootElement(document); - Element bodyElement = createBodyElement(rootElement, document); + bodyElement = createBodyElement(rootElement, document); styles = new OpenXMLStyles(); cursorStack.add(bodyElement); } @@ -154,8 +155,75 @@ public class OpenXMLDocument { Element paragraphEl = createParagraphEl(styleEl, Collections.singletonList(runEl)); getCursor().appendChild(paragraphEl); } +/* +<w:sectPr w:rsidR="00F528BA" w:rsidRPr="00DF16C8" w:rsidSect="007347AA"> + <w:pgSz w:w="11900" w:h="16840" /> + <w:pgMar w:top="1417" w:right="1417" w:bottom="1134" w:left="1417" w:header="708" w:footer="708" w:gutter="0" /> + <w:cols w:space="708" /> + <w:docGrid w:linePitch="360" /> +</w:sectPr> + */ + /** + * Must be done at the end of the document + */ + public void appendPageSettings() { + Node lastChild = bodyElement.getLastChild(); + if(lastChild != null && "w:sectPr".equals(lastChild.getLocalName())) { + return;//nothing to do, already set + } + + Node sectionPrefs = bodyElement.appendChild(document.createElement("w:sectPr")); + //A4 + Element pageSize = (Element)sectionPrefs.appendChild(document.createElement("w:pgSz")); + pageSize.setAttribute("w:w", "11900"); + pageSize.setAttribute("w:h", "16840"); + Element margins = (Element)sectionPrefs.appendChild(document.createElement("w:pgMar")); + margins.setAttribute("w:top", "1440"); + margins.setAttribute("w:right", "1440"); + margins.setAttribute("w:bottom", "1440"); + margins.setAttribute("w:left", "1440"); + margins.setAttribute("w:header", "708"); + margins.setAttribute("w:footer", "708"); + margins.setAttribute("w:gutter", "0"); + } + + public void appendFillInBlanck(int length, boolean newParagraph) { + Element paragraphEl = getParagraphToAppendTo(newParagraph); + + Node runEl = paragraphEl.appendChild(createRunEl(null)); + runEl.appendChild(createRunPrefsEl(Style.underline)); + + int tabLength = length / 5; + for(int i=tabLength; i-->0; ) { + runEl.appendChild(document.createElement("w:tab")); + } + getCursor().appendChild(paragraphEl); + } - public void appendTextParagraph(String text) { +/* +<w:p w:rsidR="00F528BA" w:rsidRPr="00245F75" w:rsidRDefault="00F528BA" w:rsidP="00245F75"> + <w:pPr> + <w:pBdr> + <w:bottom w:val="single" w:sz="4" w:space="1" w:color="auto" /> + </w:pBdr> + </w:pPr> +</w:p> + */ + public void appendFillInBlanckWholeLine(int rows) { + for(int i=rows+1; i-->0; ) { + Element paragraphEl = createParagraphEl(); + Node pargraphPrefs = paragraphEl.appendChild(document.createElement("w:pPr")); + Node pargraphBottomPrefs = pargraphPrefs.appendChild(document.createElement("w:pBdr")); + Element bottomEl = (Element)pargraphBottomPrefs.appendChild(document.createElement("w:between")); + bottomEl.setAttribute("w:val", "single"); + bottomEl.setAttribute("w:sz", "4"); + bottomEl.setAttribute("w:space", "1"); + bottomEl.setAttribute("w:color", "auto"); + getCursor().appendChild(paragraphEl); + } + } + + public void appendText(String text, boolean newParagraph, Style... styles) { if(!StringHelper.containsNonWhitespace(text)) return; List<Element> textEls = new ArrayList<Element>(); @@ -169,12 +237,58 @@ public class OpenXMLDocument { } if(textEls.size() > 0) { - Element runEl = createRunEl(textEls); - Element paragraphEl = createParagraphEl(null, Collections.singletonList(runEl)); + Element paragraphEl = getParagraphToAppendTo(newParagraph); + Element runEl = document.createElement("w:r"); + if(styles != null && styles.length > 0) { + runEl.appendChild(createRunPrefsEl(styles)); + } + for(Element textEl:textEls) { + runEl.appendChild(textEl); + } + paragraphEl.appendChild(runEl); getCursor().appendChild(paragraphEl); } } + /** + * Get a paragraph, if @param newParagraph is false, try to get the + * last paragraph of the cursor. If @param newParagraph is true, create + * always a new paragraph. + * @param newParagraph + * @return + */ + private Element getParagraphToAppendTo(boolean newParagraph) { + Element paragraphEl = null; + if(!newParagraph) { + paragraphEl = getCurrentParagraph(); + //add a blank between + if(paragraphEl != null) { + Element runEl = document.createElement("w:r"); + runEl.appendChild(createPreserveSpaceEl()); + paragraphEl.appendChild(runEl); + } + } + if(paragraphEl == null) { + paragraphEl = createParagraphEl(); + } + return paragraphEl; + } + + /** + * Return the paragraph if and only if it's the last element. Return + * null if not found. + * @return + */ + private Element getCurrentParagraph() { + Element paragraphEl = null; + Node currentNode = getCursor(); + if(currentNode != null && currentNode.getLastChild() != null + && "w:p".equals(currentNode.getLastChild().getNodeName())) { + paragraphEl = (Element)currentNode.getLastChild(); + } + return paragraphEl; + } + public void appendPageBreak() { getCursor().appendChild(createPageBreakEl()); } @@ -186,11 +300,12 @@ public class OpenXMLDocument { getCursor().appendChild(paragraphEl); } - public void appendHtmlText(String html) { + public void appendHtmlText(String html, boolean newParagraph) { if(!StringHelper.containsNonWhitespace(html)) return; try { SAXParser parser = new SAXParser(); - parser.setContentHandler(new HTMLToOpenXMLHandler(this)); + Element paragraphEl = getParagraphToAppendTo(newParagraph); + parser.setContentHandler(new HTMLToOpenXMLHandler(this, paragraphEl)); parser.parse(new InputSource(new StringReader(html))); } catch (SAXException e) { log.error("", e); @@ -234,11 +349,8 @@ public class OpenXMLDocument { return paragraphEl; } - public Element createParagraphEl(Element styleEl) { + public Element createParagraphEl() { Element paragraphEl = document.createElement("w:p"); - if(styleEl != null) { - paragraphEl.appendChild(styleEl); - } return paragraphEl; } @@ -252,8 +364,25 @@ public class OpenXMLDocument { return runEl; } - public Element createRunPrefsEl() { + public Element createRunPrefsEl(Style... styles) { Element runPrefsEl = document.createElement("w:rPr"); + if(styles != null && styles.length > 0) { + for(Style style:styles) { + if(style != null) { + switch(style) { + case underline: { + Element underlinePrefs = (Element)runPrefsEl.appendChild(document.createElement("w:u")); + underlinePrefs.setAttribute("w:val", "single"); + break; + } + case italic: { + runPrefsEl.appendChild(document.createElement("w:i")); + break; + } + } + } + } + } return runPrefsEl; } @@ -276,7 +405,7 @@ public class OpenXMLDocument { } public Element createParagraphEl(String text) { - Element paragraphEl = createParagraphEl((Element)null); + Element paragraphEl = createParagraphEl(); Node runEl = paragraphEl.appendChild(document.createElement("w:r")); for(StringTokenizer tokenizer = new StringTokenizer(text, "\n\r"); tokenizer.hasMoreTokens(); ) { @@ -398,7 +527,7 @@ public class OpenXMLDocument { Node prefEl = null; if(unit != null) { prefEl = cellEl.appendChild(document.createElement("w:tcPr")); - createWidthEl("w:tcW", width, Unit.auto, cellEl); + createWidthEl("w:tcW", width, unit, cellEl); } if(StringHelper.containsNonWhitespace(background)) { if(prefEl == null) { @@ -471,8 +600,6 @@ public class OpenXMLDocument { try { //convert latex -> mathml String mathml = ConvertFromLatexToMathML.convertToMathML(latex); - System.out.println("Latex: " + latex); - System.out.println("MathML: " + mathml); //convert mathml to word docx ByteArrayOutputStream out = new ByteArrayOutputStream(20000); @@ -710,6 +837,11 @@ public class OpenXMLDocument { return bodyEl; } + public enum Style { + underline, + italic, + } + public enum Unit { dxa("dxa"), pct("pct"), diff --git a/src/main/java/org/olat/core/util/openxml/OpenXMLDocumentWriter.java b/src/main/java/org/olat/core/util/openxml/OpenXMLDocumentWriter.java index 644b231a677c7ce5deafee81803d7f8d3cf6f39a..494a0e1b4413fea55691713d9671545a41ec05b6 100644 --- a/src/main/java/org/olat/core/util/openxml/OpenXMLDocumentWriter.java +++ b/src/main/java/org/olat/core/util/openxml/OpenXMLDocumentWriter.java @@ -100,6 +100,7 @@ public class OpenXMLDocumentWriter { //word/document.xml ZipEntry wordDocument = new ZipEntry("word/document.xml"); out.putNextEntry(wordDocument); + document.appendPageSettings(); OpenXMLUtils.writeTo(document.getDocument(), out, false); out.closeEntry(); diff --git a/src/main/java/org/olat/core/util/openxml/OpenXMLUtils.java b/src/main/java/org/olat/core/util/openxml/OpenXMLUtils.java index 92cd07309c8deca309d76aa716c8d7526d99a838..2215c319d282787bce75cceb1fa145752400289b 100644 --- a/src/main/java/org/olat/core/util/openxml/OpenXMLUtils.java +++ b/src/main/java/org/olat/core/util/openxml/OpenXMLUtils.java @@ -39,9 +39,11 @@ import javax.xml.transform.stream.StreamResult; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; +import org.olat.core.util.StringHelper; import org.olat.core.util.image.Size; import org.w3c.dom.Document; import org.w3c.dom.Node; +import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -69,6 +71,21 @@ public class OpenXMLUtils { return new Size((int)widthEmus, (int)heightEmus, true); } + public static int getSpanAttribute(String name, Attributes attrs) { + name = name.toLowerCase(); + int span = -1; + for(int i=attrs.getLength(); i-->0; ) { + String attrName = attrs.getQName(i); + if(name.equals(attrName.toLowerCase())) { + String val = attrs.getValue(i); + if(StringHelper.isLong(val)) { + return Integer.parseInt(val); + } + } + } + return span < 1 ? 1 : span; + } + public static boolean contains(Node parent, String nodeName) { boolean found = false; for(Node node=parent.getFirstChild(); node!=null; node=node.getNextSibling()) { diff --git a/src/main/java/org/olat/ims/qti/container/HttpItemInput.java b/src/main/java/org/olat/ims/qti/container/HttpItemInput.java index 576766035c9de8368883a21e800816095fd0e267..7b453b8291fde674693c7fee6d2861085c0f54e2 100644 --- a/src/main/java/org/olat/ims/qti/container/HttpItemInput.java +++ b/src/main/java/org/olat/ims/qti/container/HttpItemInput.java @@ -39,29 +39,32 @@ import java.util.Map; */ public class HttpItemInput implements ItemInput, Serializable { - private Map m; + private static final long serialVersionUID = 7283305686407753678L; + private Map<String,List<String>> m; private String ident; /** * Constructor */ public HttpItemInput(String itemIdent) { - m = new HashMap(); + m = new HashMap<String,List<String>>(); ident = itemIdent; } public void addTestVariableVal(String varName) { - List li = (List) m.get(varName); + List<String> li = m.get(varName); if (li == null) { - li = new ArrayList(); + li = new ArrayList<String>(); m.put(varName,li); } li.add("1.23456"); // a value which satisfies all compares } public Object putSingle(String key, String value) { - List l = getAsList(key); - if (l == null) { l = new ArrayList(); } + List<String> l = getAsList(key); + if (l == null) { + l = new ArrayList<String>(); + } l.add(value.trim()); return m.put(key, l); } @@ -70,7 +73,7 @@ public class HttpItemInput implements ItemInput, Serializable { * @see org.olat.qti.process.ItemInput#getAsString(java.lang.String) */ public String getSingle(String varName) { - List li = getAsList(varName); + List<String> li = getAsList(varName); if(li==null) { return ""; } @@ -81,8 +84,8 @@ public class HttpItemInput implements ItemInput, Serializable { /** * @see org.olat.qti.process.ItemInput#getAsSet(java.lang.String) */ - public List getAsList(String varName) { - List li = (List) m.get(varName); + public List<String> getAsList(String varName) { + List<String> li = m.get(varName); return li; } @@ -90,12 +93,12 @@ public class HttpItemInput implements ItemInput, Serializable { * Return the map of answers for all inputs * @return */ - public Map getInputMap() { + public Map<String,List<String>> getInputMap() { return m; } public boolean contains(String varName, String value) { - List li = (List) m.get(varName); + List<String> li = m.get(varName); if (li == null) { /* If variable was not declared, we return false without throwing up This is necessary for example for composite multiple choice, single select @@ -109,7 +112,7 @@ public class HttpItemInput implements ItemInput, Serializable { } public boolean containsIgnoreCase(String varName, String value) { - List li = (List) m.get(varName); + List<String> li = m.get(varName); if (li == null) { /* If variable was not declared, we return false without throwing up This is necessary for example for composite multiple choice, single select @@ -119,8 +122,8 @@ public class HttpItemInput implements ItemInput, Serializable { // throw new RuntimeException("variable "+varName+" was not declared!"); return false; } - for (Iterator iter = li.iterator(); iter.hasNext();) { - String element = (String) iter.next(); + for (Iterator<String> iter = li.iterator(); iter.hasNext();) { + String element = iter.next(); if (element.equalsIgnoreCase(value)) return true; } return false; diff --git a/src/main/java/org/olat/ims/qti/container/ItemInput.java b/src/main/java/org/olat/ims/qti/container/ItemInput.java index 599e348015aed87782575757de259d41427b7a0b..36126d24c82af32ed8c3551dcbdb26a4b4d6c63a 100644 --- a/src/main/java/org/olat/ims/qti/container/ItemInput.java +++ b/src/main/java/org/olat/ims/qti/container/ItemInput.java @@ -49,14 +49,14 @@ public interface ItemInput { * @param varName * @return List the List containing the String(s) */ - public List getAsList(String varName); + public List<String> getAsList(String varName); /** * returns a map of all inputs for all response_xxx. response_xxx are keys that * have corresponding Lists of Strings with the answers for that response_xxx * @return */ - public Map getInputMap(); + public Map<String,List<String>> getInputMap(); /** * diff --git a/src/main/java/org/olat/ims/qti/container/qtielements/Matapplet.java b/src/main/java/org/olat/ims/qti/container/qtielements/Matapplet.java index 50fce790a3f591867f85e478fd2bde5fbf26a75e..83bb1288cdf63ef8f0be7aa0c2b2770bc2390015 100644 --- a/src/main/java/org/olat/ims/qti/container/qtielements/Matapplet.java +++ b/src/main/java/org/olat/ims/qti/container/qtielements/Matapplet.java @@ -122,6 +122,6 @@ public class Matapplet extends GenericQTIElement { @Override public void renderOpenXML(OpenXMLDocument document, RenderInstructions ri) { - document.appendTextParagraph("Applet not supported"); + document.appendText("Applet not supported", true); } } \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti/container/qtielements/Matemtext.java b/src/main/java/org/olat/ims/qti/container/qtielements/Matemtext.java index ad67b68314e093a18b0575c5116b726a260037dd..00e587e07b917d01b8a2d3d37de2f858c28266af 100644 --- a/src/main/java/org/olat/ims/qti/container/qtielements/Matemtext.java +++ b/src/main/java/org/olat/ims/qti/container/qtielements/Matemtext.java @@ -27,6 +27,7 @@ package org.olat.ims.qti.container.qtielements; import org.dom4j.Element; import org.olat.core.util.openxml.OpenXMLDocument; +import org.olat.core.util.openxml.OpenXMLDocument.Style; /** * Initial Date: 24.11.2004 * @@ -84,6 +85,6 @@ public class Matemtext extends GenericQTIElement { @Override public void renderOpenXML(OpenXMLDocument document, RenderInstructions ri) { - document.appendTextParagraph(content); + document.appendText(content, false, Style.italic); } } \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti/container/qtielements/Mattext.java b/src/main/java/org/olat/ims/qti/container/qtielements/Mattext.java index 399ef8c69bd4e1b347573b930b0ff30cd5e6c6f5..11b6044963b8d74c28d8888f0bb5b08e8f54e826 100644 --- a/src/main/java/org/olat/ims/qti/container/qtielements/Mattext.java +++ b/src/main/java/org/olat/ims/qti/container/qtielements/Mattext.java @@ -26,6 +26,7 @@ package org.olat.ims.qti.container.qtielements; import org.dom4j.Element; +import org.olat.core.util.ConsumableBoolean; import org.olat.core.util.Formatter; import org.olat.core.util.filter.Filter; import org.olat.core.util.filter.FilterFactory; @@ -96,6 +97,8 @@ public class Mattext extends GenericQTIElement { @Override public void renderOpenXML(OpenXMLDocument document, RenderInstructions ri) { - document.appendHtmlText(content); + ConsumableBoolean br = (ConsumableBoolean)ri.get(RenderInstructions.KEY_BREAK_DELAY); + boolean newParagraph = br == null || !br.isTrue(); + document.appendHtmlText(content, newParagraph); } } diff --git a/src/main/java/org/olat/ims/qti/container/qtielements/Objectives.java b/src/main/java/org/olat/ims/qti/container/qtielements/Objectives.java index 51f3a8fb141e98ff7bf660ef10f4817c7b4de5be..0019fc6d832e58494ebd1fe8534aac17ed8eccb2 100644 --- a/src/main/java/org/olat/ims/qti/container/qtielements/Objectives.java +++ b/src/main/java/org/olat/ims/qti/container/qtielements/Objectives.java @@ -61,6 +61,7 @@ public class Objectives extends GenericQTIElement { @Override public void renderOpenXML(OpenXMLDocument document, RenderInstructions ri) { super.renderOpenXML(document, ri); + //document.appendBreak(); } diff --git a/src/main/java/org/olat/ims/qti/container/qtielements/RenderInstructions.java b/src/main/java/org/olat/ims/qti/container/qtielements/RenderInstructions.java index 59ffb1e4e522eac2d6bc50a2206be374b11b9584..5cb3e593584663f900965e0e0674564f6fc38f90 100644 --- a/src/main/java/org/olat/ims/qti/container/qtielements/RenderInstructions.java +++ b/src/main/java/org/olat/ims/qti/container/qtielements/RenderInstructions.java @@ -101,6 +101,13 @@ public class RenderInstructions extends HashMap<String,Object> { protected static final String KEY_FIB_COLUMNS = "cols"; // Denotes maxlength attribute of a render_fib element protected static final String KEY_FIB_MAXLENGTH = "max"; + // Output the correct responses too + public static final String KEY_RENDER_CORRECT_RESPONSES = "rCorrectResponses"; + + public static final String KEY_CORRECT_RESPONSES_MAP = "correctResponsesMap"; + + public static final String KEY_BREAK_DELAY = "breakDelay"; + // just a simple HashMap } diff --git a/src/main/java/org/olat/ims/qti/container/qtielements/Render_choice.java b/src/main/java/org/olat/ims/qti/container/qtielements/Render_choice.java index 87a04ce7e91a0637daee962c76c05c2deb401f2a..1fcb84b508c05566048d7ee18741b9cbadf5b14c 100644 --- a/src/main/java/org/olat/ims/qti/container/qtielements/Render_choice.java +++ b/src/main/java/org/olat/ims/qti/container/qtielements/Render_choice.java @@ -165,7 +165,8 @@ public class Render_choice extends GenericQTIElement { } ri.put(RenderInstructions.KEY_RENDER_CLASS, "choice"); //open a table with 2 columns - table = document.appendTable(new Integer[]{10178, 1116}); + //{10178, 1116} + table = document.appendTable(new Integer[]{8468, 745}); } document.pushCursor(table); super.renderOpenXML(document, ri); diff --git a/src/main/java/org/olat/ims/qti/container/qtielements/Render_fib.java b/src/main/java/org/olat/ims/qti/container/qtielements/Render_fib.java index 2c9387a00b1a5aed80d4bf287496a6e6812e4c99..d6a3085a1493cd0c57b21239cf5f59ac08db4d0b 100644 --- a/src/main/java/org/olat/ims/qti/container/qtielements/Render_fib.java +++ b/src/main/java/org/olat/ims/qti/container/qtielements/Render_fib.java @@ -27,6 +27,7 @@ package org.olat.ims.qti.container.qtielements; import org.dom4j.Element; import org.olat.core.logging.AssertException; +import org.olat.core.util.ConsumableBoolean; import org.olat.core.util.openxml.OpenXMLDocument; /** @@ -179,5 +180,7 @@ public class Render_fib extends GenericQTIElement { ri.put(RenderInstructions.KEY_FIB_ROWS, new Integer(rows)); ri.put(RenderInstructions.KEY_FIB_COLUMNS, new Integer(columns)); ri.put(RenderInstructions.KEY_FIB_MAXLENGTH, new Integer(maxchars)); + ri.put(RenderInstructions.KEY_FIB_MAXLENGTH, new Integer(maxchars)); + ri.put(RenderInstructions.KEY_BREAK_DELAY, new ConsumableBoolean(true)); } } diff --git a/src/main/java/org/olat/ims/qti/container/qtielements/Response_label.java b/src/main/java/org/olat/ims/qti/container/qtielements/Response_label.java index b8d08a3648c4d5e02f59651d07d0bcf5e74e7a00..1e578550c645815c17ca0e428c061473361e6c64 100644 --- a/src/main/java/org/olat/ims/qti/container/qtielements/Response_label.java +++ b/src/main/java/org/olat/ims/qti/container/qtielements/Response_label.java @@ -26,10 +26,13 @@ package org.olat.ims.qti.container.qtielements; import java.util.List; +import java.util.Map; import org.dom4j.Element; import org.olat.core.logging.AssertException; +import org.olat.core.util.StringHelper; import org.olat.core.util.openxml.OpenXMLDocument; +import org.olat.core.util.openxml.OpenXMLDocument.Style; import org.olat.core.util.openxml.OpenXMLDocument.Unit; import org.olat.ims.qti.container.ItemInput; import org.w3c.dom.Node; @@ -47,18 +50,19 @@ public class Response_label extends GenericQTIElement { */ public static final String xmlClass = "response_label"; private static String PARA = "§"; + /** * @param el_element */ public Response_label(Element el_element) { super(el_element); - } /** * @see org.olat.ims.qti.container.qtielements.QTIElement#render(StringBuilder, * RenderInstructions) */ + @Override public void render(StringBuilder buffer, RenderInstructions ri) { ItemInput iinput = (ItemInput) ri.get(RenderInstructions.KEY_ITEM_INPUT); String responseIdent = (String) ri.get(RenderInstructions.KEY_RESPONSE_IDENT); @@ -206,52 +210,91 @@ public class Response_label extends GenericQTIElement { @Override public void renderOpenXML(OpenXMLDocument document, RenderInstructions ri) { - String responseIdent = (String) ri.get(RenderInstructions.KEY_RESPONSE_IDENT); String renderClass = (String) ri.get(RenderInstructions.KEY_RENDER_CLASS); + + if(renderClass == null) { //we don't know what to do } else if(renderClass.equals("choice")) { Node row = document.createTableRow(); //answer - Node answerCell = row.appendChild(document.createTableCell("E9EAF2", 10178, Unit.dxa)); + Node answerCell = row.appendChild(document.createTableCell("E9EAF2", 4560, Unit.pct)); document.pushCursor(answerCell); super.renderOpenXML(document, ri); document.popCursor(answerCell); //checkbox - String response = ""; - boolean checked = response.equals(getQTIIdent()); - appendCheckBox(checked, row, document); - + boolean correct = isCorrectMCResponse(ri); + appendCheckBox(correct, row, document); //append row document.getCursor().appendChild(row); } else if (renderClass.equals("kprim")) { Node row = document.createTableRow(); //answer - Node answerCell = row.appendChild(document.createTableCell("E9EAF2", 9062, Unit.dxa)); + Node answerCell = row.appendChild(document.createTableCell("E9EAF2", 4120, Unit.pct)); document.pushCursor(answerCell); super.renderOpenXML(document, ri); document.popCursor(answerCell); - + //checkbox - String response = ""; - boolean checked = response.equals(getQTIIdent()); - appendCheckBox(checked, row, document); - appendCheckBox(checked, row, document); + boolean correct = isCorrectKPrimResponse(ri, "correct"); + appendCheckBox(correct, row, document); + boolean wrong = isCorrectKPrimResponse(ri, "wrong"); + appendCheckBox(wrong, row, document); //append row document.getCursor().appendChild(row); - } else if (renderClass.equals("fib")) { + Boolean render = (Boolean)ri.get(RenderInstructions.KEY_RENDER_CORRECT_RESPONSES); + @SuppressWarnings("unchecked") + Map<String,String> iinput = (Map<String,String>)ri.get(RenderInstructions.KEY_CORRECT_RESPONSES_MAP); + if(render != null && render.booleanValue() && iinput != null + && StringHelper.containsNonWhitespace(iinput.get(getQTIIdent()))) { + //show the response + String response = iinput.get(getQTIIdent()); + response = response.replace(";", ", "); + document.appendText(response, false, Style.underline); + } else { + Integer rows = (Integer)ri.get(RenderInstructions.KEY_FIB_ROWS); + if (rows != null && rows.intValue() > 1) { + document.appendFillInBlanckWholeLine(rows.intValue()); + } else { + Integer maxlength = (Integer)ri.get(RenderInstructions.KEY_FIB_MAXLENGTH); + int length = (maxlength == null ? 8 : maxlength.intValue()) / 2; + document.appendFillInBlanck(length, false); + } + } } } private void appendCheckBox(boolean checked, Node row, OpenXMLDocument document) { - Node checkboxCell = row.appendChild(document.createTableCell(null, 1116, Unit.dxa)); + Node checkboxCell = row.appendChild(document.createTableCell(null, 369, Unit.pct)); Node responseEl = document.createCheckbox(checked); Node wrapEl = document.wrapInParagraph(responseEl); //Node responseEl = document.createParagraphEl("OK"); checkboxCell.appendChild(wrapEl); } + + private boolean isCorrectMCResponse(RenderInstructions ri) { + Boolean render = (Boolean)ri.get(RenderInstructions.KEY_RENDER_CORRECT_RESPONSES); + if(render == null || !render.booleanValue()) return false; + @SuppressWarnings("unchecked") + Map<String,String> iinput = (Map<String,String>)ri.get(RenderInstructions.KEY_CORRECT_RESPONSES_MAP); + if(iinput != null && iinput.containsKey(getQTIIdent())) { + return true; + } + return false; + } + + private boolean isCorrectKPrimResponse(RenderInstructions ri, String add) { + Boolean render = (Boolean)ri.get(RenderInstructions.KEY_RENDER_CORRECT_RESPONSES); + if(render == null || !render.booleanValue()) return false; + @SuppressWarnings("unchecked") + Map<String,String> iinput = (Map<String,String>)ri.get(RenderInstructions.KEY_CORRECT_RESPONSES_MAP); + if(iinput != null && iinput.containsKey(getQTIIdent() + ":" + add)) { + return true; + } + return false; + } } diff --git a/src/main/java/org/olat/ims/qti/editor/FIBItemController.java b/src/main/java/org/olat/ims/qti/editor/FIBItemController.java index b75c9d6294604bcdd81e5c5e78fe3b2e73e17146..ed64f90a13731b610e53b3f706eae07c332287d5 100644 --- a/src/main/java/org/olat/ims/qti/editor/FIBItemController.java +++ b/src/main/java/org/olat/ims/qti/editor/FIBItemController.java @@ -38,7 +38,6 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.gui.control.generic.dialog.DialogController; import org.olat.core.gui.translator.Translator; -import org.olat.core.util.Formatter; import org.olat.core.util.Util; import org.olat.ims.qti.editor.beecom.objects.FIBQuestion; import org.olat.ims.qti.editor.beecom.objects.FIBResponse; @@ -108,14 +107,14 @@ public class FIBItemController extends DefaultController implements ControllerEv if (sPosid != null) posid = Integer.parseInt(sPosid); if (cmd.equals("up")) { if (posid > 0) { - List elements = item.getQuestion().getResponses(); - Object obj = elements.remove(posid); + List<Response> elements = item.getQuestion().getResponses(); + Response obj = elements.remove(posid); elements.add(posid - 1, obj); } } else if (cmd.equals("down")) { - List elements = item.getQuestion().getResponses(); + List<Response> elements = item.getQuestion().getResponses(); if (posid < elements.size() - 1) { - Object obj = elements.remove(posid); + Response obj = elements.remove(posid); elements.add(posid + 1, obj); } } else if (cmd.equals("editq")) { @@ -150,7 +149,7 @@ public class FIBItemController extends DefaultController implements ControllerEv FIBQuestion question = (FIBQuestion) item.getQuestion(); // Survey specific variables if (surveyMode) { - List responses = question.getResponses(); + List<Response> responses = question.getResponses(); for (int i = 0; i < responses.size(); i++) { FIBResponse response = (FIBResponse) responses.get(i); if (FIBResponse.TYPE_BLANK.equals(response.getType())) { @@ -181,7 +180,7 @@ public class FIBItemController extends DefaultController implements ControllerEv NodeBeforeChangeEvent nce = new NodeBeforeChangeEvent(); nce.setItemIdent(item.getIdent()); - List responses = question.getResponses(); + List<Response> responses = question.getResponses(); for (int i = 0; i < responses.size(); i++) { FIBResponse response = (FIBResponse) responses.get(i); nce.setResponseIdent(response.getIdent()); @@ -260,7 +259,7 @@ public class FIBItemController extends DefaultController implements ControllerEv Object position = delYesNoCtrl.getUserObject(); if(position instanceof Integer) { int pos = ((Integer)position).intValue(); - List responses = item.getQuestion().getResponses(); + List<Response> responses = item.getQuestion().getResponses(); if(!responses.isEmpty() && pos < responses.size()) { responses.remove(pos); } diff --git a/src/main/java/org/olat/ims/qti/editor/QTIEditHelper.java b/src/main/java/org/olat/ims/qti/editor/QTIEditHelper.java index 9de4b52d1e2d3e4b6f18de27e940f064276f4af6..a7e35189e1b5a5689cb851e7e344d858e90b1667 100644 --- a/src/main/java/org/olat/ims/qti/editor/QTIEditHelper.java +++ b/src/main/java/org/olat/ims/qti/editor/QTIEditHelper.java @@ -33,6 +33,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import org.dom4j.Document; @@ -108,9 +109,9 @@ public class QTIEditHelper { */ public static int countItems(Assessment assessment) { int itemCount = 0; - Iterator sectionIter = assessment.getSections().iterator(); + Iterator<Section> sectionIter = assessment.getSections().iterator(); while (sectionIter.hasNext()) { - itemCount += ((Section)sectionIter.next()).getItems().size(); + itemCount += sectionIter.next().getItems().size(); } return itemCount; } @@ -153,9 +154,9 @@ public class QTIEditHelper { newItem.setIdent(EDITOR_IDENT+":"+ITEM_TYPE_SC+":"+String.valueOf(CodeHelper.getRAMUniqueID())); newItem.setTitle(trans.translate("editor.newquestion")); newItem.setLabel(""); - // conrols + // controls Control control = new Control(); - ArrayList controls = new ArrayList(); + List<Control> controls = new ArrayList<Control>(); controls.add(control); newItem.setItemcontrols(controls); @@ -194,7 +195,7 @@ public class QTIEditHelper { // conrols Control control = new Control(); - ArrayList controls = new ArrayList(); + List<Control> controls = new ArrayList<Control>(); controls.add(control); newItem.setItemcontrols(controls); @@ -233,7 +234,7 @@ public class QTIEditHelper { // controls Control control = new Control(); - ArrayList controls = new ArrayList(); + List<Control> controls = new ArrayList<Control>(); controls.add(control); newItem.setItemcontrols(controls); @@ -289,7 +290,7 @@ public class QTIEditHelper { // conrols Control control = new Control(); - ArrayList controls = new ArrayList(); + List<Control> controls = new ArrayList<Control>(); controls.add(control); newItem.setItemcontrols(controls); @@ -326,7 +327,7 @@ public class QTIEditHelper { // conrols Control control = new Control(); - ArrayList controls = new ArrayList(); + List<Control> controls = new ArrayList<Control>(); controls.add(control); newItem.setItemcontrols(controls); @@ -408,8 +409,8 @@ public class QTIEditHelper { public static float calculateMaxScore(Question question) { float tmpScore = 0; if (question.isSingleCorrect()) return question.getSingleCorrectScore(); - for (Iterator iter = question.getResponses().iterator(); iter.hasNext();) { - Response resp = (Response) iter.next(); + for (Iterator<Response> iter = question.getResponses().iterator(); iter.hasNext();) { + Response resp = iter.next(); float points = resp.getPoints(); if (points > 0) tmpScore = tmpScore + points; } @@ -423,9 +424,9 @@ public class QTIEditHelper { * @param type * @return hasmap with responselabel_idents as keys and points as values. */ - public static HashMap fetchPoints(List respconditions, int type) { - HashMap points = new HashMap(); - for (Iterator i = respconditions.iterator(); i.hasNext();) { + public static Map<String,Float> fetchPoints(List<?> respconditions, int type) { + Map<String,Float> points = new HashMap<String,Float>(); + for (Iterator<?> i = respconditions.iterator(); i.hasNext();) { Element el_resp_condition = (Element) i.next(); ///todo float fPoints = 0; @@ -444,11 +445,14 @@ public class QTIEditHelper { Element and = conditionvar.element("and"); // in and are all choices that are true - List tmp_points = (and == null) ? conditionvar.selectNodes(".//varequal") : and.selectNodes(".//varequal"); - for (Iterator iter = tmp_points.iterator(); iter.hasNext();) { + List<?> tmp_points = (and == null) ? conditionvar.selectNodes(".//varequal") : and.selectNodes(".//varequal"); + for (Iterator<?> iter = tmp_points.iterator(); iter.hasNext();) { Element el_varequal = (Element) iter.next(); - if (type == Question.TYPE_SC || type == Question.TYPE_MC || type == Question.TYPE_KPRIM) points.put(el_varequal.getTextTrim(), new Float(fPoints)); - else if (type == Question.TYPE_FIB) points.put(el_varequal.attributeValue("respident"), new Float(fPoints)); + if (type == Question.TYPE_SC || type == Question.TYPE_MC || type == Question.TYPE_KPRIM){ + points.put(el_varequal.getTextTrim(), new Float(fPoints)); + } else if (type == Question.TYPE_FIB) { + points.put(el_varequal.attributeValue("respident"), new Float(fPoints)); + } } } } diff --git a/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java b/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java index 70d35e7f2d15459a7e43c6c96daa14ca944aa41a..054a20ec074d76effe0710ee09782c463c2ceee5 100644 --- a/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java +++ b/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java @@ -391,7 +391,7 @@ public class QTIEditorMainController extends MainLayoutBasicController implement private void updateWarning() { boolean warningEssay = false; - if(!qtiPackage.getQTIDocument().isSurvey()) { + if(qtiPackage.getQTIDocument() != null && !qtiPackage.getQTIDocument().isSurvey()) { //check if the test contains some essay List<TreeNode> flattedTree = new ArrayList<TreeNode>(); TreeHelper.makeTreeFlat(menuTreeModel.getRootNode(), flattedTree); diff --git a/src/main/java/org/olat/ims/qti/editor/beecom/objects/ChoiceQuestion.java b/src/main/java/org/olat/ims/qti/editor/beecom/objects/ChoiceQuestion.java index 13517e3f6c67b14398efbc30cf69281f7eefb813..9fdbfae92e2c3c6fe488f06041c2c4df3d65e0a1 100644 --- a/src/main/java/org/olat/ims/qti/editor/beecom/objects/ChoiceQuestion.java +++ b/src/main/java/org/olat/ims/qti/editor/beecom/objects/ChoiceQuestion.java @@ -26,9 +26,9 @@ package org.olat.ims.qti.editor.beecom.objects; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import org.dom4j.Element; import org.dom4j.Node; @@ -120,12 +120,12 @@ public class ChoiceQuestion extends Question implements QTIObject { if (resprocessingXML != null) { List respconditions = resprocessingXML.elements("respcondition"); - HashMap points = QTIEditHelper.fetchPoints(respconditions, instance.getType()); + Map<String,Float> points = QTIEditHelper.fetchPoints(respconditions, instance.getType()); // postprocessing choices for (Iterator i = choices.iterator(); i.hasNext();) { ChoiceResponse choice = (ChoiceResponse) i.next(); - Float fPoints = (Float) points.get(choice.getIdent()); + Float fPoints = points.get(choice.getIdent()); if (fPoints != null) { choice.setPoints(fPoints.floatValue()); choice.setCorrect(true); diff --git a/src/main/java/org/olat/ims/qti/editor/beecom/objects/FIBQuestion.java b/src/main/java/org/olat/ims/qti/editor/beecom/objects/FIBQuestion.java index 16b64832b05c84f6de71a4e44df0395dc453f2c0..896d68e703c29fa4b260480aae3a46b9d4f015eb 100644 --- a/src/main/java/org/olat/ims/qti/editor/beecom/objects/FIBQuestion.java +++ b/src/main/java/org/olat/ims/qti/editor/beecom/objects/FIBQuestion.java @@ -27,10 +27,10 @@ package org.olat.ims.qti.editor.beecom.objects; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.dom4j.Element; @@ -61,7 +61,7 @@ public class FIBQuestion extends Question implements QTIObject { Element presentationXML = item.element("presentation"); List elementsXML = presentationXML.element("flow").elements(); - List responses = instance.getResponses(); + List<Response> responses = instance.getResponses(); Element el_resprocessing = item.element("resprocessing"); for(Iterator i = elementsXML.iterator(); i.hasNext();) { @@ -83,7 +83,7 @@ public class FIBQuestion extends Question implements QTIObject { fibresponse.setSizeFromColumns(render_fib.attribute("columns")); fibresponse.setMaxLengthFromMaxChar(render_fib.attribute("maxchars")); List el_varequals = el_resprocessing.selectNodes(".//varequal[@respident='" + ident + "']"); - List processedSolutions = new ArrayList(); // list of already process strings + List<String> processedSolutions = new ArrayList<String>(); // list of already process strings if (el_varequals != null) { String correctBlank = ""; String correctBlankCaseAttribute = "No"; @@ -111,12 +111,12 @@ public class FIBQuestion extends Question implements QTIObject { if (resprocessingXML != null) { List respconditions = resprocessingXML.elements("respcondition"); - HashMap points = QTIEditHelper.fetchPoints(respconditions, instance.getType()); + Map<String,Float> points = QTIEditHelper.fetchPoints(respconditions, instance.getType()); // postprocessing choices for(Iterator i = responses.iterator(); i.hasNext();) { FIBResponse fibResp = (FIBResponse)i.next(); - Float fPoints = (Float)points.get(fibResp.getIdent()); + Float fPoints = points.get(fibResp.getIdent()); if (fPoints != null) { fibResp.setPoints(fPoints.floatValue()); fibResp.setCorrect(true); diff --git a/src/main/java/org/olat/ims/qti/export/QTIWordExport.java b/src/main/java/org/olat/ims/qti/export/QTIWordExport.java index 45e40673818b6242813122134c3846f6338777a0..bb3de78543f21bc3df89e5684a4b5a484083471c 100644 --- a/src/main/java/org/olat/ims/qti/export/QTIWordExport.java +++ b/src/main/java/org/olat/ims/qti/export/QTIWordExport.java @@ -19,9 +19,14 @@ */ package org.olat.ims.qti.export; +import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.servlet.http.HttpServletResponse; @@ -37,8 +42,14 @@ import org.olat.core.util.openxml.OpenXMLDocument; import org.olat.core.util.openxml.OpenXMLDocumentWriter; import org.olat.core.util.vfs.VFSContainer; import org.olat.ims.qti.container.qtielements.RenderInstructions; +import org.olat.ims.qti.editor.QTIEditHelper; import org.olat.ims.qti.editor.beecom.objects.Assessment; +import org.olat.ims.qti.editor.beecom.objects.ChoiceQuestion; +import org.olat.ims.qti.editor.beecom.objects.FIBQuestion; +import org.olat.ims.qti.editor.beecom.objects.FIBResponse; import org.olat.ims.qti.editor.beecom.objects.Item; +import org.olat.ims.qti.editor.beecom.objects.Question; +import org.olat.ims.qti.editor.beecom.objects.Response; import org.olat.ims.qti.editor.beecom.objects.Section; import org.olat.ims.qti.editor.tree.AssessmentNode; @@ -66,7 +77,8 @@ public class QTIWordExport implements MediaResource { @Override public String getContentType() { - return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; + return "application/zip"; + //return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; } @Override @@ -96,12 +108,36 @@ public class QTIWordExport implements MediaResource { } catch (Exception e) { log.error("", e); } - - String label = rootNode.getTitle(); - String file = StringHelper.transformDisplayNameToFileSystemName(label) + ".docx"; - hres.setHeader("Content-Disposition","attachment; filename=\"" + StringHelper.urlEncodeISO88591(file) + "\""); - hres.setHeader("Content-Description",StringHelper.urlEncodeISO88591(label)); - + + ZipOutputStream zout = null; + try { + String label = rootNode.getTitle(); + String secureLabel = StringHelper.transformDisplayNameToFileSystemName(label); + + String file = secureLabel + ".zip"; + hres.setHeader("Content-Disposition","attachment; filename=\"" + StringHelper.urlEncodeISO88591(file) + "\""); + hres.setHeader("Content-Description",StringHelper.urlEncodeISO88591(label)); + + zout = new ZipOutputStream(hres.getOutputStream()); + zout.setLevel(9); + + ZipEntry test = new ZipEntry(secureLabel + ".docx"); + zout.putNextEntry(test); + exportTest(zout, false); + zout.closeEntry(); + + ZipEntry responses = new ZipEntry(secureLabel + "_responses.docx"); + zout.putNextEntry(responses); + exportTest(zout, true); + zout.closeEntry(); + } catch (Exception e) { + log.error("", e); + } finally { + IOUtils.closeQuietly(zout); + } + } + + private void exportTest(OutputStream out, boolean withResponses) { String mediaBaseUrl = ""; ZipOutputStream zout = null; try { @@ -115,12 +151,12 @@ public class QTIWordExport implements MediaResource { renderSection(section, document); List<Item> items = section.getItems(); for(Item item:items) { - renderItem(item, document, mediaBaseUrl); + renderItem(item, document, mediaBaseUrl, withResponses); document.appendPageBreak(); } } - zout = new ZipOutputStream(hres.getOutputStream()); + zout = new ZipOutputStream(out); zout.setLevel(9); OpenXMLDocumentWriter writer = new OpenXMLDocumentWriter(); @@ -128,35 +164,71 @@ public class QTIWordExport implements MediaResource { } catch (Exception e) { log.error("", e); } finally { - IOUtils.closeQuietly(zout); + if(zout != null) { + try { + zout.finish(); + } catch (IOException e) { + log.error("", e); + } + } } } - private void renderItem(Item item, OpenXMLDocument document, String mediaBaseUrl) { + private void renderItem(Item item, OpenXMLDocument document, String mediaBaseUrl, boolean withResponses) { Element el = DocumentFactory.getInstance().createElement("dummy"); item.addToElement(el); + Element itemEl = (Element)el.elements().get(0); org.olat.ims.qti.container.qtielements.Item foo - = new org.olat.ims.qti.container.qtielements.Item((Element)el.elements().get(0)); - + = new org.olat.ims.qti.container.qtielements.Item(itemEl); + RenderInstructions renderInstructions = new RenderInstructions(); renderInstructions.put(RenderInstructions.KEY_STATICS_PATH, mediaBaseUrl + "/"); renderInstructions.put(RenderInstructions.KEY_LOCALE, locale); renderInstructions.put(RenderInstructions.KEY_RENDER_TITLE, Boolean.TRUE); + if(withResponses) { + + Map<String,String> iinput = new HashMap<String,String>(); + + Question question = item.getQuestion(); + if(question instanceof ChoiceQuestion) { + ChoiceQuestion choice = (ChoiceQuestion)question; + Element resprocessingXML = itemEl.element("resprocessing"); + if(resprocessingXML != null) { + List<?> respconditions = resprocessingXML.elements("respcondition"); + Map<String,Float> points = QTIEditHelper.fetchPoints(respconditions, choice.getType()); + for(String point:points.keySet()) { + iinput.put(point, point); + } + } + } else if(question instanceof FIBQuestion) { + for (Response response: question.getResponses()) { + FIBResponse fibResponse = (FIBResponse)response; + if("BLANK".equals(fibResponse.getType())) { + iinput.put(fibResponse.getIdent(), fibResponse.getCorrectBlank()); + } + } + } + + renderInstructions.put(RenderInstructions.KEY_RENDER_CORRECT_RESPONSES, Boolean.TRUE); + renderInstructions.put(RenderInstructions.KEY_CORRECT_RESPONSES_MAP, iinput); + } foo.renderOpenXML(document, renderInstructions); } + + private void renderSection(Section section, OpenXMLDocument document) { String title = section.getTitle(); document.appendHeading1(title); String objectives = section.getObjectives(); - document.appendTextParagraph(objectives); + document.appendText(objectives, true); } private void renderAssessment(Assessment assessment, OpenXMLDocument document) { String title = assessment.getTitle(); document.appendTitle(title); String objectives = assessment.getObjectives(); - document.appendTextParagraph(objectives); + document.appendText(objectives, true); } } diff --git a/src/test/java/org/olat/core/util/openxml/OpenXMLDocumentTest.java b/src/test/java/org/olat/core/util/openxml/OpenXMLDocumentTest.java index 4b9309e442bbb3c5740d842b3784cb719c36dfdf..9d3c7e670823912b69f582a5c6f4c63d96d08313 100644 --- a/src/test/java/org/olat/core/util/openxml/OpenXMLDocumentTest.java +++ b/src/test/java/org/olat/core/util/openxml/OpenXMLDocumentTest.java @@ -19,7 +19,18 @@ */ package org.olat.core.util.openxml; +import java.io.File; +import java.io.FileOutputStream; +import java.net.URL; +import java.util.zip.ZipOutputStream; + +import javax.xml.bind.Element; + +import org.apache.commons.io.IOUtils; import org.junit.Test; +import org.olat.restapi.UserMgmtTest; + +import com.sun.xml.ws.policy.privateutil.PolicyUtils.Collections; /** * @@ -28,90 +39,26 @@ import org.junit.Test; * */ public class OpenXMLDocumentTest { - /* - @Test - public void convertHtmlCode() { - String html = "<p>Ceci est un <strong>test</strong> de <span style='text-decoration: underline;'>format</span> </p><p>sans toutefois <em>une</em> table:</p>"; - OpenXMLDocument document = new OpenXMLDocument(); - document.appendHtmlText(html); - - OpenXMLUtils.writeTo(document.getDocument(), System.out, true); - }*/ - @Test - public void converLatexCode() { - String latex = "x^2 \\epsilon"; - - OpenXMLDocument document = new OpenXMLDocument(); - document.convertLaTeX(latex); - - - } - - //@Test - public void convert2Paragraph() { - String test = "<p>Hello</p><p>World</p>"; - - OpenXMLDocument document = new OpenXMLDocument(); - document.appendHtmlText(test); - - OpenXMLUtils.writeTo(document.getDocument(), System.out, true); - - } - - //@Test public void convertHtmlCode() { - String html = "<p>Ceci est un <strong>test</strong> de <span style='text-decoration: underline;'>format</span> </p><p>sans toutefois <em>une</em> table:</p>"; + String html = "<table style='height: 80px;' width='446'><tbody><tr><td>1-1</td><td colspan='2' rowspan='2'>1-21-32-32-2</td><td>1-4</td></tr><tr><td>2-1</td><td>2-4</td></tr><tr><td>3-1</td><td>3-2</td><td colspan='2'>3-33-4</td></tr></tbody></table>"; OpenXMLDocument document = new OpenXMLDocument(); - document.appendHtmlText(html); + document.appendHtmlText(html, false); OpenXMLUtils.writeTo(document.getDocument(), System.out, true); } + @Test - public void convertTableCode() { - String html = "<p>Ceci est une table</p><table style='height: 75px;' width='356'><tbody><tr><td>1 - 1</td><td><p>1 - 2</p></td><td> 1-3 avec un plus de text</td></tr><tr><td>2 - 1</td><td>Avec beaucoup de text</td><td>2 - 3</td></tr><tr><td>3 - 1 mais avec du text et encore et encore</td><td>3 - 2</td><td>3 - 3</td></tr></tbody></table><p> </p>"; - - OpenXMLDocument document = new OpenXMLDocument(); - document.appendHtmlText(html); - - OpenXMLUtils.writeTo(document.getDocument(), System.out, true); - } - - /*@Test public void writeDoc() throws Exception { FileOutputStream fileOut = new FileOutputStream(new File("/HotCoffee/tmp/test_1_min.docx")); ZipOutputStream out = new ZipOutputStream(fileOut); OpenXMLDocument document = new OpenXMLDocument(); - Element text = document.createTextEl("Hello word!"); - Element run = document.createRunEl(Collections.singletonList(text)); - Element paragraph = document.createParagraphEl(null, Collections.singletonList(run)); - document.getBodyElement().appendChild(paragraph); - - //add break page - Element breakEl = document.createPageBreakEl(); - document.getBodyElement().appendChild(breakEl); - - //add an image - URL imageUrl = UserMgmtTest.class.getResource("portrait.jpg"); - assertNotNull(imageUrl); - File image = new File(imageUrl.toURI()); - Element imgEl = document.createImageEl(image); - Element imgRun = document.createRunEl(Collections.singletonList(imgEl)); - Element imgParagraph = document.createParagraphEl(null, Collections.singletonList(imgRun)); - document.getBodyElement().appendChild(imgParagraph); - - - Element break2El = document.createPageBreakEl(); - document.getBodyElement().appendChild(break2El); - - Element text2 = document.createTextEl("Miko rule the world!"); - Element run2 = document.createRunEl(Collections.singletonList(text2)); - Element paragraph2 = document.createParagraphEl(null, Collections.singletonList(run2)); - document.getBodyElement().appendChild(paragraph2); + String html = "<table style='height: 80px;' width='446'><tbody><tr><td>1-1</td><td colspan='2' rowspan='2'>1-21-32-32-2</td><td>1-4</td></tr><tr><td>2-1</td><td>2-4</td></tr><tr><td>3-1</td><td>3-2</td><td colspan='2'>3-33-4</td></tr></tbody></table>"; + document.appendHtmlText(html, false); OpenXMLDocumentWriter writer = new OpenXMLDocumentWriter(); writer.createDocument(out, document); @@ -120,7 +67,7 @@ public class OpenXMLDocumentTest { fileOut.flush(); IOUtils.closeQuietly(out); IOUtils.closeQuietly(fileOut); - }*/ + }