diff --git a/src/main/java/org/olat/core/commons/controllers/resume/ResumeController.java b/src/main/java/org/olat/core/commons/controllers/resume/ResumeController.java index ffe200243a8b6f2f584a626c36a4f4a3b5f254be..3bcb5423e38a86a68bea830231068115eacd146a 100644 --- a/src/main/java/org/olat/core/commons/controllers/resume/ResumeController.java +++ b/src/main/java/org/olat/core/commons/controllers/resume/ResumeController.java @@ -20,6 +20,8 @@ */ package org.olat.core.commons.controllers.resume; +import java.util.List; + import org.olat.NewControllerFactory; import org.olat.admin.landingpages.LandingPagesModule; import org.olat.admin.landingpages.model.Rules; @@ -39,6 +41,7 @@ import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.id.context.BusinessControl; import org.olat.core.id.context.BusinessControlFactory; +import org.olat.core.id.context.ContextEntry; import org.olat.core.id.context.HistoryManager; import org.olat.core.id.context.HistoryModule; import org.olat.core.id.context.HistoryPoint; @@ -120,7 +123,8 @@ public class ResumeController extends FormBasicController implements SupportsAft } else if ("auto".equals(resumePrefs)) { HistoryPoint historyEntry = HistoryManager.getInstance().readHistoryPoint(ureq.getIdentity()); if(historyEntry != null && StringHelper.containsNonWhitespace(historyEntry.getBusinessPath())) { - BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(historyEntry.getEntries()); + List<ContextEntry> cloneCes = BusinessControlFactory.getInstance().cloneContextEntries(historyEntry.getEntries()); + BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(cloneCes); launch(ureq, bc); } else { String bc = getLandingBC(ureq); @@ -179,7 +183,8 @@ public class ResumeController extends FormBasicController implements SupportsAft HistoryPoint historyEntry = historyManager.readHistoryPoint(ureq.getIdentity()); if(historyEntry != null && StringHelper.containsNonWhitespace(historyEntry.getBusinessPath())) { - BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(historyEntry.getEntries()); + List<ContextEntry> cloneCes = BusinessControlFactory.getInstance().cloneContextEntries(historyEntry.getEntries()); + BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(cloneCes); WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl()); try { //make the resume secure. If something fail, don't generate a red screen diff --git a/src/main/java/org/olat/core/commons/services/image/spi/ImageHelperImpl.java b/src/main/java/org/olat/core/commons/services/image/spi/ImageHelperImpl.java index 80d34da4c078e24dc3b7e8cd50cfbab65c6cb063..08fbfbac79cbbcba75751d3708a3cec8aca3a0c7 100644 --- a/src/main/java/org/olat/core/commons/services/image/spi/ImageHelperImpl.java +++ b/src/main/java/org/olat/core/commons/services/image/spi/ImageHelperImpl.java @@ -247,6 +247,9 @@ public class ImageHelperImpl extends AbstractImageHelper { */ private ImageInputStream getInputStream(VFSLeaf leaf) throws IOException { + if(leaf == null) { + return null; + } if(leaf instanceof LocalFileImpl) { LocalFileImpl file = (LocalFileImpl)leaf; if(file.getBasefile() != null) { diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableElementImpl.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableElementImpl.java index 6c39caea27a1e4d979ed857d3132ec730727703b..a369041080712df759a97d8f58e7b732a4ebfa19 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableElementImpl.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableElementImpl.java @@ -394,7 +394,9 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle public void setSortSettings(FlexiTableSortOptions options) { this.sortOptions = options; if(options.getDefaultOrderBy() != null) { - orderBy = new SortKey[]{ options.getDefaultOrderBy() }; + SortKey defaultOrder = options.getDefaultOrderBy(); + orderBy = new SortKey[]{ defaultOrder }; + selectSortOption(defaultOrder.getKey(), defaultOrder.isAsc()); } } @@ -770,6 +772,11 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle dataSource.load(null, conditionalQueries, 0, getPageSize(), orderBy); } + selectSortOption(sortKey, asc); + component.setDirty(true); + } + + private void selectSortOption(String sortKey, boolean asc) { if(sortOptions != null) { for(FlexiTableSort sort:sortOptions.getSorts()) { boolean selected = sort.getSortKey().getKey().equals(sortKey); @@ -781,8 +788,6 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle } } } - - component.setDirty(true); } private void doFilter(String filterKey) { @@ -970,6 +975,7 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle if(sortKey != null) { orderBy = new SortKey[]{ new SortKey(sortKey, tablePrefs.isSortDirection()) }; + selectSortOption(sortKey, tablePrefs.isSortDirection()); } } diff --git a/src/main/java/org/olat/core/gui/control/generic/iframe/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/core/gui/control/generic/iframe/_i18n/LocalStrings_de.properties index ee28d32b19988ec234cf349d566bae00ddfee1f8..d75cdc3cbdd699ae4c6c7db8e76c6ae54fd33dad 100644 --- a/src/main/java/org/olat/core/gui/control/generic/iframe/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/core/gui/control/generic/iframe/_i18n/LocalStrings_de.properties @@ -21,7 +21,7 @@ encoding.same=Gleich wie Inhalt height.auto=Automatisch height.label=H\u00F6he Anzeigefl\u00E4che inherit.label=Standardwerte übernehmen -inherit=Aus Lernressourcenverwaltung übernehmen +inherit=Aus Layouteinstellungen der Lernressource \u00FCbernehmen custom=Anpassen glossary.need.jQuery=Glossar braucht jQuery automatic.need.js=Automatic braucht javascript diff --git a/src/main/java/org/olat/core/gui/control/generic/iframe/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/core/gui/control/generic/iframe/_i18n/LocalStrings_en.properties index f7fc02748a7945bb1fead76b796f555b787675fb..c7faf0818746b42443eb2dff3cbdbc9a9422f6d0 100644 --- a/src/main/java/org/olat/core/gui/control/generic/iframe/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/core/gui/control/generic/iframe/_i18n/LocalStrings_en.properties @@ -21,7 +21,7 @@ automatic.need.js=Automatic needs javascript height.auto=Automatic height.label=Display height iframe.content=Page content within iframe -inherit=Use from learning resource configuration +inherit=Use from learning resource layout configuration inherit.label=Use standard configuration mode=Display mode mode.configured=Optimized for OpenOLAT diff --git a/src/main/java/org/olat/core/id/context/BusinessControlFactory.java b/src/main/java/org/olat/core/id/context/BusinessControlFactory.java index dd21774de3e57b1fa8d1670031f1e240a6915f2e..1d9129cc1ab5186139059263b42f68e753162723 100644 --- a/src/main/java/org/olat/core/id/context/BusinessControlFactory.java +++ b/src/main/java/org/olat/core/id/context/BusinessControlFactory.java @@ -271,7 +271,15 @@ public class BusinessControlFactory { return createFromContextEntries(ces); } - //fxdiff BAKS-7 Resume function + public List<ContextEntry> cloneContextEntries(final List<ContextEntry> ces) { + final List<ContextEntry> clones = new ArrayList<ContextEntry>(ces.size()); + for(ContextEntry ce:ces) { + OLATResourceable clone = OresHelper.clone(ce.getOLATResourceable()); + clones.add(new MyContextEntry(clone)); + } + return clones; + } + public BusinessControl createFromContextEntries(final List<ContextEntry> ces) { ContextEntry rootEntry = null; if (ces.isEmpty() || ((rootEntry = ces.get(0))==null)) { diff --git a/src/main/java/org/olat/course/nodes/feed/FeedPeekviewController.java b/src/main/java/org/olat/course/nodes/feed/FeedPeekviewController.java index ff2a06bf3274206d40f4dae7448ae3d33e463c98..6291886ded82435f774b1af70d5ff70e6d5a115e 100644 --- a/src/main/java/org/olat/course/nodes/feed/FeedPeekviewController.java +++ b/src/main/java/org/olat/course/nodes/feed/FeedPeekviewController.java @@ -22,7 +22,6 @@ package org.olat.course.nodes.feed; import java.util.ArrayList; import java.util.List; -import org.olat.core.CoreSpringFactory; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.htmlsite.OlatCmdEvent; @@ -43,6 +42,7 @@ import org.olat.modules.webFeed.models.Item; import org.olat.modules.webFeed.ui.FeedUIFactory; import org.olat.resource.OLATResource; import org.olat.user.UserManager; +import org.springframework.beans.factory.annotation.Autowired; /** * <h3>Description:</h3> The feed peekview controller displays the configurable @@ -60,6 +60,11 @@ import org.olat.user.UserManager; public class FeedPeekviewController extends BasicController implements Controller { // the current course node id private final String nodeId; + + @Autowired + private FeedManager feedManager; + @Autowired + private UserManager userManager; /** * Constructor for the feed peekview controller @@ -79,43 +84,46 @@ public class FeedPeekviewController extends BasicController implements Controlle Long courseId, String nodeId, FeedUIFactory feedUIFactory, int itemsToDisplay, String wrapperCssClass) { super(ureq, wControl); this.nodeId = nodeId; - FeedManager feedManager = FeedManager.getInstance(); Feed feed = feedManager.getFeed(olatResource); - UserManager userManager = CoreSpringFactory.getImpl(UserManager.class); VelocityContainer peekviewVC = createVelocityContainer("peekview"); - peekviewVC.contextPut("wrapperCssClass", wrapperCssClass != null ? wrapperCssClass : ""); - // add gui helper - String authorFullname = userManager.getUserDisplayName(feed.getAuthor()); - FeedViewHelper helper = new FeedViewHelper(feed, getIdentity(), authorFullname, getTranslator(), courseId, nodeId, callback); - peekviewVC.contextPut("helper", helper); - // add items, only as many as configured - List<Item> allItems = feed.getFilteredItems(callback, getIdentity()); - List<Item> items = new ArrayList<Item>(); - for (int i = 0; i < allItems.size(); i++) { - if (items.size() == itemsToDisplay) { - break; - } - // add item itself if published - Item item = allItems.get(i); - if (item.isPublished()) { - items.add(item); - // add link to item - // Add link to jump to course node - Link nodeLink = LinkFactory.createLink("nodeLink_" + item.getGuid(), peekviewVC, this); - nodeLink.setCustomDisplayText(StringHelper.escapeHtml(item.getTitle())); - nodeLink.setIconLeftCSS("o_icon o_" + feed.getResourceableTypeName().replace(".", "-") + "_icon"); - nodeLink.setCustomEnabledLinkCSS("o_gotoNode"); - nodeLink.setUserObject(item.getGuid()); + if(feed == null) { + peekviewVC.contextPut("items", new ArrayList<>(1)); + peekviewVC.contextPut("errorMessage", translate("peekview.error")); + } else { + peekviewVC.contextPut("wrapperCssClass", wrapperCssClass != null ? wrapperCssClass : ""); + // add gui helper + String authorFullname = userManager.getUserDisplayName(feed.getAuthor()); + FeedViewHelper helper = new FeedViewHelper(feed, getIdentity(), authorFullname, getTranslator(), courseId, nodeId, callback); + peekviewVC.contextPut("helper", helper); + // add items, only as many as configured + List<Item> allItems = feed.getFilteredItems(callback, getIdentity()); + List<Item> items = new ArrayList<Item>(); + for (int i = 0; i < allItems.size(); i++) { + if (items.size() == itemsToDisplay) { + break; + } + // add item itself if published + Item item = allItems.get(i); + if (item.isPublished()) { + items.add(item); + // add link to item + // Add link to jump to course node + Link nodeLink = LinkFactory.createLink("nodeLink_" + item.getGuid(), peekviewVC, this); + nodeLink.setCustomDisplayText(StringHelper.escapeHtml(item.getTitle())); + nodeLink.setIconLeftCSS("o_icon o_" + feed.getResourceableTypeName().replace(".", "-") + "_icon"); + nodeLink.setCustomEnabledLinkCSS("o_gotoNode"); + nodeLink.setUserObject(item.getGuid()); + } } + peekviewVC.contextPut("items", items); + // Add link to show all items (go to node) + Link allItemsLink = LinkFactory.createLink("peekview.allItemsLink", peekviewVC, this); + allItemsLink.setIconRightCSS("o_icon o_icon_start"); + allItemsLink.setCustomEnabledLinkCSS("pull-right"); + // Add Formatter for proper date formatting + peekviewVC.contextPut("formatter", Formatter.getInstance(getLocale())); } - peekviewVC.contextPut("items", items); - // Add link to show all items (go to node) - Link allItemsLink = LinkFactory.createLink("peekview.allItemsLink", peekviewVC, this); - allItemsLink.setIconRightCSS("o_icon o_icon_start"); - allItemsLink.setCustomEnabledLinkCSS("pull-right"); - // Add Formatter for proper date formatting - peekviewVC.contextPut("formatter", Formatter.getInstance(getLocale())); putInitialPanel(peekviewVC); } diff --git a/src/main/java/org/olat/course/nodes/feed/_content/peekview.html b/src/main/java/org/olat/course/nodes/feed/_content/peekview.html index ca90c7c3c3fd0dc9ae87c433423e75ab89ffafc0..37a90fdff8b033a53f1af767bb8822613b565b6e 100644 --- a/src/main/java/org/olat/course/nodes/feed/_content/peekview.html +++ b/src/main/java/org/olat/course/nodes/feed/_content/peekview.html @@ -1,3 +1,6 @@ +#if($errorMessage) + <div class="o_error">$errorMessage</div> +#end <div class="o_feed_peekview $wrapperCssClass clearfix"> #foreach( $post in $items ) <div class="o_item"> @@ -10,5 +13,7 @@ $!helper.getItemDescriptionForBrowser($post) </div> #end +#if($r.available("peekview.allItemsLink")) $r.render("peekview.allItemsLink") +#end </div> \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/feed/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/feed/_i18n/LocalStrings_de.properties index a346215cc5d2013cb8822d83f7e0c67b1e90dbc7..f02641ff9b4b8b66c614b74d4dd28ca898b690db 100644 --- a/src/main/java/org/olat/course/nodes/feed/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/nodes/feed/_i18n/LocalStrings_de.properties @@ -9,7 +9,7 @@ command.close=Vorschau schliessen condition.accessModerator.title=Moderieren condition.accessPoster.title=Lesen und Schreiben condition.accessReader.title=Nur Lesen - +peekview.error=$org.olat.modules.webFeed.ui:feed.error peekview.allItemsLink = Alle Ausgaben help.hover.feed.mo=Hilfe, um den moderierenden Zugang zu konfigurieren help.hover.feed.r=Hilfe, um den lesenden Zugang zu konfigurieren diff --git a/src/main/java/org/olat/course/run/CourseRuntimeController.java b/src/main/java/org/olat/course/run/CourseRuntimeController.java index 64321d93cb6ded1d450aa7d66d0843affeb1fba9..5da6c4eddcf251724ce5d3488c55ec4d0456be52 100644 --- a/src/main/java/org/olat/course/run/CourseRuntimeController.java +++ b/src/main/java/org/olat/course/run/CourseRuntimeController.java @@ -330,7 +330,8 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im private void initTools(Dropdown tools, ICourse course, final UserCourseEnvironmentImpl uce) { // 1) administrative tools - if (reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR) + if (reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || reSecurity.isGroupCoach() + || hasCourseRight(CourseRights.RIGHT_COURSEEDITOR) || hasCourseRight(CourseRights.RIGHT_GROUPMANAGEMENT) || hasCourseRight(CourseRights.RIGHT_ARCHIVING) || hasCourseRight(CourseRights.RIGHT_STATISTICS) || hasCourseRight(CourseRights.RIGHT_DB) || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) { @@ -356,7 +357,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im membersLink = LinkFactory.createToolLink("unifiedusermngt", translate("command.opensimplegroupmngt"), this, "o_icon_membersmanagement"); tools.addComponent(membersLink); } - if (reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) { + if (reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || reSecurity.isGroupCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) { assessmentLink = LinkFactory.createToolLink("assessment",translate("command.openassessment"), this, "o_icon_assessment_tool"); tools.addComponent(assessmentLink); } @@ -371,7 +372,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im courseStatisticLink = LinkFactory.createToolLink("statistic",translate("command.openstatistic"), this, "o_icon_statistics_tool"); tools.addComponent(courseStatisticLink); } - if (uce != null && (reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || hasCourseRight(CourseRights.RIGHT_STATISTICS))) { + if (uce != null && (reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || reSecurity.isGroupCoach() || hasCourseRight(CourseRights.RIGHT_STATISTICS))) { final AtomicInteger testNodes = new AtomicInteger(); final AtomicInteger surveyNodes = new AtomicInteger(); new TreeVisitor(new Visitor() { @@ -746,7 +747,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im } } else if ("assessmentTool".equalsIgnoreCase(type)) { //check the security before, the link is perhaps in the wrong hands - if(reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) { + if(reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || reSecurity.isGroupCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) { try { Activateable2 assessmentCtrl = doAssessmentTool(ureq); List<ContextEntry> subEntries; @@ -762,7 +763,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im } } else if ("TestStatistics".equalsIgnoreCase(type) || "SurveyStatistics".equalsIgnoreCase(type)) { //check the security before, the link is perhaps in the wrong hands - if(reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) { + if(reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || reSecurity.isGroupCoach() || hasCourseRight(CourseRights.RIGHT_ASSESSMENT)) { try { Activateable2 assessmentCtrl = null; if("TestStatistics".equalsIgnoreCase(type)) { @@ -941,7 +942,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im OLATResourceable ores = OresHelper.createOLATResourceableType(typeName); ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores)); WindowControl swControl = addToHistory(ureq, ores, null); - if (reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || hasCourseRight(CourseRights.RIGHT_STATISTICS)) { + if (reSecurity.isEntryAdmin() || reSecurity.isCourseCoach() || reSecurity.isGroupCoach() || hasCourseRight(CourseRights.RIGHT_STATISTICS)) { removeCustomCSS(ureq); UserCourseEnvironmentImpl uce = getUserCourseEnvironment(); StatisticCourseNodesController ctrl = new StatisticCourseNodesController(ureq, swControl, toolbarPanel, uce, types); @@ -972,7 +973,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im return assessmentToolCtr; } // 2) users with coach right: limited access to coached groups - if (reSecurity.isCourseCoach()) { + if (reSecurity.isCourseCoach() || reSecurity.isGroupCoach()) { removeCustomCSS(ureq); AssessmentMainController ctrl = new AssessmentMainController(ureq, swControl, toolbarPanel, getOlatResourceable(), new CoachingGroupAccessAssessmentCallback()); @@ -1022,7 +1023,7 @@ public class CourseRuntimeController extends RepositoryEntryRuntimeController im } private void launchChat(UserRequest ureq) { - boolean vip = reSecurity.isCourseCoach() || reSecurity.isEntryAdmin(); + boolean vip = reSecurity.isCourseCoach() || reSecurity.isGroupCoach() || reSecurity.isEntryAdmin(); ICourse course = CourseFactory.loadCourse(getRepositoryEntry().getOlatResource()); OpenInstantMessageEvent event = new OpenInstantMessageEvent(ureq, course, course.getCourseTitle(), vip); ureq.getUserSession().getSingleUserEventCenter().fireEventToListenersOf(event, InstantMessagingService.TOWER_EVENT_ORES); diff --git a/src/main/java/org/olat/ims/qti/qpool/ItemFileResourceValidator.java b/src/main/java/org/olat/ims/qti/qpool/ItemFileResourceValidator.java index 74e09c085289564caab668ed823d534d503a80c1..d156d3217abde345e12c6f751266bfa01024b000 100644 --- a/src/main/java/org/olat/ims/qti/qpool/ItemFileResourceValidator.java +++ b/src/main/java/org/olat/ims/qti/qpool/ItemFileResourceValidator.java @@ -31,6 +31,10 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.commons.io.IOUtils; +import org.dom4j.Document; +import org.dom4j.DocumentType; +import org.dom4j.io.SAXReader; +import org.dom4j.io.SAXValidator; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.vfs.VFSLeaf; @@ -38,7 +42,6 @@ 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; @@ -114,6 +117,30 @@ public class ItemFileResourceValidator { } private boolean validateXml(InputStream in) { + boolean valid = false; + Document doc = readDocument(in); + if(doc != null) { + DocumentType docType = doc.getDocType(); + if(docType == null) { + doc.addDocType("questestinterop", null, "ims_qtiasiv1p2p1.dtd"); + } + valid = validateDocument(doc); + } + return valid; + } + + private Document readDocument(InputStream in) { + try { + SAXReader reader = new SAXReader(); + reader.setEntityResolver(new IMSEntityResolver()); + reader.setValidation(false); + return reader.read(in, ""); + } catch (Exception e) { + return null; + } + } + + private boolean validateDocument(Document in) { try { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setValidating(true); @@ -127,7 +154,10 @@ public class ItemFileResourceValidator { reader.setEntityResolver(new IMSEntityResolver()); reader.setErrorHandler(errorHandler); reader.setContentHandler(contentHandler); - reader.parse(new InputSource(in)); + + SAXValidator validator = new SAXValidator(reader); + validator.validate(in); + return errorHandler.isValid() && contentHandler.isItem(); } catch (ParserConfigurationException e) { return false; @@ -176,7 +206,7 @@ public class ItemFileResourceValidator { if("questestinterop".equals(qName)) { interop = true; - } else if("item".equals(localName)) { + } else if("item".equals(localName) || "item".equals(qName)) { if(interop) { item = true; } diff --git a/src/main/java/org/olat/ims/qti/qpool/QTIImportProcessor.java b/src/main/java/org/olat/ims/qti/qpool/QTIImportProcessor.java index 4f9e934a59ad62405ee22525377c9a90715aece6..6106024213e9cc9bad9dc4ad69545c1e79a83faa 100644 --- a/src/main/java/org/olat/ims/qti/qpool/QTIImportProcessor.java +++ b/src/main/java/org/olat/ims/qti/qpool/QTIImportProcessor.java @@ -636,11 +636,12 @@ class QTIImportProcessor { } private boolean processSidecarMetadata(QuestionItemImpl item, DocInfos docInfos) { + InputStream metadataIn = null; try { Path path = docInfos.root; - if(path != null) { + if(path != null && path.getFileName() != null) { Path metadata = path.resolve(path.getFileName().toString() + "_metadata.xml"); - InputStream metadataIn = Files.newInputStream(metadata); + metadataIn = Files.newInputStream(metadata); SAXReader reader = new SAXReader(); Document document = reader.read(metadataIn); Element rootElement = document.getRootElement(); @@ -654,6 +655,8 @@ class QTIImportProcessor { } catch (Exception e) { log.error("", e); return false; + } finally { + IOUtils.closeQuietly(metadataIn); } } diff --git a/src/main/java/org/olat/modules/webFeed/_spring/webFeedContext.xml b/src/main/java/org/olat/modules/webFeed/_spring/webFeedContext.xml index 3f2299d1c573d025271f3e1f4d18ff9bb9eeff16..50b7925f335a2ba15c929c5e5d6c6dbb113eff27 100644 --- a/src/main/java/org/olat/modules/webFeed/_spring/webFeedContext.xml +++ b/src/main/java/org/olat/modules/webFeed/_spring/webFeedContext.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> + http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="feedManager" class="org.olat.modules.webFeed.managers.FeedManagerImpl"> <constructor-arg index="0" ref="resourceManager"/> diff --git a/src/main/java/org/olat/modules/webFeed/ui/FeedMainController.java b/src/main/java/org/olat/modules/webFeed/ui/FeedMainController.java index a8fd512a3646109a369bc81cb2b9becbc1dee691..d4f77ba0a9e90f94d74e85133090bc9fa46f8eb8 100644 --- a/src/main/java/org/olat/modules/webFeed/ui/FeedMainController.java +++ b/src/main/java/org/olat/modules/webFeed/ui/FeedMainController.java @@ -21,7 +21,6 @@ package org.olat.modules.webFeed.ui; import java.util.List; -import org.olat.core.CoreSpringFactory; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.form.flexible.elements.FileElement; @@ -50,6 +49,7 @@ import org.olat.modules.webFeed.models.Feed; import org.olat.modules.webFeed.models.Item; import org.olat.user.UserManager; import org.olat.util.logging.activity.LoggingResourceable; +import org.springframework.beans.factory.annotation.Autowired; /** * This is the main feed layout controller. It handles everything from adding @@ -62,7 +62,6 @@ import org.olat.util.logging.activity.LoggingResourceable; */ public class FeedMainController extends BasicController implements Activateable2, GenericEventListener { - private static final FeedManager feedManager = FeedManager.getInstance(); private Feed feed; private Link editFeedButton; private CloseableModalController cmc; @@ -77,7 +76,10 @@ public class FeedMainController extends BasicController implements Activateable2 // needed for comparison private String oldFeedUrl; - private final UserManager userManager; + @Autowired + private UserManager userManager; + @Autowired + private FeedManager feedManager; /** * Constructor for learning resource (not course nodes) @@ -107,15 +109,28 @@ public class FeedMainController extends BasicController implements Activateable2 super(ureq, wControl); this.uiFactory = uiFactory; this.callback = callback; - userManager = CoreSpringFactory.getImpl(UserManager.class); setTranslator(uiFactory.getTranslator()); feed = feedManager.getFeed(ores); - String authorFullname = userManager.getUserDisplayName(feed.getAuthor()); - helper = new FeedViewHelper(feed, getIdentity(), authorFullname, uiFactory.getTranslator(), courseId, nodeId, callback); - CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, ureq.getIdentity(), feed); - display(ureq, wControl, displayConfig); - // do logging - ThreadLocalUserActivityLogger.log(FeedLoggingAction.FEED_READ, getClass(), LoggingResourceable.wrap(feed)); + if(feed == null) { + vcMain = createVelocityContainer("feed_error"); + vcMain.contextPut("errorMessage", translate("feed.error")); + putInitialPanel(vcMain); + } else { + String authorFullname = userManager.getUserDisplayName(feed.getAuthor()); + helper = new FeedViewHelper(feed, getIdentity(), authorFullname, uiFactory.getTranslator(), courseId, nodeId, callback); + CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, ureq.getIdentity(), feed); + display(ureq, wControl, displayConfig); + // do logging + ThreadLocalUserActivityLogger.log(FeedLoggingAction.FEED_READ, getClass(), LoggingResourceable.wrap(feed)); + } + } + + @Override + protected void doDispose() { + feedManager.releaseLock(lock); + if(feed != null) { + CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, feed); + } } /** @@ -156,16 +171,7 @@ public class FeedMainController extends BasicController implements Activateable2 listenTo(itemsCtr); vcMain.put("items", itemsCtr.getInitialComponent()); - this.putInitialPanel(vcMain); - } - - /** - * @see org.olat.core.gui.control.DefaultController#doDispose() - */ - @Override - protected void doDispose() { - feedManager.releaseLock(lock); - CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, feed); + putInitialPanel(vcMain); } /** diff --git a/src/main/java/org/olat/modules/webFeed/ui/_content/feed_error.html b/src/main/java/org/olat/modules/webFeed/ui/_content/feed_error.html new file mode 100644 index 0000000000000000000000000000000000000000..5ee912b6b67d579a12fe08860452d736f453d085 --- /dev/null +++ b/src/main/java/org/olat/modules/webFeed/ui/_content/feed_error.html @@ -0,0 +1,3 @@ +<div class="o_error"> + $errorMessage +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/modules/webFeed/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/webFeed/ui/_i18n/LocalStrings_de.properties index 2af164dacdd3ee60efc472433586fb910d2eb451..7f175723acdf786ef47eb7b086d50aa8619dd9a9 100644 --- a/src/main/java/org/olat/modules/webFeed/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/webFeed/ui/_i18n/LocalStrings_de.properties @@ -34,4 +34,5 @@ feed.form.minutes.error = Die Minutenangabe ist ung\u00FCltig feed.item.confirm.delete = Wollen Sie diesen Eintrag wirklich l\u00F6schen? feed.item.is.being.edited.by = Der Eintrag wird bereits von {0} bearbeitet. feed.is.being.edited.by = Die Beschreibung wird bereits von {0} bearbeitet. -feed.link.more=weiterlesen \ No newline at end of file +feed.link.more=weiterlesen +feed.error=Leider ist ein unerwartete Fehler aufgetretten und der Feed konnte nicht dargestellt werden. \ No newline at end of file diff --git a/src/main/java/org/olat/note/Note.java b/src/main/java/org/olat/note/Note.java index f088e13c6f6316e018eb0602faab6fde848f7df2..35a43ed57e04af0d1f22a39e7800ac8b66aa53f6 100644 --- a/src/main/java/org/olat/note/Note.java +++ b/src/main/java/org/olat/note/Note.java @@ -37,18 +37,18 @@ import org.olat.core.id.Persistable; * @author Alexander Schneider */ public interface Note extends CreateInfo, ModifiedInfo, Persistable { - public abstract Identity getOwner(); - public abstract String getResourceTypeName(); - public abstract Long getResourceTypeId(); - public abstract String getSubtype(); - public abstract String getNoteTitle(); - public abstract String getNoteText(); + public Identity getOwner(); + public String getResourceTypeName(); + public Long getResourceTypeId(); + public String getSubtype(); + public String getNoteTitle(); + public String getNoteText(); - public abstract void setOwner(Identity identity); - public abstract void setResourceTypeName(String resourceTypeName); - public abstract void setResourceTypeId(Long resourceTypeId); - public abstract void setSubtype(String subtype); - public abstract void setNoteTitle(String nodeTitle); - public abstract void setNoteText(String noteText); + public void setOwner(Identity identity); + public void setResourceTypeName(String resourceTypeName); + public void setResourceTypeId(Long resourceTypeId); + public void setSubtype(String subtype); + public void setNoteTitle(String nodeTitle); + public void setNoteText(String noteText); } \ No newline at end of file diff --git a/src/main/java/org/olat/note/NoteController.java b/src/main/java/org/olat/note/NoteController.java index 8c21bdbbb927770c59f2ffa2895209dea9a09b9b..8428ceefe7c3fe39d5de2a8a7087867eef29a077 100644 --- a/src/main/java/org/olat/note/NoteController.java +++ b/src/main/java/org/olat/note/NoteController.java @@ -66,7 +66,7 @@ public class NoteController extends FormBasicController implements GenericEventL private FormSubmit submitButton; @Autowired - private NoteManager nm; + private NoteManager noteManager; /** * @param ureq @@ -101,7 +101,7 @@ public class NoteController extends FormBasicController implements GenericEventL private void init(UserRequest ureq, String resourceTypeName, Long resourceTypeId, String noteTitle) { Identity owner = ureq.getIdentity(); - n = nm.loadNoteOrCreateInRAM(owner, resourceTypeName, resourceTypeId); + n = noteManager.loadNoteOrCreateInRAM(owner, resourceTypeName, resourceTypeId); n.setNoteTitle(noteTitle); // register for local event (for the same user), is used to dispose @@ -138,12 +138,12 @@ public class NoteController extends FormBasicController implements GenericEventL private void createOrUpdateNote(String content) { n.setNoteText(content); if (n.getKey() == null) { - nm.saveNote(n); + noteManager.saveNote(n); Long newKey = n.getKey(); OLATResourceable ores = OresHelper.createOLATResourceableInstance(Note.class, newKey); sec.fireEventToListenersOf(new NoteEvent(getIdentity().getKey()), ores); } else { - nm.updateNote(n); + n = noteManager.updateNote(n); } } @@ -192,9 +192,9 @@ public class NoteController extends FormBasicController implements GenericEventL createOrUpdateNote(text); // ...and then hide the submit button, show the edit button, and make the field disabled (i.e. display-only) again. - this.submitButton.setVisible(false); - this.editButton.setVisible(true); - this.noteField.setEnabled(false); + submitButton.setVisible(false); + editButton.setVisible(true); + noteField.setEnabled(false); } /** @@ -207,10 +207,10 @@ public class NoteController extends FormBasicController implements GenericEventL // persisting: see formOK // If the user clicked the edit button, set the rich text input field to enabled and hide the edit button. - if ((source == this.editButton) && (this.editButton.isEnabled())) { - this.noteField.setEnabled(true); - this.editButton.setVisible(false); - this.submitButton.setVisible(true); + if ((source == editButton) && (editButton.isEnabled())) { + noteField.setEnabled(true); + editButton.setVisible(false); + submitButton.setVisible(true); // this is to force the redraw of the form so that the submit button gets shown: flc.setDirty(true); @@ -218,8 +218,7 @@ public class NoteController extends FormBasicController implements GenericEventL // since clicking the edit button is registered as a change on the form, the submit button would get orange, // so we need the following line to set the form back to unchanged since at this point, the user has not // yet really changed anything. - this.mainForm.setDirtyMarking(false); + mainForm.setDirtyMarking(false); } } - } \ No newline at end of file diff --git a/src/main/java/org/olat/note/NoteManager.java b/src/main/java/org/olat/note/NoteManager.java index d99169878375494026367a97b09baa85e263a3aa..34034bd2f7a415070a094b401637f8791816c0eb 100644 --- a/src/main/java/org/olat/note/NoteManager.java +++ b/src/main/java/org/olat/note/NoteManager.java @@ -139,8 +139,13 @@ public class NoteManager implements UserDataDeletable { * @param n */ public Note updateNote(Note n) { - n.setLastModified(new Date()); - Note mergedNote = dbInstance.getCurrentEntityManager().merge(n); + Note reloadedNote = dbInstance.getCurrentEntityManager() + .find(NoteImpl.class, n.getKey()); + reloadedNote.setLastModified(new Date()); + reloadedNote.setNoteTitle(n.getNoteTitle()); + reloadedNote.setNoteText(n.getNoteText()); + + Note mergedNote = dbInstance.getCurrentEntityManager().merge(reloadedNote); fireBookmarkEvent(n.getOwner()); return mergedNote; } diff --git a/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties index 3ab0efb395ef44668694259c630bb14b981841a4..6b5956aab4aef40847cc9ce679f6e6e6dcac8ae5 100644 --- a/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties @@ -209,6 +209,8 @@ cif.error.description.empty=Bitte f\u00FCllen Sie das Feld "Beschreibung" aus. cif.error.displayname.empty=Bitte f\u00FCllen Sie das Feld "Titel der Lernressource" aus. cif.error.idnotint=Diese Identifikationsnummer existiert nicht. cif.error.resourcename.empty=Bitte f\u00FCllen Sie das Feld "Referenz" aus. +cif.error.image=Das Titelbild könnte nicht richtig gespeichert werden. +cif.error.movie=Der Teaser-Film könnte nicht richtig gespeichert werden. cif.expenditureOfWork=Zeitaufwand cif.externalid=Extern ID cif.externalref=Kennzeichen diff --git a/src/main/java/org/olat/repository/ui/author/AuthorListController.java b/src/main/java/org/olat/repository/ui/author/AuthorListController.java index 71b5e7ca6c4e0ff9b7ab423adff7e6f891b9ef9d..54d1efd23395b4e8622ee3045992a937f5ad16bf 100644 --- a/src/main/java/org/olat/repository/ui/author/AuthorListController.java +++ b/src/main/java/org/olat/repository/ui/author/AuthorListController.java @@ -760,8 +760,16 @@ public class AuthorListController extends FormBasicController implements Activat NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl()); } - private void launchEditDescription(UserRequest ureq, RepositoryEntryRef ref) { - String businessPath = "[RepositoryEntry:" + ref.getKey() + "][EditDescription:0]"; + private void launchEditDescription(UserRequest ureq, RepositoryEntry re) { + RepositoryHandler handler = repositoryHandlerFactory.getRepositoryHandler(re); + if(handler != null && handler.supportsLaunch()) { + String businessPath = "[RepositoryEntry:" + re.getKey() + "][EditDescription:0]"; + NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl()); + } + } + + private void launchEditDescription(UserRequest ureq, RepositoryEntryRef re) { + String businessPath = "[RepositoryEntry:" + re.getKey() + "][EditDescription:0]"; NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl()); } diff --git a/src/main/java/org/olat/repository/ui/author/ImportRepositoryEntryController.java b/src/main/java/org/olat/repository/ui/author/ImportRepositoryEntryController.java index 8c072a84ef7a3f3f38467d3369fc9a7e9c7945a9..df377b4c819e6bd6d7b4ccdd180d303a196318f7 100644 --- a/src/main/java/org/olat/repository/ui/author/ImportRepositoryEntryController.java +++ b/src/main/java/org/olat/repository/ui/author/ImportRepositoryEntryController.java @@ -199,18 +199,25 @@ public class ImportRepositoryEntryController extends FormBasicController { private void doAnalyseUpload() { File uploadedFile = uploadFileEl.getUploadFile(); - String uploadedFilename = uploadFileEl.getUploadFileName(); - - List<ResourceHandler> handlers = new ArrayList<>(3); - for(String type:repositoryHandlerFactory.getSupportedTypes()) { - RepositoryHandler handler = repositoryHandlerFactory.getRepositoryHandler(type); - ResourceEvaluation eval = handler.acceptImport(uploadedFile, uploadedFilename); - if(eval != null && eval.isValid()) { - handlers.add(new ResourceHandler(handler, eval)); + if(uploadedFile == null) {//OO-1320 + typeEl.setVisible(false); + selectType.setVisible(false); + uploadFileEl.reset(); + importButton.setEnabled(false); + } else { + String uploadedFilename = uploadFileEl.getUploadFileName(); + + List<ResourceHandler> handlers = new ArrayList<>(3); + for(String type:repositoryHandlerFactory.getSupportedTypes()) { + RepositoryHandler handler = repositoryHandlerFactory.getRepositoryHandler(type); + ResourceEvaluation eval = handler.acceptImport(uploadedFile, uploadedFilename); + if(eval != null && eval.isValid()) { + handlers.add(new ResourceHandler(handler, eval)); + } } + + updateResourceInfos(handlers); } - - updateResourceInfos(handlers); } private void updateResourceInfos(List<ResourceHandler> handlers) { diff --git a/src/main/java/org/olat/repository/ui/author/RepositoryEditDescriptionController.java b/src/main/java/org/olat/repository/ui/author/RepositoryEditDescriptionController.java index b7772ff29107abff960b6c10f18b85f21da95907..4884adb458c0cd88abc3ddb01d189b3b56365a8b 100644 --- a/src/main/java/org/olat/repository/ui/author/RepositoryEditDescriptionController.java +++ b/src/main/java/org/olat/repository/ui/author/RepositoryEditDescriptionController.java @@ -466,7 +466,7 @@ public class RepositoryEditDescriptionController extends FormBasicController { VFSLeaf newFile = fileUpload.moveUploadFileTo(container);//give it it's real name and extension boolean ok = repositoryManager.setImage(newFile, repositoryEntry); if (!ok) { - showError("Failed"); + showWarning("cif.error.image"); } else { VFSLeaf image = repositoryManager.getImage(repositoryEntry); if(image instanceof LocalFileImpl) { @@ -481,7 +481,7 @@ public class RepositoryEditDescriptionController extends FormBasicController { VFSContainer m = (VFSContainer)mediaContainer.resolve("media"); VFSLeaf newFile = movieUpload.moveUploadFileTo(m); if (newFile == null) { - showError("Failed"); + showWarning("cif.error.movie"); } else { String filename = movieUpload.getUploadFileName(); String extension = FileUtils.getFileSuffix(filename); diff --git a/src/main/java/org/olat/repository/ui/list/RepositoryEntryListController.java b/src/main/java/org/olat/repository/ui/list/RepositoryEntryListController.java index d066587d49f538a463b5db689299186af3869200..050604b9ed449abe9793b962fdee169a72d8f52b 100644 --- a/src/main/java/org/olat/repository/ui/list/RepositoryEntryListController.java +++ b/src/main/java/org/olat/repository/ui/list/RepositoryEntryListController.java @@ -239,7 +239,7 @@ public class RepositoryEntryListController extends FormBasicController } FlexiTableSortOptions options = new FlexiTableSortOptions(sorters); - options.setDefaultOrderBy(new SortKey(OrderBy.automatic.name(), true)); + options.setDefaultOrderBy(new SortKey(OrderBy.title.name(), true)); tableElement.setSortSettings(options); } diff --git a/src/test/java/org/olat/ims/qti/qpool/ItemFileResourceValidatorTest.java b/src/test/java/org/olat/ims/qti/qpool/ItemFileResourceValidatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..77bd7cd1b0deb65f04916dbf8f187c6459f46331 --- /dev/null +++ b/src/test/java/org/olat/ims/qti/qpool/ItemFileResourceValidatorTest.java @@ -0,0 +1,55 @@ +package org.olat.ims.qti.qpool; + +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; + +import junit.framework.Assert; + +import org.junit.Test; + +/** + * + * Initial date: 20.11.2014<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class ItemFileResourceValidatorTest { + + @Test + public void validate() throws URISyntaxException { + URL itemUrl = ItemFileResourceValidatorTest.class.getResource("fibi_i_001.xml"); + Assert.assertNotNull(itemUrl); + File itemFile = new File(itemUrl.toURI()); + + ItemFileResourceValidator validator = new ItemFileResourceValidator(); + boolean validate = validator.validate("fibi_i_001.xml", itemFile); + Assert.assertTrue(validate); + } + + @Test + public void validate_missingDoctype() throws URISyntaxException { + URL itemUrl = ItemFileResourceValidatorTest.class.getResource("oo_item_without_doctype.xml"); + Assert.assertNotNull(itemUrl); + File itemFile = new File(itemUrl.toURI()); + + ItemFileResourceValidator validator = new ItemFileResourceValidator(); + boolean validate = validator.validate("oo_item_without_doctype.xml", itemFile); + Assert.assertTrue(validate); + } + + @Test + public void validate_missingDoctype_invalid() throws URISyntaxException { + URL itemUrl = ItemFileResourceValidatorTest.class.getResource("oo_item_without_doctype_invalid.xml"); + Assert.assertNotNull(itemUrl); + File itemFile = new File(itemUrl.toURI()); + + ItemFileResourceValidator validator = new ItemFileResourceValidator(); + boolean validate = validator.validate("oo_item_without_doctype_invalid.xml", itemFile); + Assert.assertFalse(validate); + } + + + + +} diff --git a/src/test/java/org/olat/ims/qti/qpool/oo_item_without_doctype.xml b/src/test/java/org/olat/ims/qti/qpool/oo_item_without_doctype.xml new file mode 100644 index 0000000000000000000000000000000000000000..3353d8f1289c5511a1fc69ed20265521c5baa905 --- /dev/null +++ b/src/test/java/org/olat/ims/qti/qpool/oo_item_without_doctype.xml @@ -0,0 +1,49 @@ +<questestinterop> + <item ident="QTIEDIT:MCQ:1000002286" title="New question"> + <presentation label="New question"> + <material> + <mattext texttype="text/html"><![CDATA[New question]]></mattext> + </material> + <response_lid ident="1000002288" rcardinality="Multiple" rtiming="No"> + <render_choice shuffle="No" minnumber="0" maxnumber="1"> + <flow_label class="List"> + <response_label ident="1000002291" rshuffle="Yes"> + <material> + <mattext texttype="text/html"><![CDATA[New answer]]></mattext> + </material> + </response_label> + </flow_label> + </render_choice> + </response_lid> + </presentation> + <resprocessing> + <outcomes> + <decvar varname="SCORE" vartype="Decimal" defaultval="0" minvalue="0.0" maxvalue="1.0" cutvalue="1.0"/> + </outcomes> + <respcondition title="Mastery" continue="Yes"> + <conditionvar> + <and> + <varequal respident="1000002288" case="Yes">1000002291</varequal> + </and> + </conditionvar> + <setvar varname="SCORE" action="Set">1.0</setvar> + <displayfeedback feedbacktype="Response" linkrefid="Mastery"/> + </respcondition> + <respcondition title="_olat_resp_feedback" continue="Yes"> + <conditionvar> + <varequal respident="1000002288" case="Yes">1000002291</varequal> + </conditionvar> + <displayfeedback feedbacktype="Response" linkrefid="1000002291"/> + </respcondition> + <respcondition title="Fail" continue="Yes"> + <conditionvar> + <other/> + </conditionvar> + <setvar varname="SCORE" action="Set">0</setvar> + <displayfeedback feedbacktype="Response" linkrefid="Fail"/> + <displayfeedback feedbacktype="Solution" linkrefid="Solution"/> + <displayfeedback feedbacktype="Hint" linkrefid="Hint"/> + </respcondition> + </resprocessing> + </item> +</questestinterop> diff --git a/src/test/java/org/olat/ims/qti/qpool/oo_item_without_doctype_invalid.xml b/src/test/java/org/olat/ims/qti/qpool/oo_item_without_doctype_invalid.xml new file mode 100644 index 0000000000000000000000000000000000000000..451de66caa59d30b745bab1acd2b7228d76eacc0 --- /dev/null +++ b/src/test/java/org/olat/ims/qti/qpool/oo_item_without_doctype_invalid.xml @@ -0,0 +1,49 @@ +<questestinterop> + <itemop ident="QTIEDIT:MCQ:1000002286" title="New question"> + <presentation label="New question"> + <material> + <mattext texttype="text/html"><![CDATA[New question]]></mattext> + </material> + <response_lid ident="1000002288" rcardinality="Multiple" rtiming="No"> + <render_choice shuffle="No" minnumber="0" maxnumber="1"> + <flow_label class="List"> + <response_label ident="1000002291" rshuffle="Yes"> + <material> + <mattext texttype="text/html"><![CDATA[New answer]]></mattext> + </material> + </response_label> + </flow_label> + </render_choice> + </response_lid> + </presentation> + <resprocessing> + <outcomes> + <decvar varname="SCORE" vartype="Decimal" defaultval="0" minvalue="0.0" maxvalue="1.0" cutvalue="1.0"/> + </outcomes> + <respcondition title="Mastery" continue="Yes"> + <conditionvar> + <and> + <varequal respident="1000002288" case="Yes">1000002291</varequal> + </and> + </conditionvar> + <setvar varname="SCORE" action="Set">1.0</setvar> + <displayfeedback feedbacktype="Response" linkrefid="Mastery"/> + </respcondition> + <respcondition title="_olat_resp_feedback" continue="Yes"> + <conditionvar> + <varequal respident="1000002288" case="Yes">1000002291</varequal> + </conditionvar> + <displayfeedback feedbacktype="Response" linkrefid="1000002291"/> + </respcondition> + <respcondition title="Fail" continue="Yes"> + <conditionvar> + <other/> + </conditionvar> + <setvar varname="SCORE" action="Set">0</setvar> + <displayfeedback feedbacktype="Response" linkrefid="Fail"/> + <displayfeedback feedbacktype="Solution" linkrefid="Solution"/> + <displayfeedback feedbacktype="Hint" linkrefid="Hint"/> + </respcondition> + </resprocessing> + </itemop> +</questestinterop> diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java index 8019e90d1e8438a1a3154a37f29498834d9f22ce..34547efd2c3cc4b28f856cfa80ce787d5e46460d 100644 --- a/src/test/java/org/olat/test/AllTestsJunit4.java +++ b/src/test/java/org/olat/test/AllTestsJunit4.java @@ -144,6 +144,7 @@ import org.junit.runners.Suite; org.olat.ims.qti.QTIResultManagerTest.class, org.olat.ims.qti.qpool.QTIImportProcessorTest.class, org.olat.ims.qti.qpool.QTIExportProcessorTest.class, + org.olat.ims.qti.qpool.ItemFileResourceValidatorTest.class, org.olat.ims.qti.questionimport.CSVToQuestionConverterTest.class, org.olat.ims.qti.statistics.manager.QTIStatisticsManagerLargeTest.class, org.olat.ims.qti.statistics.manager.QTIStatisticsManagerTest.class,