diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/elements/FlexiTableElement.java b/src/main/java/org/olat/core/gui/components/form/flexible/elements/FlexiTableElement.java index 18174b0e23f6c55256b79342964e13c8cbb043ba..1623beea125068c01e9bca168c1ed0d4b3bba3a2 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/elements/FlexiTableElement.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/elements/FlexiTableElement.java @@ -380,6 +380,14 @@ public interface FlexiTableElement extends FormItem { */ public void reset(boolean page, boolean internal, boolean reloadData); + /** + * It will reload all the data without filter. Use it with cautious as + * at some place, there are minimal restrictions to the search string. + * + * @param ureq + */ + public void resetSearch(UserRequest ureq); + public void reloadData(); /** 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 71e78716a52bf90ceea9e663b2db35dad5a6f7d4..094ef037ac676593239304979bb3437c8ecce93d 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 @@ -1295,7 +1295,7 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle if(StringHelper.containsNonWhitespace(search)) { doSearch(ureq, FlexiTableSearchEvent.QUICK_SEARCH, search, null); } else { - doResetSearch(ureq); + resetSearch(ureq); } } @@ -1367,7 +1367,8 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle search, getSelectedFilters(), getSelectedExtendedFilters(), condQueries, FormEvent.ONCLICK)); } - protected void doResetSearch(UserRequest ureq) { + @Override + public void resetSearch(UserRequest ureq) { conditionalQueries = null; currentPage = 0; if(dataSource != null) { diff --git a/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java b/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java index 37dced270d73df11f297c153d4f58aadf5afb7c9..0b8c19d7f7bc43d40e2f2661c4fc72d5da426985 100644 --- a/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java +++ b/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java @@ -63,6 +63,7 @@ import org.olat.ims.qti.export.QTIExportFormatterCSVType2; import org.olat.ims.qti.export.QTIExportManager; import org.olat.ims.qti.fileresource.TestFileResource; import org.olat.ims.qti.process.AssessmentInstance; +import org.olat.ims.qti21.manager.AssessmentTestSessionDAO; import org.olat.modules.ModuleConfiguration; import org.olat.modules.iq.IQManager; import org.olat.modules.iq.IQSecurityCallback; @@ -230,12 +231,15 @@ public class IQSELFCourseNode extends AbstractAccessableCourseNode implements Se */ @Override public void cleanupOnDelete(ICourse course) { - // Delete all qtiresults for this node. No properties used on this node + // 1) Delete all qtiresults for this node. No properties used on this node String repositorySoftKey = (String) getModuleConfiguration().get(IQEditController.CONFIG_KEY_REPOSITORY_SOFTKEY); RepositoryEntry re = RepositoryManager.getInstance().lookupRepositoryEntryBySoftkey(repositorySoftKey, false); if(re != null) { QTIResultManager.getInstance().deleteAllResults(course.getResourceableId(), getIdent(), re.getKey()); } + // 2) Delete all assessment test sessions (QTI 2.1) + RepositoryEntry courseEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); + CoreSpringFactory.getImpl(AssessmentTestSessionDAO.class).deleteAllUserTestSessionsByCourse(courseEntry, getIdent()); } @Override diff --git a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java index d552e9434517f44fcb07beb95305eb3ce891ab66..e1f3f1128b92c42ee8c47ad06e6b35d959159a10 100644 --- a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java +++ b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java @@ -90,6 +90,7 @@ import org.olat.ims.qti.statistics.ui.QTI12PullTestsToolController; import org.olat.ims.qti.statistics.ui.QTI12StatisticsToolController; import org.olat.ims.qti21.QTI21DeliveryOptions; import org.olat.ims.qti21.QTI21Service; +import org.olat.ims.qti21.manager.AssessmentTestSessionDAO; import org.olat.ims.qti21.manager.archive.QTI21ArchiveFormat; import org.olat.ims.qti21.model.QTI21StatisticSearchParams; import org.olat.ims.qti21.ui.QTI21AssessmentDetailsController; @@ -612,12 +613,15 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements Pe // 1) Delete all properties: score, passed, log, comment, coach_comment, // attempts pm.deleteNodeProperties(this, null); - // 2) Delete all qtiresults for this node + // 2) Delete all qtiresults for this node (QTI 1.2 + qtiworks) String repositorySoftKey = (String) getModuleConfiguration().get(IQEditController.CONFIG_KEY_REPOSITORY_SOFTKEY); RepositoryEntry re = RepositoryManager.getInstance().lookupRepositoryEntryBySoftkey(repositorySoftKey, false); if(re != null) { QTIResultManager.getInstance().deleteAllResults(course.getResourceableId(), getIdent(), re.getKey()); } + // 3) Delete all assessment test sessions (QTI 2.1) + RepositoryEntry courseEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); + CoreSpringFactory.getImpl(AssessmentTestSessionDAO.class).deleteAllUserTestSessionsByCourse(courseEntry, getIdent()); } @Override diff --git a/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java b/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java index db3d90f9b6e02a03b9135f4717c1a176b17eaa4d..cde3cd51bc0e8d4d95c0684f9e3dc4d55db03578 100644 --- a/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java +++ b/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java @@ -422,7 +422,7 @@ public class AssessmentTestSessionDAO { * @param testEntry * @return */ - public int deleteAllUserTestSessions(RepositoryEntryRef testEntry) { + public int deleteAllUserTestSessionsByTest(RepositoryEntryRef testEntry) { String marksSb = "delete from qtiassessmentmarks marks where marks.testEntry.key=:testEntryKey"; int marks = dbInstance.getCurrentEntityManager() .createQuery(marksSb) @@ -457,4 +457,82 @@ public class AssessmentTestSessionDAO { .executeUpdate(); return marks + itemSessions + sessions + responses; } + + + public int deleteAllUserTestSessionsByCourse(RepositoryEntryRef entry, String subIdent) { + String marksSb = "delete from qtiassessmentmarks marks where marks.repositoryEntry.key=:entryKey and marks.subIdent=:subIdent"; + int marks = dbInstance.getCurrentEntityManager() + .createQuery(marksSb) + .setParameter("entryKey", entry.getKey()) + .setParameter("subIdent", subIdent) + .executeUpdate(); + + StringBuilder responseSb = new StringBuilder(); + responseSb.append("delete from qtiassessmentresponse response where") + .append(" response.assessmentItemSession.key in (") + .append(" select itemSession from qtiassessmentitemsession itemSession, qtiassessmenttestsession session ") + .append(" where itemSession.assessmentTestSession.key=session.key and session.repositoryEntry.key=:entryKey and session.subIdent=:subIdent") + .append(" )"); + int responses = dbInstance.getCurrentEntityManager() + .createQuery(responseSb.toString()) + .setParameter("entryKey", entry.getKey()) + .setParameter("subIdent", subIdent) + .executeUpdate(); + + StringBuilder itemSb = new StringBuilder(); + itemSb.append("delete from qtiassessmentitemsession itemSession") + .append(" where itemSession.assessmentTestSession.key in(") + .append(" select session.key from qtiassessmenttestsession session where session.repositoryEntry.key=:entryKey and session.subIdent=:subIdent") + .append(" )"); + int itemSessions = dbInstance.getCurrentEntityManager() + .createQuery(itemSb.toString()) + .setParameter("entryKey", entry.getKey()) + .setParameter("subIdent", subIdent) + .executeUpdate(); + + String q = "delete from qtiassessmenttestsession session where session.repositoryEntry.key=:entryKey and session.subIdent=:subIdent"; + int sessions = dbInstance.getCurrentEntityManager() + .createQuery(q) + .setParameter("entryKey", entry.getKey()) + .setParameter("subIdent", subIdent) + .executeUpdate(); + return marks + itemSessions + sessions + responses; + } + + + public int deleteAllUserTestSessionsByCourse(RepositoryEntryRef entry) { + String marksSb = "delete from qtiassessmentmarks marks where marks.repositoryEntry.key=:entryKey"; + int marks = dbInstance.getCurrentEntityManager() + .createQuery(marksSb) + .setParameter("entryKey", entry.getKey()) + .executeUpdate(); + + StringBuilder responseSb = new StringBuilder(); + responseSb.append("delete from qtiassessmentresponse response where") + .append(" response.assessmentItemSession.key in (") + .append(" select itemSession from qtiassessmentitemsession itemSession, qtiassessmenttestsession session ") + .append(" where itemSession.assessmentTestSession.key=session.key and session.repositoryEntry.key=:entryKey") + .append(" )"); + int responses = dbInstance.getCurrentEntityManager() + .createQuery(responseSb.toString()) + .setParameter("entryKey", entry.getKey()) + .executeUpdate(); + + StringBuilder itemSb = new StringBuilder(); + itemSb.append("delete from qtiassessmentitemsession itemSession") + .append(" where itemSession.assessmentTestSession.key in(") + .append(" select session.key from qtiassessmenttestsession session where session.repositoryEntry.key=:entryKey") + .append(" )"); + int itemSessions = dbInstance.getCurrentEntityManager() + .createQuery(itemSb.toString()) + .setParameter("entryKey", entry.getKey()) + .executeUpdate(); + + String q = "delete from qtiassessmenttestsession session where session.repositoryEntry.key=:entryKey"; + int sessions = dbInstance.getCurrentEntityManager() + .createQuery(q) + .setParameter("entryKey", entry.getKey()) + .executeUpdate(); + return marks + itemSessions + sessions + responses; + } } diff --git a/src/main/java/org/olat/ims/qti21/pool/QTI21ExportProcessor.java b/src/main/java/org/olat/ims/qti21/pool/QTI21ExportProcessor.java index 18ad4a54030512718afa93bbe458c18fbdcb4354..e17a38786670f2088356b5b44b9e12d2ce244420 100644 --- a/src/main/java/org/olat/ims/qti21/pool/QTI21ExportProcessor.java +++ b/src/main/java/org/olat/ims/qti21/pool/QTI21ExportProcessor.java @@ -38,7 +38,6 @@ import org.olat.core.util.FileUtils; import org.olat.core.util.StringHelper; import org.olat.core.util.ZipUtil; import org.olat.core.util.io.ShieldOutputStream; -import org.olat.core.util.vfs.VFSLeaf; import org.olat.ims.qti21.QTI21Service; import org.olat.ims.qti21.model.xml.AssessmentTestFactory; import org.olat.ims.qti21.model.xml.ManifestBuilder; @@ -47,12 +46,15 @@ import org.olat.imscp.xml.manifest.ResourceType; import org.olat.modules.qpool.QuestionItemFull; import org.olat.modules.qpool.manager.QPoolFileStorage; +import uk.ac.ed.ph.jqtiplus.node.content.xhtml.image.Img; +import uk.ac.ed.ph.jqtiplus.node.content.xhtml.object.Object; import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem; import uk.ac.ed.ph.jqtiplus.node.item.interaction.Interaction; import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection; import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest; import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem; import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer; +import uk.ac.ed.ph.jqtiplus.utils.QueryUtils; /** * @@ -122,8 +124,12 @@ public class QTI21ExportProcessor { //write materials for(ItemMaterial material:itemAndMaterials.getMaterials()) { String exportPath = material.getExportUri(); - File leaf = new File(editorContainer, exportPath); - FileUtils.bcopy(leaf, editorContainer, "Export to QTI 2.1 editor"); + File originalFile = material.getFile(); + File exportFile = new File(editorContainer, exportPath); + if(!exportFile.getParentFile().exists()) { + exportFile.getParentFile().mkdirs(); + } + FileUtils.bcopy(originalFile, exportFile, "Copy material QTI 2.1"); } return assessmentItem; } @@ -138,11 +144,35 @@ public class QTI21ExportProcessor { ResolvedAssessmentItem assessmentItem = qtiService.loadAndResolveAssessmentItem(itemFile.toURI(), resourceDirectory); //enrichScore(itemEl); //enrichWithMetadata(fullItem, itemEl); - //collectResources(itemEl, container, materials); + collectResources(assessmentItem.getRootNodeLookup().extractIfSuccessful(), itemFile, materials); materials.addItemEl(assessmentItem); } } + protected void collectResources(AssessmentItem item, File itemFile, AssessmentItemsAndResources materials) { + File directory = itemFile.getParentFile(); + + QueryUtils.search(Img.class, item).forEach((img) -> { + if(img.getSrc() != null) { + String imgPath = img.getSrc().toString(); + File imgFile = new File(directory, imgPath); + if(imgFile.exists()) { + materials.addMaterial(new ItemMaterial(imgFile, imgPath)); + } + } + }); + + QueryUtils.search(Object.class, item).forEach((object) -> { + if(StringHelper.containsNonWhitespace(object.getData())) { + String path = object.getData(); + File objectFile = new File(directory, path); + if(objectFile.exists()) { + materials.addMaterial(new ItemMaterial(objectFile, path)); + } + } + }); + } + public void enrichWithMetadata(QuestionItemFull qitem, ResolvedAssessmentItem resolvedAssessmentItem, ManifestBuilder manifestBuilder) { ResourceType resource = manifestBuilder.getResourceTypeByHref(qitem.getRootFilename()); if(resource == null) { @@ -331,8 +361,8 @@ public class QTI21ExportProcessor { private static final class AssessmentItemsAndResources { private final Set<String> paths = new HashSet<String>(); - private final List<ResolvedAssessmentItem> itemEls = new ArrayList<ResolvedAssessmentItem>(); - private final List<ItemMaterial> materials = new ArrayList<ItemMaterial>(); + private final List<ResolvedAssessmentItem> itemEls = new ArrayList<>(); + private final List<ItemMaterial> materials = new ArrayList<>(); public Set<String> getPaths() { return paths; @@ -356,16 +386,16 @@ public class QTI21ExportProcessor { } private static final class ItemMaterial { - private final VFSLeaf leaf; + private final File file; private final String exportUri; - public ItemMaterial(VFSLeaf leaf, String exportUri) { - this.leaf = leaf; + public ItemMaterial(File file, String exportUri) { + this.file = file; this.exportUri = exportUri; } - public VFSLeaf getLeaf() { - return leaf; + public File getFile() { + return file; } public String getExportUri() { diff --git a/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java b/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java index 6010b463379fb6eef1049367ab008212dfdb4be3..4f65ee540e308f88b1d76aed585079e5786815a4 100644 --- a/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java +++ b/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java @@ -458,7 +458,7 @@ public class QTI21AssessmentTestHandler extends FileHandler { @Override public boolean cleanupOnDelete(RepositoryEntry entry, OLATResourceable res) { boolean clean = super.cleanupOnDelete(entry, res); - assessmentTestSessionDao.deleteAllUserTestSessions(entry); + assessmentTestSessionDao.deleteAllUserTestSessionsByTest(entry); return clean; } diff --git a/src/main/java/org/olat/modules/qpool/ui/AbstractItemListController.java b/src/main/java/org/olat/modules/qpool/ui/AbstractItemListController.java index 03cb37f6692af444c0d758df0f7e321835803735..18b08a9e9ce0ac0dfa11073f647cad32f586b27e 100644 --- a/src/main/java/org/olat/modules/qpool/ui/AbstractItemListController.java +++ b/src/main/java/org/olat/modules/qpool/ui/AbstractItemListController.java @@ -55,6 +55,7 @@ import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.id.OLATResourceable; +import org.olat.core.util.StringHelper; import org.olat.core.util.event.EventBus; import org.olat.core.util.event.GenericEventListener; import org.olat.modules.qpool.QPoolService; @@ -109,7 +110,6 @@ public abstract class AbstractItemListController extends FormBasicController extendedSearchCtrl = new ExtendedSearchController(ureq, getWindowControl(), key, mainForm); extendedSearchCtrl.setEnabled(false); - listenTo(extendedSearchCtrl); initForm(ureq); @@ -167,6 +167,7 @@ public abstract class AbstractItemListController extends FormBasicController itemsTable.setExtendedSearch(extendedSearchCtrl); itemsTable.setColumnIndexForDragAndDropLabel(Cols.title.ordinal()); itemsTable.setAndLoadPersistedPreferences(ureq, "qpool-list-" + prefsKey); + listenTo(extendedSearchCtrl); VelocityContainer detailsVC = createVelocityContainer("item_list_details"); itemsTable.setDetailsRenderer(detailsVC, this); @@ -225,6 +226,16 @@ public abstract class AbstractItemListController extends FormBasicController @Override protected void event(UserRequest ureq, Controller source, Event event) { + if(extendedSearchCtrl == source) { + if(event == Event.CANCELLED_EVENT) { + String quickSearch = itemsTable.getQuickSearchString(); + if(StringHelper.containsNonWhitespace(quickSearch)) { + itemsTable.quickSearch(ureq, quickSearch); + } else { + itemsTable.resetSearch(ureq); + } + } + } super.event(ureq, source, event); } diff --git a/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java b/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java index 186f83390968a4c83d72cb0485297549d60805fb..948a7ceb56af9031713d3a40b8e308f1e035627d 100644 --- a/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java +++ b/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java @@ -60,6 +60,7 @@ import org.olat.core.util.vfs.VFSManager; import org.olat.course.assessment.manager.AssessmentModeDAO; import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.certificate.CertificatesManager; +import org.olat.ims.qti21.manager.AssessmentTestSessionDAO; import org.olat.modules.assessment.manager.AssessmentEntryDAO; import org.olat.modules.reminder.manager.ReminderDAO; import org.olat.repository.ErrorList; @@ -136,6 +137,8 @@ public class RepositoryServiceImpl implements RepositoryService { @Autowired private AssessmentModeDAO assessmentModeDao; @Autowired + private AssessmentTestSessionDAO assessmentTestSessionDao; + @Autowired private PersistentTaskDAO persistentTaskDao; @Autowired private ReminderDAO reminderDao; @@ -400,7 +403,9 @@ public class RepositoryServiceImpl implements RepositoryService { // referenced resourceable a swell. handler.cleanupOnDelete(entry, resource); dbInstance.commit(); - + + //delete all test sessions + assessmentTestSessionDao.deleteAllUserTestSessionsByCourse(entry); //nullify the reference assessmentEntryDao.removeEntryForReferenceEntry(entry); assessmentEntryDao.deleteEntryForRepositoryEntry(entry);