diff --git a/src/main/java/org/olat/ims/qti21/AssessmentTestSession.java b/src/main/java/org/olat/ims/qti21/AssessmentTestSession.java index a46a69d496ccfa44f16ec129a6e2d466d3b93083..55ed4b405965707fd38009f3df2b4c45de713f10 100644 --- a/src/main/java/org/olat/ims/qti21/AssessmentTestSession.java +++ b/src/main/java/org/olat/ims/qti21/AssessmentTestSession.java @@ -112,7 +112,8 @@ public interface AssessmentTestSession extends CreateInfo, ModifiedInfo { public String getAnonymousIdentifier(); - + public boolean isAuthorMode(); + public AssessmentEntry getAssessmentEntry(); public RepositoryEntry getTestEntry(); diff --git a/src/main/java/org/olat/ims/qti21/QTI21Service.java b/src/main/java/org/olat/ims/qti21/QTI21Service.java index d883b38e070f695734293e018440698e97b89220..b951c29fed082fb237513b26008973cc20f225fd 100644 --- a/src/main/java/org/olat/ims/qti21/QTI21Service.java +++ b/src/main/java/org/olat/ims/qti21/QTI21Service.java @@ -151,13 +151,23 @@ public interface QTI21Service { * Remove all test sessions in author mode, e.g. after an assessment test * was changed. * - * @param testEntry + * @param testEntry The test repository entry + * @return + */ + public boolean deleteAuthorsAssessmentTestSession(RepositoryEntryRef testEntry); + + /** + * Remove a test sessions in author mode, e.g. after an assessment test + * was changed. + * + * @param testEntry The test repository entry + * @param testSession The session of the author * @return */ - public boolean deleteAuthorAssessmentTestSession(RepositoryEntryRef testEntry); + public boolean deleteAuthorAssessmentTestSession(RepositoryEntryRef testEntry, AssessmentTestSession testSession); /** - * Delete a specific test session. + * Delete a specific preview test session. * * @param testSession * @return diff --git a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java index 841551c90f9913dc5e6ee2e2aa915bf3cd31c3ab..2c0959f4a4a0908a266b0d4ea122756fdf47da06 100644 --- a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java +++ b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java @@ -458,8 +458,8 @@ public class QTI21ServiceImpl implements QTI21Service, UserDataDeletable, Initia } @Override - public boolean deleteAuthorAssessmentTestSession(RepositoryEntryRef testEntry) { - log.info(Tracing.M_AUDIT, "Delete author assessment sessions for test: " + testEntry); + public boolean deleteAuthorsAssessmentTestSession(RepositoryEntryRef testEntry) { + log.info(Tracing.M_AUDIT, "Delete author assessment sessions for test: {}", testEntry); List<AssessmentTestSession> sessions = testSessionDao.getAuthorAssessmentTestSession(testEntry); for(AssessmentTestSession session:sessions) { File fileStorage = testSessionDao.getSessionStorage(session); @@ -470,6 +470,16 @@ public class QTI21ServiceImpl implements QTI21Service, UserDataDeletable, Initia return true; } + @Override + public boolean deleteAuthorAssessmentTestSession(RepositoryEntryRef testEntry, AssessmentTestSession session) { + log.info(Tracing.M_AUDIT, "Delete author assessment sessions for test: {}", testEntry); + File fileStorage = testSessionDao.getSessionStorage(session); + testSessionDao.deleteTestSession(session); + FileUtils.deleteDirsAndFiles(fileStorage, true, true); + dbInstance.commit();// make sure it's flushed on the database + return true; + } + @Override public boolean deleteAssessmentTestSession(AssessmentTestSession testSession) { if(testSession == null || testSession.getKey() == null) return false; diff --git a/src/main/java/org/olat/ims/qti21/model/InMemoryAssessmentTestSession.java b/src/main/java/org/olat/ims/qti21/model/InMemoryAssessmentTestSession.java index b9eae61adc5874b1abac9dafea4e8c568fa77719..f9cf254155775e9c2f6a8935728e9a3ed3a4fdd2 100644 --- a/src/main/java/org/olat/ims/qti21/model/InMemoryAssessmentTestSession.java +++ b/src/main/java/org/olat/ims/qti21/model/InMemoryAssessmentTestSession.java @@ -203,6 +203,11 @@ public class InMemoryAssessmentTestSession implements AssessmentTestSession { return anonymousIdentifier; } + @Override + public boolean isAuthorMode() { + return false; + } + @Override public AssessmentEntry getAssessmentEntry() { return null; diff --git a/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentTestSessionImpl.java b/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentTestSessionImpl.java index d7c67f923561c906512fba8828ecf62cd31339c4..536e3e87faad0d50b883ca8ac5818cfae6218602 100644 --- a/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentTestSessionImpl.java +++ b/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentTestSessionImpl.java @@ -240,6 +240,7 @@ public class AssessmentTestSessionImpl implements AssessmentTestSession, Persist this.anonymousIdentifier = identifier; } + @Override public boolean isAuthorMode() { return authorMode; } diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java index 371f1c906084efef1e16fe7a96340dbf936869e7..5cccb71d98fcf8df2dc4c2ec584acf0e2cd94742 100644 --- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java +++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java @@ -97,6 +97,7 @@ import org.olat.ims.qti21.ui.ResponseInput.StringInput; import org.olat.ims.qti21.ui.components.AssessmentTestFormItem; import org.olat.ims.qti21.ui.components.AssessmentTestTimerFormItem; import org.olat.ims.qti21.ui.components.AssessmentTreeFormItem; +import org.olat.ims.qti21.ui.event.RestartEvent; import org.olat.ims.qti21.ui.event.RetrieveAssessmentTestSessionEvent; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.AssessmentService; @@ -756,9 +757,17 @@ public class AssessmentTestDisplayController extends BasicController implements case rubric: toogleRubric(qe.getSubCommand()); break; + case restart: + restartTest(ureq); + break; } } + private void restartTest(UserRequest ureq) { + if(!candidateSession.isAuthorMode()) return; + fireEvent(ureq, new RestartEvent()); + } + private void toogleMark(String itemRef) { marks = getMarkerObject(); diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21RuntimeController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21RuntimeController.java index b6cc6815ceb551d05a2cf382aa38233667843884..014ce54b4d3808efe4201acd3c17ad1e2de67f03 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21RuntimeController.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21RuntimeController.java @@ -35,10 +35,12 @@ import org.olat.core.id.OLATResourceable; import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; import org.olat.core.util.resource.OresHelper; import org.olat.fileresource.FileResourceManager; +import org.olat.ims.qti21.AssessmentTestSession; import org.olat.ims.qti21.QTI21Constants; import org.olat.ims.qti21.QTI21Service; import org.olat.ims.qti21.model.xml.QtiNodesExtractor; import org.olat.ims.qti21.ui.editor.AssessmentTestComposerController; +import org.olat.ims.qti21.ui.event.RestartEvent; import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.modules.assessment.ui.AssessableResource; import org.olat.modules.assessment.ui.AssessmentToolController; @@ -105,6 +107,11 @@ public class QTI21RuntimeController extends RepositoryEntryRuntimeController { if(event == Event.CHANGED_EVENT) { reloadRuntime = true; } + } else if(source instanceof AssessmentTestDisplayController) { + if(event instanceof RestartEvent) { + AssessmentTestDisplayController ctrl = (AssessmentTestDisplayController)source; + doRestartRunningRuntimeController(ureq, ctrl.getCandidateSession()); + } } super.event(ureq, source, event); } @@ -135,11 +142,34 @@ public class QTI21RuntimeController extends RepositoryEntryRuntimeController { } super.event(ureq, source, event); } + + /** + * The method will clean up only the test session of the author. + * + * @param ureq The user request + * @param candidateSession The test session of the author + */ + private void doRestartRunningRuntimeController(UserRequest ureq, AssessmentTestSession candidateSession) { + disposeRuntimeController(); + if(reSecurity.isEntryAdmin()) { + qtiService.deleteAuthorAssessmentTestSession(getRepositoryEntry(), candidateSession); + } + launchContent(ureq, reSecurity); + if(toolbarPanel.getTools().isEmpty()) { + initToolbar(); + } + reloadRuntime = false; + } + /** + * The method will clean up all authors sessions. + * + * @param ureq The user request + */ private void doReloadRuntimeController(UserRequest ureq) { disposeRuntimeController(); if(reSecurity.isEntryAdmin()) { - qtiService.deleteAuthorAssessmentTestSession(getRepositoryEntry()); + qtiService.deleteAuthorsAssessmentTestSession(getRepositoryEntry()); } launchContent(ureq, reSecurity); if(toolbarPanel.getTools().isEmpty()) { diff --git a/src/main/java/org/olat/ims/qti21/ui/QTIWorksAssessmentTestEvent.java b/src/main/java/org/olat/ims/qti21/ui/QTIWorksAssessmentTestEvent.java index 854391ea9d0a98728b685bd4488c2203c23a2dc9..fa33e3991dba0977152f1ae32122bda759ea656b 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTIWorksAssessmentTestEvent.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTIWorksAssessmentTestEvent.java @@ -56,7 +56,8 @@ public class QTIWorksAssessmentTestEvent extends FormEvent { timesUp("times-up", "times-up"), tmpResponse("response-tmp", "response-tmp"), mark("mark", "mark"), - rubric("rubric", "rubric"); + rubric("rubric", "rubric"), + restart("restart", "restart"); private final String path; private final String event; diff --git a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties index 9963e5377ac02b3411e8b45476b34f4b2134b684..9ac861defce0876640fe94e021f1cd90b2dab894 100644 --- a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties @@ -55,6 +55,8 @@ assessment.test.nextQuestion=N\u00E4chste Frage assessment.test.notReleased=$org.olat.ims.qti\:notReleased assessment.test.open=Offen assessment.test.questionMenu=Menu-Navigation Test +assessment.test.restart.test=Start den Test neu +assessment.test.restart.test.explanation=Neu start steht nur f\u00FCr Autoren zur Verf\u00FCgung. assessment.test.session.reseted=Die Testergebnisse wurde wahrscheinlich von dem Kursadministrator zur\u00FCckgesetzt. assessment.test.suspended=Der Test wurde unterbrochen. assessment.testpart.config=Test part diff --git a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties index 68bb384fb7eb11d3fe34a211bbc917df4f8580a2..53cf7ead9a6a8f9a667277281c0497f7061d4cc6 100644 --- a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties @@ -55,6 +55,8 @@ assessment.test.nextQuestion=Next question assessment.test.notReleased=$org.olat.ims.qti\:notReleased assessment.test.open=Open assessment.test.questionMenu=Test question menu +assessment.test.restart.test=Restart the test +assessment.test.restart.test.explanation=Restart is only available to authors. assessment.test.session.reseted=The results of the test was probably reseted by a course administrator. assessment.test.suspended=The test has been suspended. assessment.testpart.config=Test part 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 6534f5f817118e862316419526fff3839387f143..6926b1a3733aa99309ddab11805d95002a905626 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 @@ -107,7 +107,7 @@ public class AssessmentTestComponentRenderer extends AssessmentObjectComponentRe TestSessionController testSessionController = cmp.getTestSessionController(); if(testSessionController.getTestSessionState().isEnded()) { - renderTerminated(sb, translator); + renderTestTerminated(sb, cmp, translator); } else { /* Create appropriate options that link back to this controller */ TestSessionState testSessionState = testSessionController.getTestSessionState(); @@ -117,7 +117,7 @@ public class AssessmentTestComponentRenderer extends AssessmentObjectComponentRe if (candidateSession.isExploded()) { renderExploded(sb, translator); } else if (candidateSessionContext.isTerminated()) { - renderTerminated(sb, translator); + renderTestTerminated(sb, cmp, translator); } else { /* Touch the session's duration state if appropriate */ if (testSessionState.isEntered() && !testSessionState.isEnded()) { @@ -147,7 +147,7 @@ public class AssessmentTestComponentRenderer extends AssessmentObjectComponentRe /* If session has terminated, render appropriate state and exit */ final TestSessionState testSessionState = testSessionController.getTestSessionState(); if (candidateSessionContext.isTerminated() || testSessionState.isExited()) { - renderTerminated(target, translator); + renderTestTerminated(target, component, translator); } else if (testEventType == CandidateTestEventType.REVIEW_ITEM) { renderer.setReviewMode(true); TestPlanNodeKey itemKey = extractTargetItemKey(candidateEvent); @@ -188,6 +188,21 @@ public class AssessmentTestComponentRenderer extends AssessmentObjectComponentRe } } + private void renderTestTerminated(StringOutput sb, AssessmentTestComponent component, Translator translator) { + renderTerminated(sb, translator); + + CandidateSessionContext candidateSessionContext = component.getCandidateSessionContext(); + final AssessmentTestSession candidateSession = candidateSessionContext.getCandidateSession(); + if(candidateSession.isAuthorMode()) { + sb.append("<div class='o_button_group'>"); + String title = translator.translate("assessment.test.restart.test"); + String explanation = translator.translate("assessment.test.restart.test.explanation"); + renderControl(sb, component, title, explanation, true, "o_sel_enter_test", + new NameValuePair("cid", Event.restart.name())); + sb.append("</div>"); + } + } + private void renderTestEntry(StringOutput sb, AssessmentTestComponent component, Translator translator) { int numOfParts = component.getAssessmentTest().getTestParts().size(); sb.append("<h4>").append(translator.translate("test.entry.page.title")).append("</h4>") @@ -196,12 +211,24 @@ public class AssessmentTestComponentRenderer extends AssessmentObjectComponentRe .append("</div><div class='o_button_group'>"); //precondition -> up to String title = translator.translate("assessment.test.enter.test"); - renderControl(sb, component, title, true, "o_sel_enter_test", + renderControl(sb, component, title, null, true, "o_sel_enter_test", new NameValuePair("cid", Event.advanceTestPart.name())); sb.append("</div>"); } + private void renderControl(StringOutput sb, AssessmentTestComponent component, String title, String explanation, boolean primary, String cssClass, NameValuePair... pairs) { + Form form = component.getQtiItem().getRootForm(); + String dispatchId = component.getQtiItem().getFormDispatchId(); + sb.append("<button type='button' ") + .onClickKeyEnter(FormJSHelper.getXHRFnCallFor(form, dispatchId, 1, true, true, pairs)) + .append(" class='btn ").append("btn-primary ", "btn-default ", primary).append(cssClass).append("'"); + if(StringHelper.containsNonWhitespace(explanation)) { + sb.append(" title=\"").append(explanation).append("\""); + } + sb.append("><span>").append(title).append("</span></button>"); + } + private void renderTestItem(AssessmentRenderer renderer, StringOutput sb, AssessmentTestComponent component, TestPlanNodeKey itemRefKey, URLBuilder ubu, Translator translator, RenderingRequest options) { final TestSessionController testSessionController = component.getTestSessionController(); @@ -236,40 +263,40 @@ public class AssessmentTestComponentRenderer extends AssessmentObjectComponentRe //advanceTestItemAllowed /* && testSessionState.getCurrentItemKey() != null && testSessionController.mayAdvanceItemLinear() */ if(options.isAdvanceTestItemAllowed() ) {//TODO need to find if there is a next question String title = translator.translate("assessment.test.nextQuestion"); - renderControl(sb, component, title, false, "o_sel_next_question", new NameValuePair("cid", Event.finishItem.name())); + renderControl(sb, component, title, null, false, "o_sel_next_question", new NameValuePair("cid", Event.finishItem.name())); } //nextItem if(options.isNextItemAllowed() && testSessionController.hasFollowingNonLinearItem()) { String title = translator.translate("assessment.test.nextQuestion"); - renderControl(sb, component, title, false, "o_sel_next_question", new NameValuePair("cid", Event.nextItem.name())); + renderControl(sb, component, title, null, false, "o_sel_next_question", new NameValuePair("cid", Event.nextItem.name())); } //testPartNavigationAllowed" if(options.isTestPartNavigationAllowed() && component.isRenderNavigation()) { String title = translator.translate("assessment.test.questionMenu"); - renderControl(sb, component, title, false, "o_sel_question_menu", new NameValuePair("cid", Event.testPartNavigation.name())); + renderControl(sb, component, title, null, false, "o_sel_question_menu", new NameValuePair("cid", Event.testPartNavigation.name())); } //endTestPartAllowed if(options.isEndTestPartAllowed()) { String title = component.hasMultipleTestParts() ? translator.translate("assessment.test.end.testPart") : translator.translate("assessment.test.end.test"); - renderControl(sb, component, title, false, "o_sel_end_testpart", new NameValuePair("cid", Event.endTestPart.name())); + renderControl(sb, component, title, null, false, "o_sel_end_testpart", new NameValuePair("cid", Event.endTestPart.name())); } //reviewMode if(options.isReviewMode()) { String title = translator.translate("assessment.test.backToTestFeedback"); - renderControl(sb, component, title, false, "o_sel_back_test_feedback", new NameValuePair("cid", Event.reviewTestPart.name())); + renderControl(sb, component, title, null, false, "o_sel_back_test_feedback", new NameValuePair("cid", Event.reviewTestPart.name())); } // <xsl:variable name="provideItemSolutionButton" as="xs:boolean" select="$reviewMode and $showSolution and not($solutionMode)"/> if(options.isReviewMode() && effectiveItemSessionControl.isShowSolution() && !options.isSolutionMode()) { String title = translator.translate("assessment.solution.show"); - renderControl(sb, component, title, false, "o_sel_show_solution", + renderControl(sb, component, title, null, false, "o_sel_show_solution", new NameValuePair("cid", Event.itemSolution.name()), new NameValuePair("item", key)); } if(options.isReviewMode() && options.isSolutionMode()) { String title = translator.translate("assessment.solution.hide"); - renderControl(sb, component, title, false, "o_sel_solution_hide", + renderControl(sb, component, title, null, false, "o_sel_solution_hide", new NameValuePair("cid", Event.reviewItem.name()), new NameValuePair("item", key)); } sb.append("</div>");//end controls diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestFormItem.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestFormItem.java index f027c7f2c04cbd4acae0385073edf10ec401a9e6..7a4ac1505180101feddd850d0d94bd153b0896ca 100644 --- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestFormItem.java +++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestFormItem.java @@ -25,6 +25,7 @@ import static org.olat.ims.qti21.ui.QTIWorksAssessmentTestEvent.Event.exitTest; import static org.olat.ims.qti21.ui.QTIWorksAssessmentTestEvent.Event.finishItem; import static org.olat.ims.qti21.ui.QTIWorksAssessmentTestEvent.Event.itemSolution; import static org.olat.ims.qti21.ui.QTIWorksAssessmentTestEvent.Event.nextItem; +import static org.olat.ims.qti21.ui.QTIWorksAssessmentTestEvent.Event.restart; import static org.olat.ims.qti21.ui.QTIWorksAssessmentTestEvent.Event.reviewItem; import static org.olat.ims.qti21.ui.QTIWorksAssessmentTestEvent.Event.reviewTestPart; import static org.olat.ims.qti21.ui.QTIWorksAssessmentTestEvent.Event.rubric; @@ -230,6 +231,10 @@ public class AssessmentTestFormItem extends AssessmentObjectFormItem { event = new QTIWorksAssessmentTestEvent(rubric, selectedSection, this); break; } + case restart: { + event = new QTIWorksAssessmentTestEvent(restart, this); + break; + } default: { event = null; } diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java index 636c181d3422c2797686b785bea91ebef2fbf714..1eca92857a47dd723b8ad14c665bf70490d80a21 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java @@ -796,6 +796,10 @@ public class AssessmentTestComposerController extends MainLayoutBasicController private void doInsert(UserRequest ureq, List<QuestionItemView> items) { TreeNode selectedNode = menuTree.getSelectedNode(); TreeNode sectionNode = getNearestSection(selectedNode); + if(sectionNode == null) { + showWarning("error.missing.section"); + return; + } boolean allOk = true; String firstItemId = null; @@ -845,6 +849,10 @@ public class AssessmentTestComposerController extends MainLayoutBasicController private void doInsert(UserRequest ureq, AssessmentItemsPackage importPackage) { TreeNode selectedNode = menuTree.getSelectedNode(); TreeNode sectionNode = getNearestSection(selectedNode); + if(sectionNode == null) { + showWarning("error.missing.section"); + return; + } String firstItemId = null; boolean errorOnImport = false; @@ -990,7 +998,7 @@ public class AssessmentTestComposerController extends MainLayoutBasicController TreeNode rootNode = menuTree.getTreeModel().getRootNode(); AssessmentTest assessmentTest = (AssessmentTest)rootNode.getUserObject(); List<TestPart> parts = assessmentTest.getTestParts(); - if(parts != null && parts.size() > 0) { + if(parts != null && !parts.isEmpty()) { parentPart = parts.get(0); } else { showWarning("error.cannot.create.section"); @@ -1034,6 +1042,10 @@ public class AssessmentTestComposerController extends MainLayoutBasicController private void doNewAssessmentItem(UserRequest ureq, TreeNode selectedNode, AssessmentItemBuilder itemBuilder) { try { TreeNode sectionNode = getNearestSection(selectedNode); + if(sectionNode == null) { + showWarning("error.missing.section"); + return; + } AssessmentSection section = (AssessmentSection)sectionNode.getUserObject(); AssessmentItemRef itemRef = new AssessmentItemRef(section); @@ -1508,7 +1520,7 @@ public class AssessmentTestComposerController extends MainLayoutBasicController if(!deleteAuthorSesssion) { deleteAuthorSesssion = true;//delete sessions only once - qtiService.deleteAuthorAssessmentTestSession(testEntry); + qtiService.deleteAuthorsAssessmentTestSession(testEntry); fireEvent(ureq, Event.CHANGED_EVENT); } } diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties index 6c4967be0b07e3750b9d001abcb2499c678a85ee..2de18b06514907ef5e8638632bd7813086ed7be6 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties @@ -49,6 +49,7 @@ error.mimetype=$org.olat.core.commons.modules.bc\:WrongMimeType error.min.score.bigger.max=Minimal erreichbare Punktzahl muss kleiner als then maximal erreichbare Punktzahl sein. error.missing.fib=Die Frage mussen mindestens einen L\u00FCckentext oder eine Numerische Eingabe enthalten. error.missing.hottext=Die Frage mussen mindestens einen Hottext enthalten. +error.missing.section=Sie m\u00FCssen mindestens eine Sektion im Test oder Test Part haben. error.need.correct.answer=Sie m\u00FCssen mindestens eine Antwort als korrekt markieren. error.positive.double=Falsches Zahlenformat, nur positive Nummer sind erlaubt. Beispiele\: 15.0, 5.5, 10 error.singlechoice=Genau ein ausw\u00E4hlen diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties index 402e824fbc0f2095d91c2b3c213fa72e3a65a907..39055cfd0125b7b061475e64972ecd796fa454b2 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties @@ -49,6 +49,7 @@ error.mimetype=$org.olat.core.commons.modules.bc\:WrongMimeType error.min.score.bigger.max=Min. score must be bigger than the max. score. error.missing.fib=The question need at least a gap text or a numerical input. error.missing.hottext=The question need at least a hottext. +error.missing.section=You must have at least one section in your test or test part. error.need.correct.answer=You need a least one correct answer. error.positive.double=Only positive number are allowed. Example\: 15.0, 5.5, 10 error.singlechoice=Choose exactly one diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBEditorController.java index 80b13a40f6f5d85884ff4c06738bb64c64f8d932..1afbb0ca03d2d7d303a3f80ffc1d5b716279cb52 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBEditorController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/FIBEditorController.java @@ -117,13 +117,10 @@ public class FIBEditorController extends FormBasicController { if(!hasNumericals && !hasTexts) { if(preferredType == QTI21QuestionType.numerical) { hasNumericals = true; - hasTexts = false; } else if(preferredType == QTI21QuestionType.fib) { hasNumericals = false; - hasTexts = true; } else { hasNumericals = true; - hasTexts = true; } } if(hasNumericals) { @@ -131,7 +128,7 @@ public class FIBEditorController extends FormBasicController { } else { setFormContextHelp("Test editor QTI 2.1 in detail#details_testeditor_fragetypen_fib"); } - richTextConfig.enableQTITools(hasTexts, hasNumericals, false); + richTextConfig.enableQTITools(true, true, false); // Submit Button FormLayoutContainer buttonsContainer = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); diff --git a/src/main/java/org/olat/ims/qti21/ui/event/RestartEvent.java b/src/main/java/org/olat/ims/qti21/ui/event/RestartEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..d339c279d995f618d2be8c07bf7f8caaf379afbc --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/ui/event/RestartEvent.java @@ -0,0 +1,39 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.ims.qti21.ui.event; + +import org.olat.core.gui.control.Event; + +/** + * + * Initial date: 11 juin 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class RestartEvent extends Event { + + private static final long serialVersionUID = -7101011728830529141L; + private static final String RESTART_TEST = "restart-assessment-test"; + + public RestartEvent() { + super(RESTART_TEST); + } + +} diff --git a/src/main/java/org/olat/modules/curriculum/ui/CurriculumListManagerController.java b/src/main/java/org/olat/modules/curriculum/ui/CurriculumListManagerController.java index 1bb56c4240946f25668c644ef94f832ced9b70e3..cc45df0e7f52c9c7446c42870daa8fee1e7662c2 100644 --- a/src/main/java/org/olat/modules/curriculum/ui/CurriculumListManagerController.java +++ b/src/main/java/org/olat/modules/curriculum/ui/CurriculumListManagerController.java @@ -123,7 +123,8 @@ public class CurriculumListManagerController extends FormBasicController impleme columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, CurriculumCols.externalId, "select")); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(CurriculumCols.numOfElements)); DefaultFlexiColumnModel editCol = new DefaultFlexiColumnModel("edit.icon", CurriculumCols.edit.ordinal(), "edit", - new BooleanCellRenderer(new StaticFlexiCellRenderer(translate("edit"), "edit"), null)); + new BooleanCellRenderer(new StaticFlexiCellRenderer(translate("edit"), "edit"), + new StaticFlexiCellRenderer(translate("select"), "edit"))); editCol.setExportable(false); columnsModel.addFlexiColumnModel(editCol); if(secCallback.canEditCurriculum()) { @@ -166,7 +167,7 @@ public class CurriculumListManagerController extends FormBasicController impleme private CurriculumRow forgeManagedRow(CurriculumInfos curriculum) { FormLink toolsLink = uifactory.addFormLink("tools_" + (++counter), "tools", "", null, null, Link.NONTRANSLATED); toolsLink.setIconLeftCSS("o_icon o_icon_actions o_icon-lg"); - CurriculumRow row = new CurriculumRow(curriculum, toolsLink, true); + CurriculumRow row = new CurriculumRow(curriculum, toolsLink, secCallback.canEditCurriculum()); toolsLink.setUserObject(row); return row; }