diff --git a/pom.xml b/pom.xml index 64f4642997efa7ae3f6c9273a9d7f2079ce7858a..01add9d00225563fc6b79e1f74ca38d7131bcb74 100644 --- a/pom.xml +++ b/pom.xml @@ -1817,7 +1817,7 @@ <dependency> <groupId>org.openolat.jamwiki</groupId> <artifactId>jamwiki-core</artifactId> - <version>1.0.0</version> + <version>1.0.1</version> </dependency> <dependency> <groupId>opensaml</groupId> diff --git a/src/main/java/org/olat/core/commons/services/text/impl/nutch/LanguageIdentifier.java b/src/main/java/org/olat/core/commons/services/text/impl/nutch/LanguageIdentifier.java index 7cd48d66dacd3a7eebcf1e729dcae1fdbe0f9281..318baeb002463dea12834686d1a49cf02f6b9742 100644 --- a/src/main/java/org/olat/core/commons/services/text/impl/nutch/LanguageIdentifier.java +++ b/src/main/java/org/olat/core/commons/services/text/impl/nutch/LanguageIdentifier.java @@ -66,8 +66,7 @@ public class LanguageIdentifier { /** A global index of ngrams of all supported languages */ private Map<CharSequence, NGramEntry[]> ngramsIdx = new HashMap<CharSequence, NGramEntry[]>(); - /** The NGramProfile used for identification */ - private NGramProfile suspect = null; + /** @@ -136,7 +135,6 @@ public class LanguageIdentifier { } log.info(list.toString()); // Create the suspect profile - suspect = new NGramProfile("suspect", minLength, maxLength); } catch (Exception e) { log.error("", e); } @@ -172,6 +170,7 @@ public class LanguageIdentifier { text.setLength(analyzeLength); } + NGramProfile suspect = new NGramProfile("suspect", minLength, maxLength); suspect.analyze(text); Iterator<NGramEntry> iter = suspect.getSorted().iterator(); float topscore = Float.MIN_VALUE; diff --git a/src/main/java/org/olat/core/commons/services/text/impl/nutch/NGramProfile.java b/src/main/java/org/olat/core/commons/services/text/impl/nutch/NGramProfile.java index 0dd725e558fecf2bde09c50858d51776800371da..b7bb2ce8b5d6599d5074f34eeaf8cf60e746b26c 100644 --- a/src/main/java/org/olat/core/commons/services/text/impl/nutch/NGramProfile.java +++ b/src/main/java/org/olat/core/commons/services/text/impl/nutch/NGramProfile.java @@ -127,7 +127,7 @@ public class NGramProfile { * * @param w is the word to add */ - public void add(StringBuffer w) { + public void add(StringBuilder w) { for (int i=minLength; (i <= maxLength) && (i < w.length()); i++) { add(w, i); } @@ -203,7 +203,7 @@ public class NGramProfile { * @param w * @param n sequence length */ - private void add(StringBuffer w, int n) { + private void add(StringBuilder w, int n) { for (int i=0; i <= w.length()-n; i++) { add(w.subSequence(i, i + n)); } @@ -254,9 +254,10 @@ public class NGramProfile { } // Inherited JavaDoc + @Override public String toString() { - StringBuffer s = new StringBuffer().append("NGramProfile: ") + StringBuilder s = new StringBuilder().append("NGramProfile: ") .append(name).append("\n"); Iterator<NGramEntry> i = getSorted().iterator(); @@ -543,7 +544,7 @@ public class NGramProfile { * @param count is the number of occurences of this ngram */ public NGramEntry(String seq, int count) { - this.seq = new StringBuffer(seq).subSequence(0, seq.length()); + this.seq = new StringBuilder(seq).subSequence(0, seq.length()); this.count = count; } diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java index 32a612f91776795a3c89f6b81335ce484f3006d5..16dfa3f314cfdcf72b8a860767254c7484989347 100644 --- a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java +++ b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java @@ -274,18 +274,19 @@ public class WebDAVAuthManager implements AuthenticationSPI { try { dbInstance.commit(); Identity reloadedIdentity = securityManager.loadIdentityByKey(identity.getKey()); - securityManager.createAndPersistAuthentication(reloadedIdentity, provider, authUsername, digestToken, Encoder.Algorithm.md5_noSalt); + securityManager.createAndPersistAuthentication(reloadedIdentity, provider, authUsername, digestToken, Encoder.Algorithm.md5_iso_8859_1); log.audit(doer.getKey() + " created new WebDAV (HA1) authentication for identity: " + identity.getKey() + " (" + authUsername + ")"); } catch(DBRuntimeException e) { log.error("Cannot create digest password with provider " + provider + " for identity:" + identity, e); dbInstance.commit(); } } else { - String md5DigestToken = Encoder.encrypt(digestToken, null, Encoder.Algorithm.md5_noSalt); + String md5DigestToken = Encoder.encrypt(digestToken, null, Encoder.Algorithm.md5_iso_8859_1); if (!md5DigestToken.equals(authHa1.getCredential()) || !authHa1.getAuthusername().equals(authUsername)) { try { authHa1.setCredential(md5DigestToken); authHa1.setAuthusername(authUsername); + authHa1.setAlgorithm(Encoder.Algorithm.md5_iso_8859_1.name()); securityManager.updateAuthentication(authHa1); log.audit(doer.getKey() + " set new WebDAV (HA1) password for identity: " + identity.getKey() + " (" + authUsername + ")"); } catch (DBRuntimeException e) { diff --git a/src/main/java/org/olat/core/gui/render/StringOutput.java b/src/main/java/org/olat/core/gui/render/StringOutput.java index 412d7433c221ca65b2fb9712038445fd086ff987..0118b2ea155809d4f10fa95160b36813248b0592 100644 --- a/src/main/java/org/olat/core/gui/render/StringOutput.java +++ b/src/main/java/org/olat/core/gui/render/StringOutput.java @@ -207,6 +207,10 @@ public class StringOutput extends Writer { return sb.length(); } + public boolean contains(String str) { + return sb.indexOf(str) >= 0; + } + public Reader getReader() { return new StringOutputReader(); } diff --git a/src/main/java/org/olat/core/util/Encoder.java b/src/main/java/org/olat/core/util/Encoder.java index 59e88a10a7c8cea7552f3025c6e2096db9157412..1856b19dd172b80edf310898005c1435101535c0 100644 --- a/src/main/java/org/olat/core/util/Encoder.java +++ b/src/main/java/org/olat/core/util/Encoder.java @@ -27,6 +27,7 @@ package org.olat.core.util; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -52,22 +53,49 @@ public class Encoder { private static final OLog log = Tracing.createLoggerFor(Encoder.class); public enum Algorithm { - md5("MD5", 1, true), - md5_noSalt("MD5", 1, false), - sha1("SHA-1", 100, true), - sha256("SHA-256", 100, true), - sha512("SHA-512", 100, true), - pbkdf2("PBKDF2WithHmacSHA1", 20000, true), - sha256Exam("SHA-256", 1, false); + /** + * md5 with one iteration and salted (conversion string to bytes made with UTF-8) + */ + md5("MD5", 1, true, null), + /** + * md5 with one iteration without any salt (conversion string to bytes made with UTF-8) + */ + md5_noSalt("MD5", 1, false, null), + /** + * md5 with one iteration and salted (conversion string to bytes made with ISO-8859-1) + */ + md5_iso_8859_1("MD5", 1, false, "ISO-8859-1"), + /** + * SHA-1 with 100 iterations and salted + */ + sha1("SHA-1", 100, true, null), + /** + * SHA-256 with 100 iterations and salted + */ + sha256("SHA-256", 100, true, null), + /** + * SHA-512 with 100 iterations and salted + */ + sha512("SHA-512", 100, true, null), + /** + * PBKDF2 with 20'000 iterations and salted, made to be slow to prevent brute force attack + */ + pbkdf2("PBKDF2WithHmacSHA1", 20000, true, null), + /** + * SHA-256 with one iteration no salted + */ + sha256Exam("SHA-256", 1, false, null); private final boolean salted; private final int iterations; private final String algorithm; + private final Charset charset; - private Algorithm(String algorithm, int iterations, boolean salted) { + private Algorithm(String algorithm, int iterations, boolean salted, String charsetName) { this.algorithm = algorithm; this.iterations = iterations; this.salted = salted; + charset = charsetName == null ? null : Charset.forName(charsetName); } public boolean isSalted() { @@ -82,6 +110,10 @@ public class Encoder { return iterations; } + public Charset getCharset() { + return charset; + } + public static final Algorithm find(String str) { if(StringHelper.containsNonWhitespace(str)) { for(Algorithm value:values()) { @@ -106,7 +138,7 @@ public class Encoder { * @return MD5 encrypted string */ public static String md5hash(String s) { - return md5(s, null); + return md5(s, null, null); } public static String sha256Exam(String s) { @@ -128,24 +160,30 @@ public class Encoder { public static String encrypt(String s, String salt, Algorithm algorithm) { switch(algorithm) { - case md5: return md5(s, salt); + case md5: + return md5(s, salt, algorithm.getCharset()); + case md5_noSalt: + return md5(s, null, algorithm.getCharset()); + case md5_iso_8859_1: + return md5(s, salt, algorithm.getCharset()); case sha1: case sha256: case sha512: return digest(s, salt, algorithm); case pbkdf2: return secretKey(s, salt, algorithm); - default: return md5(s, salt); + default: return md5(s, salt, algorithm.getCharset()); } } - protected static String md5(String s, String salt) { + protected static String md5(String s, String salt, Charset charset) { try { - byte[] inbytes = s.getBytes(); + byte[] inbytes = charset == null ? s.getBytes() : s.getBytes(charset); MessageDigest digest = MessageDigest.getInstance(Algorithm.md5.algorithm); digest.reset(); if(salt != null) { - digest.update(salt.getBytes()); + byte[] saltbytes = charset == null ? salt.getBytes() : salt.getBytes(charset); + digest.update(saltbytes); } byte[] outbytes = digest.digest(inbytes); return md5Encoder.encode(outbytes); diff --git a/src/main/java/org/olat/core/util/Formatter.java b/src/main/java/org/olat/core/util/Formatter.java index a4356f856ae82b665312ec876973eaab32a45142..28b0ad2caaa5fb74cc8df3be14ed5afbbce4f748 100644 --- a/src/main/java/org/olat/core/util/Formatter.java +++ b/src/main/java/org/olat/core/util/Formatter.java @@ -68,9 +68,6 @@ public class Formatter { private static final Map<Locale,Formatter> localToFormatterMap = new HashMap<>(); - // Pattern to find math classes - private static final Pattern classMathPattern = Pattern.compile(".*class[ ]*=[ ]*(math|(['\"])([a-zA-Z0-9_\\- ]* )*math( [a-zA-Z0-9_\\- ]*)*\\2).*"); - private final Locale locale; private final DateFormat shortDateFormat; private final DateFormat longDateFormat; diff --git a/src/main/java/org/olat/core/util/WebappHelper.java b/src/main/java/org/olat/core/util/WebappHelper.java index 9b9431688e968da772d580a15e8ac3b1ab8ff70a..0aa920f952ac8ac8497ac97dbcce21185660c0c6 100644 --- a/src/main/java/org/olat/core/util/WebappHelper.java +++ b/src/main/java/org/olat/core/util/WebappHelper.java @@ -77,6 +77,8 @@ public class WebappHelper implements Initializable, Destroyable, ServletContextA private static String mathJaxCdn; private static String mathJaxConfig; + private static boolean mathJaxMarkers; + private static String mobileContext; /** need to set this at least once before the actual request, since we cannot extract it from the servletContext, @@ -312,6 +314,14 @@ public class WebappHelper implements Initializable, Destroyable, ServletContextA public void setMathJaxConfig(String mathJaxConfig) { WebappHelper.mathJaxConfig = mathJaxConfig; } + + public static boolean isMathJaxMarkers() { + return mathJaxMarkers; + } + + public void setMathJaxMarkers(boolean mathJaxMarkers) { + WebappHelper.mathJaxMarkers = mathJaxMarkers; + } public void setFullPathToSrc(String fullPathToSrc) { File path = new File(fullPathToSrc); diff --git a/src/main/java/org/olat/core/util/_spring/utilCorecontext.xml b/src/main/java/org/olat/core/util/_spring/utilCorecontext.xml index 3ba94af78d718724cd494ba568c72c1832380527..71919276e949e9e9f71aa88dcaeac36ddfd6dbfa 100644 --- a/src/main/java/org/olat/core/util/_spring/utilCorecontext.xml +++ b/src/main/java/org/olat/core/util/_spring/utilCorecontext.xml @@ -49,6 +49,7 @@ <property name="mobileContext" value="${mobile.context}" /> <property name="mathJaxCdn" value="${mathjax.cdn}"/> <property name="mathJaxConfig" value="${mathjax.config}"/> + <property name="mathJaxMarkers" value="${mathjax.markers}"/> </bean> <bean id="org.olat.core.helpers.Settings" class="org.olat.core.helpers.Settings" depends-on="org.olat.core.util.WebappHelper"> diff --git a/src/main/java/org/olat/core/util/filter/impl/NekoHTMLMathScanner.java b/src/main/java/org/olat/core/util/filter/impl/NekoHTMLMathScanner.java index 68c23ea336c9868a747ec535a1a51de559566c6f..f2a0ef1aa36f330d9d47813e23cc494697fc6619 100644 --- a/src/main/java/org/olat/core/util/filter/impl/NekoHTMLMathScanner.java +++ b/src/main/java/org/olat/core/util/filter/impl/NekoHTMLMathScanner.java @@ -24,8 +24,10 @@ import java.io.StringReader; import org.cyberneko.html.parsers.SAXParser; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; +import org.olat.core.util.WebappHelper; import org.xml.sax.Attributes; import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** @@ -80,5 +82,15 @@ public class NekoHTMLMathScanner { } } } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + if(!mathFound && WebappHelper.isMathJaxMarkers()) { + String content = new String(ch, start, length); + if(content.contains("\\(") || content.contains("\\[") || content.contains("$$")) { + mathFound = true; + } + } + } } } diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentItemComponentRenderer.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentItemComponentRenderer.java index 2cfa1a044ce1700da89288305387c951170f3309..cf61e54aaa4daab522cd8f9a710d9cb146d2bb37 100644 --- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentItemComponentRenderer.java +++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentItemComponentRenderer.java @@ -30,7 +30,9 @@ import org.olat.core.gui.translator.Translator; import org.olat.core.logging.OLATRuntimeException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; +import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; +import org.olat.core.util.WebappHelper; import org.olat.ims.qti21.AssessmentTestSession; import org.olat.ims.qti21.model.audit.CandidateEvent; import org.olat.ims.qti21.model.audit.CandidateItemEventType; @@ -90,6 +92,11 @@ public class AssessmentItemComponentRenderer extends AssessmentObjectComponentRe /* Render event */ AssessmentRenderer renderHints = new AssessmentRenderer(renderer); renderItemEvent(renderHints, sb, cmp, latestEvent, itemSessionState, ubu, translator); + + if(renderHints.isMathJax() + || (WebappHelper.isMathJaxMarkers() && ((sb.contains("\\(") || sb.contains("\\[") || sb.contains("&&"))))) { + sb.append(Formatter.elementLatexFormattingScript("itemBody")); + } } sb.append("</div>"); diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java index ec1193e6a76dbd39faae0200360c1a0db76037fa..39f161764afe31e3842661284d6d1917f9dc4176 100644 --- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java +++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java @@ -83,7 +83,6 @@ import org.olat.core.helpers.Settings; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.CodeHelper; -import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.ims.qti21.QTI21Constants; @@ -472,27 +471,20 @@ public abstract class AssessmentObjectComponentRenderer extends DefaultComponent } case RubricBlock.QTI_CLASS_NAME: break; //never rendered automatically case Math.QTI_CLASS_NAME: { - String domid = "mw_" + CodeHelper.getRAMUniqueID(); - sb.append("<div id=\"").append(domid).append("\">"); + sb.append("<div>"); renderMath(renderer, sb, component, resolvedAssessmentItem, itemSessionState, (Math)block); - sb.append("</div>") - .append(Formatter.elementLatexFormattingScript(domid)); + sb.append("</div>"); + renderer.setMathJax(true); break; } case Div.QTI_CLASS_NAME: { - String domid = null; if (containsClass(block, "math")) { - domid = "mw_" + CodeHelper.getRAMUniqueID(); - sb.append("<div id=\"").append(domid).append("\">"); + renderer.setMathJax(true); } renderStartHtmlTag(sb, component, resolvedAssessmentItem, block, null); ((Div)block).getFlows().forEach((flow) -> renderFlow(renderer, sb, component, resolvedAssessmentItem, itemSessionState, flow, ubu, translator)); renderEndTag(sb, block); - if (domid != null) { - sb.append("</div>") - .append(Formatter.elementLatexFormattingScript(domid)); - } break; } case Ul.QTI_CLASS_NAME: @@ -688,11 +680,10 @@ public abstract class AssessmentObjectComponentRenderer extends DefaultComponent break; } case Math.QTI_CLASS_NAME: { - String domid = "mw_" + CodeHelper.getRAMUniqueID(); - sb.append("<span id=\"").append(domid).append("\">"); + sb.append("<span>"); renderMath(renderer, sb, component, resolvedAssessmentItem, itemSessionState, (Math)inline); - sb.append("</span>") - .append(Formatter.elementLatexFormattingScript(domid)); + sb.append("</span>"); + renderer.setMathJax(true); break; } case Img.QTI_CLASS_NAME: { @@ -730,22 +721,12 @@ public abstract class AssessmentObjectComponentRenderer extends DefaultComponent protected final void renderSpan(AssessmentRenderer renderer, StringOutput sb, Span span, AssessmentObjectComponent component, ResolvedAssessmentItem resolvedAssessmentItem, ItemSessionState itemSessionState, URLBuilder ubu, Translator translator) { if (containsClass(span,"math")) { - String domid = "mw_" + CodeHelper.getRAMUniqueID(); - sb.append("<span id=\"").append(domid).append("\">"); - - renderStartHtmlTag(sb, component, resolvedAssessmentItem, span, null); - span.getInlines().forEach((child) - -> renderInline(renderer, sb, component, resolvedAssessmentItem, itemSessionState, child, ubu, translator)); - renderEndTag(sb, span); - - sb.append("</span>") - .append(Formatter.elementLatexFormattingScript(domid)); - } else { - renderStartHtmlTag(sb, component, resolvedAssessmentItem, span, null); - span.getInlines().forEach((child) - -> renderInline(renderer, sb, component, resolvedAssessmentItem, itemSessionState, child, ubu, translator)); - renderEndTag(sb, span); + renderer.setMathJax(true); } + renderStartHtmlTag(sb, component, resolvedAssessmentItem, span, null); + span.getInlines().forEach((child) + -> renderInline(renderer, sb, component, resolvedAssessmentItem, itemSessionState, child, ubu, translator)); + renderEndTag(sb, span); } protected final void renderA(AssessmentRenderer renderer, StringOutput sb, A a, AssessmentObjectComponent component, diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderer.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderer.java index 5e773414c4cda504eb35662abf276596f60427fc..c02ac5c605e0171dd6923b787970f537eff160f4 100644 --- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderer.java +++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderer.java @@ -32,6 +32,7 @@ import org.olat.core.gui.render.URLBuilder; public class AssessmentRenderer { private Renderer renderer; + private boolean mathJax; private boolean mathXsltDisabled; private boolean solutionMode; private boolean reviewMode; @@ -145,6 +146,14 @@ public class AssessmentRenderer { this.showTitles = showTitles; } + public boolean isMathJax() { + return mathJax; + } + + public void setMathJax(boolean mathJax) { + this.mathJax = mathJax; + } + public void setRenderer(Renderer renderer) { this.renderer = renderer; } diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestComponentRenderer.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestComponentRenderer.java index df888483943013e99fd4004ca158bb35e216fb23..3c48cf5ca2b6ae1ce11c3e26183d5601fdacdc04 100644 --- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestComponentRenderer.java +++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestComponentRenderer.java @@ -51,7 +51,9 @@ import org.olat.core.gui.translator.Translator; import org.olat.core.logging.OLATRuntimeException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; +import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; +import org.olat.core.util.WebappHelper; import org.olat.course.assessment.AssessmentHelper; import org.olat.ims.qti21.AssessmentTestSession; import org.olat.ims.qti21.QTI21Constants; @@ -127,6 +129,11 @@ public class AssessmentTestComponentRenderer extends AssessmentObjectComponentRe /* Render event */ AssessmentRenderer renderHints = new AssessmentRenderer(renderer); renderTestEvent(testSessionController, renderHints, sb, cmp, ubu, translator); + + if(renderHints.isMathJax() + || (WebappHelper.isMathJaxMarkers() && ((sb.contains("\\(") || sb.contains("\\[") || sb.contains("&&"))))) { + sb.append(Formatter.elementLatexFormattingScript("itemBody")); + } } } } diff --git a/src/main/java/org/olat/modules/wiki/gui/components/wikiToHtml/OlatWikiDataHandler.java b/src/main/java/org/olat/modules/wiki/gui/components/wikiToHtml/OlatWikiDataHandler.java index 0e17435af6486558da56883f5f1a4c99cf4414a7..cef8bf64a7525f304cff4aefbf9c69fe570ea5e7 100644 --- a/src/main/java/org/olat/modules/wiki/gui/components/wikiToHtml/OlatWikiDataHandler.java +++ b/src/main/java/org/olat/modules/wiki/gui/components/wikiToHtml/OlatWikiDataHandler.java @@ -146,7 +146,7 @@ public class OlatWikiDataHandler implements DataHandler { if (InterWikiHandler.isInterWiki(topic)) { return true; } - } catch (Exception e) { + } catch (Exception | Error e) { log.warn("Cannot initialize InterWikiHandler", e); } diff --git a/src/main/java/org/olat/modules/wiki/gui/components/wikiToHtml/WikiMarkupRenderer.java b/src/main/java/org/olat/modules/wiki/gui/components/wikiToHtml/WikiMarkupRenderer.java index 2b78d36222d7562c4e9e74cf5ee1874d302dfaa8..cdf4682d6d141ec2844eb8cf8d463912aa11b61a 100644 --- a/src/main/java/org/olat/modules/wiki/gui/components/wikiToHtml/WikiMarkupRenderer.java +++ b/src/main/java/org/olat/modules/wiki/gui/components/wikiToHtml/WikiMarkupRenderer.java @@ -55,14 +55,6 @@ import org.olat.core.util.Formatter; */ public class WikiMarkupRenderer extends DefaultComponentRenderer { - /** - * @see org.olat.core.gui.components.ComponentRenderer#render(org.olat.core.gui.render.Renderer, - * org.olat.core.gui.render.StringOutput, - * org.olat.core.gui.components.Component, - * org.olat.core.gui.render.URLBuilder, - * org.olat.core.gui.translator.Translator, - * org.olat.core.gui.render.RenderResult, java.lang.String[]) - */ @Override public void render(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator, RenderResult renderResult, String[] args) { @@ -97,9 +89,9 @@ public class WikiMarkupRenderer extends DefaultComponentRenderer { String targetUrl = " onclick=\"o_XHREvent(jQuery(this).attr('href'),false,true); return false;\""; input.setURLTarget(targetUrl); } - sb.append("<div style=\"min-height:"+ wikiComp.getMinHeight() +"px\" id=\""); - sb.append(uniqueId); - sb.append("\">"); + sb.append("<div style=\"min-height:").append(wikiComp.getMinHeight()).append("px\" id=\"") + .append(uniqueId) + .append("\">"); JFlexParser parser = new JFlexParser(input); parsedDoc = parser.parseHTML(wikiComp.getWikiContent()); @@ -109,12 +101,11 @@ public class WikiMarkupRenderer extends DefaultComponentRenderer { throw new OLATRuntimeException(this.getClass(), "error while rendering wiki page with content:"+ wikiComp.getWikiContent(), e); } // Use global js math formatter for latex formulas - sb.append(Formatter.formatLatexFormulas(parsedDoc.getContent())); - sb.append("</div>"); + sb.append(Formatter.formatLatexFormulas(parsedDoc.getContent())) + .append("</div>"); //set targets of media, image and external links to target "_blank" - sb.append("<script type=\"text/javascript\">/* <![CDATA[ */ "); - String instanceUrl = Settings.getServerContextPathURI(); - sb.append("changeAnchorTargets('").append(uniqueId).append("','").append(instanceUrl).append("');"); - sb.append("/* ]]> */</script>"); + sb.append("<script type=\"text/javascript\">/* <![CDATA[ */ ") + .append("changeAnchorTargets('").append(uniqueId).append("','").append(Settings.getServerContextPathURI()).append("');") + .append("/* ]]> */</script>"); } } diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties index 2d6d9e0ad9b6e079370faddf52706d7968425145..62e2daa99201caa91f67da558c455e41dc674800 100644 --- a/src/main/resources/serviceconfig/olat.properties +++ b/src/main/resources/serviceconfig/olat.properties @@ -518,6 +518,8 @@ jmx.rmi.port=3000 mathjax.cdn=//mathjax.openolat.org/mathjax/2.7-latest/ mathjax.config=TeX-AMS-MML_HTMLorMML +# find \( \), \[ \], $$ to trigger MathJax rendering +mathjax.markers=true ######################################################################## # Database settings diff --git a/src/test/java/org/olat/core/commons/services/text/TextServiceTest.java b/src/test/java/org/olat/core/commons/services/text/TextServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5817480152c415d5af0e4a71aafba8f3ab154f59 --- /dev/null +++ b/src/test/java/org/olat/core/commons/services/text/TextServiceTest.java @@ -0,0 +1,97 @@ +package org.olat.core.commons.services.text; + +import java.util.Locale; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.Assert; +import org.junit.Test; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.test.OlatTestCase; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 29 juin 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class TextServiceTest extends OlatTestCase { + + private static final OLog log = Tracing.createLoggerFor(TextServiceTest.class); + + @Autowired + private TextService textService; + + @Test + public void detectLocale_french() { + Locale locale = textService.detectLocale("Bonjour, je parle français."); + Assert.assertNotNull(locale); + Assert.assertEquals("fr", locale.getLanguage()); + } + + @Test + public void detectLocale_english() { + Locale locale = textService.detectLocale("Hi, I speak British english."); + Assert.assertNotNull(locale); + Assert.assertEquals("en", locale.getLanguage()); + } + + @Test + public void concurrentDetection() throws InterruptedException { + int numOfThreads = 10; + CountDownLatch latch = new CountDownLatch(numOfThreads); + NGramThread[] threads = new NGramThread[numOfThreads]; + for(int i=numOfThreads; i-->0; ) { + threads[i] = new NGramThread(textService, latch); + } + + for(int i=numOfThreads; i-->0; ) { + threads[i].start(); + } + + latch.await(120, TimeUnit.SECONDS); + + for(int i=numOfThreads; i-->0; ) { + if(threads[i].getException() != null) { + log.error("", threads[i].getException()); + } + Assert.assertNull(threads[i].getException()); + } + } + + private static class NGramThread extends Thread { + + private final TextService service; + private final CountDownLatch latch; + + private Exception exception; + + public NGramThread(TextService service, CountDownLatch latch) { + this.service = service; + this.latch = latch; + } + + public Exception getException() { + return exception; + } + + @Override + public void run() { + try { + Thread.sleep(100); + for(int i=0; i<2500; i++) { + Locale locale = service.detectLocale("Bonjour, je parle français."); + Assert.assertNotNull(locale); + + } + } catch (Exception e) { + exception = e; + } finally { + latch.countDown(); + } + } + } + +} diff --git a/src/test/java/org/olat/core/util/EncoderTest.java b/src/test/java/org/olat/core/util/EncoderTest.java index de7bb013083623cce7800e11154533b5798cb43d..e0180c0ad6790bc1bb146fe91c9cfebb6ebac649 100644 --- a/src/test/java/org/olat/core/util/EncoderTest.java +++ b/src/test/java/org/olat/core/util/EncoderTest.java @@ -26,6 +26,7 @@ import java.util.concurrent.TimeUnit; import org.junit.Assert; import org.junit.Test; +import org.olat.core.commons.services.webdav.manager.WebDAVManagerImpl; import org.olat.core.util.Encoder.Algorithm; /** @@ -41,7 +42,7 @@ public class EncoderTest { //the openolat password as saved on our database String openolat = "c14c4d01c090a065eaa619ca92f8cbc0"; - String hashedOpenolat_1 = Encoder.md5("openolat", null); + String hashedOpenolat_1 = Encoder.md5("openolat", null, null); Assert.assertEquals(openolat, hashedOpenolat_1); String encryptedOpenolat_1 = Encoder.md5hash("openolat"); @@ -51,12 +52,35 @@ public class EncoderTest { Assert.assertEquals(openolat, encryptedOpenolat_2); } + /** + * The test check the use of the UTF-8 or the ISO-8859-1 + * on the hashing algorithm used by the digest authentication. + */ + @Test + public void testDigestLikeCompatibility() { + // UTF-8 encoded of standard username + String rawDigest = digest("myUsername@openolat.org", "de#34KL"); + String ha1_utf8 = Encoder.encrypt(rawDigest, null, Encoder.Algorithm.md5_noSalt); + String ha1_iso = Encoder.encrypt(rawDigest, null, Encoder.Algorithm.md5_iso_8859_1); + Assert.assertEquals(ha1_utf8, ha1_iso); + + // ISO-8859-1 difference with Umlaut + String rawUmlautDigest = digest("myUsern\u00E4me@openolat.org", "de#34KL"); + String ha1_umlaut_utf8 = Encoder.encrypt(rawUmlautDigest, null, Encoder.Algorithm.md5_noSalt); + String ha1_umlaut_iso = Encoder.encrypt(rawUmlautDigest, null, Encoder.Algorithm.md5_iso_8859_1); + Assert.assertNotEquals(ha1_umlaut_utf8, ha1_umlaut_iso); + } + + private String digest(String authUsername, String password) { + return authUsername + ":" + WebDAVManagerImpl.BASIC_AUTH_REALM + ":" + password; + } + /** * Dummy test which check that the salts are not always equals. */ @Test public void testSalt() { - Set<String> history = new HashSet<String>(); + Set<String> history = new HashSet<>(); for(int i=0; i<100; i++) { String salt = Encoder.getSalt(); Assert.assertFalse(history.contains(salt)); diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java index c522d8d91989d71a3a35df8171501d4d2af2fa9d..7b3ff4d4151bd5227b0d9044a1b89076c2bc59c2 100644 --- a/src/test/java/org/olat/test/AllTestsJunit4.java +++ b/src/test/java/org/olat/test/AllTestsJunit4.java @@ -109,6 +109,7 @@ import org.junit.runners.Suite; org.olat.core.commons.services.sms.manager.MessageLogDAOTest.class, org.olat.core.commons.services.taskexecutor.PersistentTaskDAOTest.class, org.olat.core.commons.services.taskexecutor.TaskExecutorManagerTest.class, + org.olat.core.commons.services.text.TextServiceTest.class, org.olat.admin.user.delete.service.UserDeletionManagerTest.class, org.olat.group.BusinessGroupManagedFlagsTest.class, org.olat.group.test.BGRightManagerTest.class, diff --git a/src/test/java/org/olat/user/UserNameAndPasswordSyntaxCheckerWithRegexpTest.java b/src/test/java/org/olat/user/UserNameAndPasswordSyntaxCheckerWithRegexpTest.java index eb6ba5527b085090702c8f11fcdf04431a7477da..8ac186f96499df318c9419becad751b067b9b749 100644 --- a/src/test/java/org/olat/user/UserNameAndPasswordSyntaxCheckerWithRegexpTest.java +++ b/src/test/java/org/olat/user/UserNameAndPasswordSyntaxCheckerWithRegexpTest.java @@ -30,25 +30,34 @@ import org.junit.Test; */ public class UserNameAndPasswordSyntaxCheckerWithRegexpTest { + @Test + public void defaultPasswordCheck() { + UserNameAndPasswordSyntaxCheckerWithRegexp checker = new UserNameAndPasswordSyntaxCheckerWithRegexp(); + Assert.assertFalse(checker.syntaxCheckOlatPassword("Kan")); + Assert.assertTrue(checker.syntaxCheckOlatPassword("Kanu#01")); + Assert.assertFalse(checker.syntaxCheckOlatPassword("Kan\u00FC#01")); + } + /** * Min. 7 characters, one uppercase, one lowercase, one number */ @Test - public void testCustomPasswordCheck_upperLowerCase_number() { + public void customPasswordCheck_upperLowerCase_number() { UserNameAndPasswordSyntaxCheckerWithRegexp checker = new UserNameAndPasswordSyntaxCheckerWithRegexp(); checker.setPasswordRegExp("(?=^.{7,}$)((?=.*\\d)|(?=.*\\W+))(?![.\\n])(?=.*[A-Z])(?=.*[a-z]).*$"); Assert.assertTrue(checker.syntaxCheckOlatPassword("Kanu#01")); Assert.assertTrue(checker.syntaxCheckOlatPassword("Kanuunc1")); Assert.assertFalse(checker.syntaxCheckOlatPassword("Kanu#1"));//less than 7 characters - Assert.assertFalse(checker.syntaxCheckOlatPassword("Kanuunch"));//no number + Assert.assertFalse(checker.syntaxCheckOlatPassword("Kanuunch"));//no number Kan\u00FC + Assert.assertTrue(checker.syntaxCheckOlatPassword("Kan\u00FCunc1"));// Umlaut allowed } /** * Min. 8 characters, one uppercase, one lowercase, one number, one special character */ @Test - public void testCustomPasswordCheck_upperLowerCase_number_special() { + public void customPasswordCheck_upperLowerCase_number_special() { UserNameAndPasswordSyntaxCheckerWithRegexp checker = new UserNameAndPasswordSyntaxCheckerWithRegexp(); checker.setPasswordRegExp("(?=^.{8,}$)((?=.*\\d)|(?=.*\\W+))(?![.\\n])(?=.*[A-Z])(?=.*[a-z])(?=.*[$@$!%*#?&]).*$");