diff --git a/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPane.java b/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPane.java index 4973ac1828152782bb1506d0b552b689e6fe84b2..e2b13e656285a223b415230597ecc3fb1499b998 100644 --- a/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPane.java +++ b/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPane.java @@ -260,10 +260,13 @@ public class TabbedPane extends Container implements Activateable2 { @Override public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { if(entries == null || entries.isEmpty()) return; - - int pos = entries.get(0).getOLATResourceable().getResourceableId().intValue(); - if(pos != selectedPane) { - dispatchRequest(ureq, pos); + + OLATResourceable ores = entries.get(0).getOLATResourceable(); + if("tab".equals(ores.getResourceableTypeName())) { + int pos = ores.getResourceableId().intValue(); + if(pos != selectedPane && pos >= 0 && pos < getTabCount()) { + dispatchRequest(ureq, pos); + } } } } \ No newline at end of file diff --git a/src/main/java/org/olat/course/editor/EditorMainController.java b/src/main/java/org/olat/course/editor/EditorMainController.java index af6add811a7297e7d17ba07fbb27a001fa8e42d8..168c63ca1f701a900457561dfd3868a849bda21d 100644 --- a/src/main/java/org/olat/course/editor/EditorMainController.java +++ b/src/main/java/org/olat/course/editor/EditorMainController.java @@ -1180,6 +1180,9 @@ public class EditorMainController extends MainLayoutBasicController implements G } public boolean hasPublishableChanges(ICourse course) { + if(cetm == null || course == null) { + return false; + } PublishProcess publishProcess = PublishProcess.getInstance(course, cetm, getLocale()); PublishTreeModel publishTreeModel = publishProcess.getPublishTreeModel(); return publishTreeModel.hasPublishableChanges(); diff --git a/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java b/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java index 127c1fba4998267c1a220e460e7e1950abb361c1..a5d4b95417189df600d0760f5ebc2f392483bb2d 100644 --- a/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java +++ b/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java @@ -27,6 +27,7 @@ package org.olat.course.nodes; import java.util.List; +import org.olat.core.CoreSpringFactory; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; import org.olat.core.gui.control.Controller; @@ -49,6 +50,7 @@ import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.ims.lti.LTIManager; import org.olat.ims.lti.ui.LTIResultDetailsController; import org.olat.modules.ModuleConfiguration; import org.olat.repository.RepositoryEntry; @@ -61,6 +63,7 @@ import org.olat.resource.OLATResource; public class BasicLTICourseNode extends AbstractAccessableCourseNode implements AssessableCourseNode { private static final long serialVersionUID = 2210572148308757127L; + private static final String translatorPackage = Util.getPackageName(LTIEditController.class); private static final String TYPE = "lti"; public static final String CONFIG_KEY_AUTHORROLE = "authorRole"; @@ -144,8 +147,7 @@ public class BasicLTICourseNode extends AbstractAccessableCourseNode implements if (!isValid) { // FIXME: refine statusdescriptions String[] params = new String[] { this.getShortTitle() }; - String translPackage = Util.getPackageName(LTIConfigForm.class); - sd = new StatusDescription(StatusDescription.ERROR, NLS_ERROR_HOSTMISSING_SHORT, NLS_ERROR_HOSTMISSING_LONG, params, translPackage); + sd = new StatusDescription(StatusDescription.ERROR, NLS_ERROR_HOSTMISSING_SHORT, NLS_ERROR_HOSTMISSING_LONG, params, translatorPackage); sd.setDescriptionForUnit(getIdent()); // set which pane is affected by error sd.setActivateableViewIdentifier(LTIEditController.PANE_TAB_LTCONFIG); @@ -161,8 +163,8 @@ public class BasicLTICourseNode extends AbstractAccessableCourseNode implements oneClickStatusCache = null; // only here we know which translator to take for translating condition // error messages - String translatorStr = Util.getPackageName(LTIEditController.class); - List<StatusDescription> sds = isConfigValidWithTranslator(cev, translatorStr, getConditionExpressions()); + + List<StatusDescription> sds = isConfigValidWithTranslator(cev, translatorPackage, getConditionExpressions()); oneClickStatusCache = StatusDescriptionHelper.sort(sds); return oneClickStatusCache; } @@ -182,6 +184,15 @@ public class BasicLTICourseNode extends AbstractAccessableCourseNode implements public boolean needsReferenceToARepositoryEntry() { return false; } + + /** + * @see org.olat.course.nodes.CourseNode#cleanupOnDelete(org.olat.course.ICourse) + */ + @Override + public void cleanupOnDelete(ICourse course) { + OLATResource resource = course.getCourseEnvironment().getCourseGroupManager().getCourseResource(); + CoreSpringFactory.getImpl(LTIManager.class).deleteOutcomes(resource); + } /** * Update the module configuration to have all mandatory configuration flags diff --git a/src/main/java/org/olat/course/nodes/gta/ui/CoachParticipantsModelSort.java b/src/main/java/org/olat/course/nodes/gta/ui/CoachParticipantsModelSort.java index 6b5a6e33715962bc6fa1b6f44063a26720970265..c22d828a86b9cc1bda62401f2b6f568446b0ebb8 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/CoachParticipantsModelSort.java +++ b/src/main/java/org/olat/course/nodes/gta/ui/CoachParticipantsModelSort.java @@ -45,12 +45,16 @@ public class CoachParticipantsModelSort extends SortableFlexiTableModelDelegate< @Override protected void sort(List<CoachedIdentityRow> rows) { int columnIndex = getColumnIndex(); - CGCols column = CGCols.values()[columnIndex]; - switch(column) { - case taskStatus: Collections.sort(rows, new TaskStatusComparator()); break; - default: { - super.sort(rows); + if(GTACoachedGroupGradingController.USER_PROPS_OFFSET < columnIndex) { + CGCols column = CGCols.values()[columnIndex]; + switch(column) { + case taskStatus: Collections.sort(rows, new TaskStatusComparator()); break; + default: { + super.sort(rows); + } } + } else { + super.sort(rows); } } diff --git a/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java b/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java index b8561fcb7b9d7ae727a7ffc13cdf6de5e70e44b3..0407035799f9492a524ec4425618ec6f5a4c6824 100644 --- a/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java +++ b/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java @@ -317,7 +317,6 @@ public class BusinessGroupEditController extends BasicController implements Cont } @Override - //fxdiff BAKS-7 Resume function public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { if(entries == null || entries.isEmpty() || tabbedPane == null) return; tabbedPane.activate(ureq, entries, state); diff --git a/src/main/java/org/olat/ims/lti/LTIManager.java b/src/main/java/org/olat/ims/lti/LTIManager.java index 88ec60a65e184679e902d33a138e06e279e32e88..aa28560ab8eae91e2a256760095b56c93e66bd4d 100644 --- a/src/main/java/org/olat/ims/lti/LTIManager.java +++ b/src/main/java/org/olat/ims/lti/LTIManager.java @@ -50,5 +50,11 @@ public interface LTIManager { public List<LTIOutcome> loadOutcomes(Identity identity, OLATResource resource, String resSubPath); + /** + * Remove the outcomes of a resource, typically before deleting a course. + * @param resource + */ + public void deleteOutcomes(OLATResource resource); + } diff --git a/src/main/java/org/olat/ims/lti/manager/LTIManagerImpl.java b/src/main/java/org/olat/ims/lti/manager/LTIManagerImpl.java index bc2a0045af0a2ccdb97c9c5e9c22e07d77fbee72..3956a5b7d393c2ebc63e4a33aff8bee2d8d2df31 100644 --- a/src/main/java/org/olat/ims/lti/manager/LTIManagerImpl.java +++ b/src/main/java/org/olat/ims/lti/manager/LTIManagerImpl.java @@ -111,6 +111,14 @@ public class LTIManagerImpl implements LTIManager { } return outcomes.getResultList(); } + + @Override + public void deleteOutcomes(OLATResource resource) { + String q = "delete from ltioutcome as outcome where outcome.resource=:resource"; + dbInstance.getCurrentEntityManager().createQuery(q) + .setParameter("resource", resource) + .executeUpdate(); + } @Override public Map<String,String> sign(Map<String,String> props, String url, String oauthKey, String oauthSecret) { diff --git a/src/main/java/org/olat/ims/qti/editor/FIBItemController.java b/src/main/java/org/olat/ims/qti/editor/FIBItemController.java index 30513003ccb54a3f1aeffacf7f441f3a9b9eb6cd..7595021d4eebd1b9b52793736040e54249c05daa 100644 --- a/src/main/java/org/olat/ims/qti/editor/FIBItemController.java +++ b/src/main/java/org/olat/ims/qti/editor/FIBItemController.java @@ -109,26 +109,34 @@ public class FIBItemController extends BasicController implements ControllerEven int posid = 0; if (sPosid != null) posid = Integer.parseInt(sPosid); if (cmd.equals("up")) { - if (posid > 0) { - List<Response> elements = item.getQuestion().getResponses(); + List<Response> elements = item.getQuestion().getResponses(); + if (posid > 0 && posid < item.getQuestion().getResponses().size()) { Response obj = elements.remove(posid); elements.add(posid - 1, obj); + } else { + logError("posid doesn't match responses length: " + posid + "/" + elements.size(), null); } } else if (cmd.equals("down")) { List<Response> elements = item.getQuestion().getResponses(); - if (posid < elements.size() - 1) { + if (posid >= 0 && posid < elements.size() - 1) { Response obj = elements.remove(posid); elements.add(posid + 1, obj); + } else { + logError("posid doesn't match responses length: " + posid + "/" + elements.size(), null); } } else if (cmd.equals("editq")) { editQuestion = item.getQuestion().getQuestion(); displayMaterialFormController(ureq, editQuestion, restrictedEdit); } else if (cmd.equals("editr")) { - editResponse = item.getQuestion().getResponses().get(posid); - Material responseMat = item.getQuestion().getResponses().get(posid).getContent(); - displayMaterialFormController(ureq, responseMat, restrictedEdit); - + List<Response> elements = item.getQuestion().getResponses(); + if (posid >= 0 && posid < elements.size()) { + editResponse = elements.get(posid); + Material responseMat = elements.get(posid).getContent(); + displayMaterialFormController(ureq, responseMat, restrictedEdit); + } else { + logError("posid doesn't match responses length: " + posid + "/" + elements.size(), null); + } } else if (cmd.equals("addtext")) { FIBQuestion fib = (FIBQuestion) item.getQuestion(); FIBResponse response = new FIBResponse(); diff --git a/src/main/java/org/olat/modules/webFeed/ui/ItemsController.java b/src/main/java/org/olat/modules/webFeed/ui/ItemsController.java index 3814693b6e1c0d007e2281610fd4e62c3c0c775b..bc223221f073268fd15671afd1cdc0a1a30ef4b0 100644 --- a/src/main/java/org/olat/modules/webFeed/ui/ItemsController.java +++ b/src/main/java/org/olat/modules/webFeed/ui/ItemsController.java @@ -616,7 +616,7 @@ public class ItemsController extends BasicController implements Activateable2 { naviCtr.add(currentItem); // ... and also to the helper helper.addItem(currentItem); - if (feed.getItems().size() == 1) { + if (feed.getItems() != null && feed.getItems().size() == 1) { // First item added, show feed url (for subscription) fireEvent(ureq, ItemsController.FEED_INFO_IS_DIRTY_EVENT); // Set the base URI of the feed for the current user. All users diff --git a/src/test/java/org/olat/ims/lti/LTIManagerTest.java b/src/test/java/org/olat/ims/lti/LTIManagerTest.java index 051d650f91020645f456eb6f32eb64eec8875b0d..5ed7b16ada0da01c691f44374bc505e095d13832 100644 --- a/src/test/java/org/olat/ims/lti/LTIManagerTest.java +++ b/src/test/java/org/olat/ims/lti/LTIManagerTest.java @@ -96,6 +96,33 @@ public class LTIManagerTest extends OlatTestCase { Assert.assertTrue(outcomes.contains(outcome2)); } - - + @Test + public void deleteOutcome() { + Identity id = JunitTestHelper.createAndPersistIdentityAsUser("lti-3-" + UUID.randomUUID().toString()); + OLATResource resource1 = JunitTestHelper.createRandomResource(); + OLATResource resource2 = JunitTestHelper.createRandomResource(); + LTIOutcome outcome1 = ltiManager.createOutcome(id, resource1, "sub", "update", "new-outcome", "lti score value"); + LTIOutcome outcome2 = ltiManager.createOutcome(id, resource1, "sub", "delete", "new-outcome", null); + LTIOutcome outcome3 = ltiManager.createOutcome(id, resource2, "sub", "delete", "new-outcome", null); + dbInstance.commitAndCloseSession(); + + //check they exist + List<LTIOutcome> outcomes1 = ltiManager.loadOutcomes(id, resource1, "sub"); + Assert.assertEquals(2, outcomes1.size()); + Assert.assertTrue(outcomes1.contains(outcome1)); + Assert.assertTrue(outcomes1.contains(outcome2)); + List<LTIOutcome> outcomes2 = ltiManager.loadOutcomes(id, resource2, "sub"); + Assert.assertEquals(1, outcomes2.size()); + + //delete outcomes of resource 1 + ltiManager.deleteOutcomes(resource1); + dbInstance.commitAndCloseSession(); + + //check that only the outcome of resource 2 survives + List<LTIOutcome> checkOutcomes1 = ltiManager.loadOutcomes(id, resource1, "sub"); + Assert.assertTrue(checkOutcomes1.isEmpty()); + List<LTIOutcome> checkOutcomes2 = ltiManager.loadOutcomes(id, resource2, "sub"); + Assert.assertEquals(1, checkOutcomes2.size()); + Assert.assertTrue(checkOutcomes2.contains(outcome3)); + } }