From 7367734fca47d1fe36035c3196fca003558cd03e Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Wed, 20 Dec 2017 11:28:44 +0100 Subject: [PATCH] OO-2967: refactor the list of assessed identities in assessment tools to be delivered by the resource or the course element them self --- ...va => AssessmentCourseNodeController.java} | 10 +- .../tool/AssessmentCourseTreeController.java | 10 +- .../tool/DefaultToolsControllerCreator.java | 65 --- .../IdentityListCourseNodeController.java | 362 ++++++------ .../IdentityListCourseNodeTableModel.java | 4 +- .../ui/tool/ToolsControllerCreator.java | 55 -- .../tool/_content/identity_courseelement.html | 14 - .../groupsandrights/CourseGroupManager.java | 2 - .../course/nodes/AssessableCourseNode.java | 21 +- .../olat/course/nodes/BasicLTICourseNode.java | 27 +- .../course/nodes/CheckListCourseNode.java | 29 +- .../org/olat/course/nodes/GTACourseNode.java | 69 +-- .../olat/course/nodes/IQTESTCourseNode.java | 155 ++---- .../org/olat/course/nodes/MSCourseNode.java | 42 +- .../course/nodes/PortfolioCourseNode.java | 29 +- .../course/nodes/ProjectBrokerCourseNode.java | 33 +- .../org/olat/course/nodes/STCourseNode.java | 17 +- .../olat/course/nodes/ScormCourseNode.java | 28 +- .../org/olat/course/nodes/TACourseNode.java | 42 +- .../gta/ui/BulkDownloadToolController.java | 81 --- .../ui/GTACoachedGroupGradingController.java | 4 +- .../ui/GTAGroupAssessmentToolController.java | 116 ---- .../GTAIdentityListCourseNodeController.java | 224 ++++++++ .../gta/ui/GroupAssessmentController.java | 18 +- .../ui/_content/identity_courseelement.html | 13 + .../nodes/iq/ConfirmResetController.java | 173 ------ .../nodes/iq/ExtraTimeCellRenderer.java | 86 +++ .../olat/course/nodes/iq/ExtraTimeInfos.java | 56 ++ .../IQIdentityListCourseNodeController.java | 513 ++++++++++++++++++ .../nodes/iq/QTI21ExtraTimeController.java | 146 ----- ...IdentityListCourseNodeToolsController.java | 70 +-- .../iq/_content/identity_courseelement.html | 31 ++ .../nodes/iq/_i18n/LocalStrings_de.properties | 7 + .../nodes/iq/_i18n/LocalStrings_en.properties | 6 + .../nodes/iq/_i18n/LocalStrings_fr.properties | 3 +- .../nodes/iq/_i18n/LocalStrings_it.properties | 1 + .../iq/_i18n/LocalStrings_pt_BR.properties | 1 + .../iq/_i18n/LocalStrings_zh_CN.properties | 1 + .../MSIdentityListCourseNodeController.java | 59 ++ .../ms/_content/identity_courseelement.html | 15 + ...rokerIdentityListCourseNodeController.java | 65 +++ .../_content/identity_courseelement.html | 8 + .../STIdentityListCourseNodeController.java | 134 +++++ .../st/_content/identity_courseelement.html | 2 + .../nodes/ta/BulkDownloadToolController.java | 80 --- .../TAIdentityListCourseNodeController.java | 95 ++++ .../ta/_content/identity_courseelement.html | 10 + .../QTI12ExportResultsReportController.java | 100 ---- .../ui/QTI12PullTestsToolController.java | 133 ++--- .../ui/QTI12StatisticsToolController.java | 69 +-- .../ui/_content/retrieve_tests.html | 5 + .../java/org/olat/ims/qti21/QTI21Service.java | 3 +- .../manager/AssessmentTestSessionDAO.java | 8 + .../QTI21ExportResultsReportController.java | 96 ---- .../ims/qti21/ui/QTI21AssessableResource.java | 18 +- .../QTI21AssessedIdentityListController.java | 158 ++++++ .../ui/QTI21AssessmentDetailsController.java | 58 +- ...ler.java => QTI21ResetDataController.java} | 252 ++++----- .../ui/QTI21RetrieveTestsController.java | 220 ++++++++ .../ui/QTI21RetrieveTestsToolController.java | 239 -------- .../qti21/ui/_content/assessment_details.html | 4 +- .../qti21/ui/_content/identity_element.html | 8 + .../ims/qti21/ui/_content/retrieve_tests.html | 5 + .../QTI21CorrectionToolController.java | 141 ----- .../QTI21ValidationToolController.java | 96 ---- .../QTI21StatisticsToolController.java | 71 +-- .../assessment/ui/AssessableResource.java | 7 +- .../ui/AssessedIdentityElementRow.java | 20 +- .../ui/AssessedIdentityListController.java | 65 +-- .../ui/AssessmentToolController.java | 16 +- .../ui/_content/identity_element.html | 8 - .../ui/model/AssessableBinderResource.java | 13 +- 72 files changed, 2416 insertions(+), 2429 deletions(-) rename src/main/java/org/olat/course/assessment/ui/tool/{IdentityListCourseNodeProvider.java => AssessmentCourseNodeController.java} (80%) delete mode 100644 src/main/java/org/olat/course/assessment/ui/tool/DefaultToolsControllerCreator.java delete mode 100644 src/main/java/org/olat/course/assessment/ui/tool/ToolsControllerCreator.java delete mode 100644 src/main/java/org/olat/course/nodes/gta/ui/BulkDownloadToolController.java delete mode 100644 src/main/java/org/olat/course/nodes/gta/ui/GTAGroupAssessmentToolController.java create mode 100644 src/main/java/org/olat/course/nodes/gta/ui/GTAIdentityListCourseNodeController.java create mode 100644 src/main/java/org/olat/course/nodes/gta/ui/_content/identity_courseelement.html delete mode 100644 src/main/java/org/olat/course/nodes/iq/ConfirmResetController.java create mode 100644 src/main/java/org/olat/course/nodes/iq/ExtraTimeCellRenderer.java create mode 100644 src/main/java/org/olat/course/nodes/iq/ExtraTimeInfos.java create mode 100644 src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java delete mode 100644 src/main/java/org/olat/course/nodes/iq/QTI21ExtraTimeController.java create mode 100644 src/main/java/org/olat/course/nodes/iq/_content/identity_courseelement.html create mode 100644 src/main/java/org/olat/course/nodes/ms/MSIdentityListCourseNodeController.java create mode 100644 src/main/java/org/olat/course/nodes/ms/_content/identity_courseelement.html create mode 100644 src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerIdentityListCourseNodeController.java create mode 100644 src/main/java/org/olat/course/nodes/projectbroker/_content/identity_courseelement.html create mode 100644 src/main/java/org/olat/course/nodes/st/STIdentityListCourseNodeController.java create mode 100644 src/main/java/org/olat/course/nodes/st/_content/identity_courseelement.html delete mode 100644 src/main/java/org/olat/course/nodes/ta/BulkDownloadToolController.java create mode 100644 src/main/java/org/olat/course/nodes/ta/TAIdentityListCourseNodeController.java create mode 100644 src/main/java/org/olat/course/nodes/ta/_content/identity_courseelement.html delete mode 100644 src/main/java/org/olat/ims/qti/resultexport/QTI12ExportResultsReportController.java create mode 100644 src/main/java/org/olat/ims/qti/statistics/ui/_content/retrieve_tests.html delete mode 100644 src/main/java/org/olat/ims/qti21/resultexport/QTI21ExportResultsReportController.java create mode 100644 src/main/java/org/olat/ims/qti21/ui/QTI21AssessedIdentityListController.java rename src/main/java/org/olat/ims/qti21/ui/{QTI21ResetToolController.java => QTI21ResetDataController.java} (56%) create mode 100644 src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsController.java delete mode 100644 src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsToolController.java create mode 100644 src/main/java/org/olat/ims/qti21/ui/_content/identity_element.html create mode 100644 src/main/java/org/olat/ims/qti21/ui/_content/retrieve_tests.html delete mode 100644 src/main/java/org/olat/ims/qti21/ui/assessment/QTI21CorrectionToolController.java delete mode 100644 src/main/java/org/olat/ims/qti21/ui/assessment/QTI21ValidationToolController.java diff --git a/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeProvider.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseNodeController.java similarity index 80% rename from src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeProvider.java rename to src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseNodeController.java index a6f9ff9c0af..249d6ec752d 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeProvider.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseNodeController.java @@ -22,16 +22,18 @@ package org.olat.course.assessment.ui.tool; import java.util.List; import org.olat.basesecurity.IdentityRef; +import org.olat.core.gui.control.Controller; +import org.olat.modules.assessment.ui.AssessedIdentityListState; /** * - * - * - * Initial date: 4 déc. 2017<br> + * Initial date: 18 déc. 2017<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public interface IdentityListCourseNodeProvider { +public interface AssessmentCourseNodeController extends Controller { + + public AssessedIdentityListState getListState(); public List<IdentityRef> getSelectedIdentities(); diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseTreeController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseTreeController.java index 644b3d5216b..c3ad56f86f1 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseTreeController.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseTreeController.java @@ -69,7 +69,7 @@ public class AssessmentCourseTreeController extends BasicController implements A private Controller currentCtrl; private Controller businessGroupListCtrl; - private IdentityListCourseNodeController identityListCtrl; + private AssessmentCourseNodeController identityListCtrl; private View view = View.users; private TreeNode selectedNodeChanged; @@ -288,8 +288,14 @@ public class AssessmentCourseTreeController extends BasicController implements A WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(oresUsers, null, getWindowControl()); OLATResourceable oresNode = OresHelper.createOLATResourceableInstance("Node", new Long(courseNode.getIdent())); WindowControl bbwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(oresNode, null, bwControl); - identityListCtrl = new IdentityListCourseNodeController(ureq, bbwControl, stackPanel, + if(courseNode instanceof AssessableCourseNode) { + identityListCtrl = ((AssessableCourseNode)courseNode).getIdentityListController(ureq, getWindowControl(), stackPanel, + courseEntry, null, coachCourseEnv, toolContainer, assessmentCallback); + } else { + identityListCtrl = new IdentityListCourseNodeController(ureq, bbwControl, stackPanel, courseEntry, null, courseNode, coachCourseEnv, toolContainer, assessmentCallback); + } + return identityListCtrl; } diff --git a/src/main/java/org/olat/course/assessment/ui/tool/DefaultToolsControllerCreator.java b/src/main/java/org/olat/course/assessment/ui/tool/DefaultToolsControllerCreator.java deleted file mode 100644 index e33a8ad1c5d..00000000000 --- a/src/main/java/org/olat/course/assessment/ui/tool/DefaultToolsControllerCreator.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * <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.course.assessment.ui.tool; - -import java.util.Collections; -import java.util.List; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.stack.TooledStackedPanel; -import org.olat.core.gui.control.Controller; -import org.olat.core.gui.control.WindowControl; -import org.olat.core.id.Identity; -import org.olat.course.run.userview.UserCourseEnvironment; -import org.olat.modules.assessment.AssessmentToolOptions; - -/** - * - * Default implementation without any tools. - * - * Initial date: 4 déc. 2017<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class DefaultToolsControllerCreator implements ToolsControllerCreator { - - @Override - public boolean hasCalloutTools() { - return false; - } - - @Override - public Controller createCalloutController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, Identity assessedIdentity) { - return null; - } - - @Override - public List<Controller> createAssessmentTools(UserRequest ureq, WindowControl wControl, - TooledStackedPanel stackPanel, UserCourseEnvironment coachCourseEnv, AssessmentToolOptions options) { - return null; - } - - @Override - public List<Controller> createMultiSelectionTools(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, IdentityListCourseNodeProvider provider) { - return Collections.emptyList(); - } -} diff --git a/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java index e631efcdf99..07a88ffac28 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java @@ -21,13 +21,10 @@ package org.olat.course.assessment.ui.tool; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityModule; @@ -46,7 +43,6 @@ import org.olat.core.gui.components.form.flexible.elements.FormLink; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.FormEvent; import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; -import org.olat.core.gui.components.form.flexible.impl.elements.table.DateFlexiCellRenderer; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; @@ -54,7 +50,6 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTable import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; -import org.olat.core.gui.components.stack.BreadcrumbPanelAware; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.components.stack.TooledStackedPanel.Align; import org.olat.core.gui.control.Controller; @@ -79,18 +74,15 @@ import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentModule; import org.olat.course.assessment.AssessmentToolManager; import org.olat.course.assessment.bulk.PassedCellRenderer; -import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.assessment.model.SearchAssessedIdentityParams; import org.olat.course.assessment.ui.tool.IdentityListCourseNodeTableModel.IdentityCourseElementCols; import org.olat.course.assessment.ui.tool.event.ShowDetailsEvent; -import org.olat.course.certificate.CertificateLight; -import org.olat.course.certificate.CertificatesManager; -import org.olat.course.certificate.ui.DownloadCertificateCellRenderer; import org.olat.course.nodes.AssessableCourseNode; import org.olat.course.nodes.CalculatedAssessableCourseNode; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.CourseNodeFactory; import org.olat.course.nodes.STCourseNode; +import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironmentImpl; @@ -116,34 +108,38 @@ import org.olat.user.propertyhandlers.UserPropertyHandler; import org.springframework.beans.factory.annotation.Autowired; /** + * This is the "abstract" class of the assessed identities list. If you want + * to inherit from it, don't forget to copy the velocity template and adapt + * it to your need. * * Initial date: 06.10.2015<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class IdentityListCourseNodeController extends FormBasicController implements Activateable2, GenericEventListener, IdentityListCourseNodeProvider { +public class IdentityListCourseNodeController extends FormBasicController + implements Activateable2, GenericEventListener, AssessmentCourseNodeController { private int counter = 0; - private final BusinessGroup group; - private final CourseNode courseNode; + protected final BusinessGroup group; + protected final CourseNode courseNode; private final RepositoryEntry courseEntry; private final RepositoryEntry referenceEntry; + private final CourseEnvironment courseEnv; private final boolean isAdministrativeUser; - private final UserCourseEnvironment coachCourseEnv; + protected final UserCourseEnvironment coachCourseEnv; private final List<UserPropertyHandler> userPropertyHandlers; - private final AssessmentToolSecurityCallback assessmentCallback; + protected final AssessmentToolSecurityCallback assessmentCallback; private Link nextLink, previousLink; - private FlexiTableElement tableEl; + protected FlexiTableElement tableEl; private FormLink bulkDoneButton, bulkVisibleButton; - private final TooledStackedPanel stackPanel; + protected final TooledStackedPanel stackPanel; private final AssessmentToolContainer toolContainer; - private IdentityListCourseNodeTableModel usersTableModel; + protected IdentityListCourseNodeTableModel usersTableModel; private Controller toolsCtrl; - private CloseableModalController cmc; + protected CloseableModalController cmc; private List<Controller> bulkToolsList; - private ToolsControllerCreator toolsCtrlCreator; private AssessedIdentityController currentIdentityCtrl; private CloseableCalloutWindowController toolsCalloutCtrl; private ConfirmUserVisibilityController changeUserVisibilityCtrl; @@ -159,16 +155,13 @@ public class IdentityListCourseNodeController extends FormBasicController implem @Autowired private CoordinatorManager coordinatorManager; @Autowired - private CertificatesManager certificatesManager; - @Autowired - private UserCourseInformationsManager userInfosMgr; - @Autowired private AssessmentToolManager assessmentToolManager; public IdentityListCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, RepositoryEntry courseEntry, BusinessGroup group, CourseNode courseNode, UserCourseEnvironment coachCourseEnv, AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { super(ureq, wControl, "identity_courseelement"); + setTranslator(Util.createPackageTranslator(IdentityListCourseNodeController.class, getLocale(), getTranslator())); setTranslator(Util.createPackageTranslator(AssessmentModule.class, getLocale(), getTranslator())); setTranslator(userManager.getPropertyHandlerTranslator(getTranslator())); @@ -179,15 +172,13 @@ public class IdentityListCourseNodeController extends FormBasicController implem this.toolContainer = toolContainer; this.coachCourseEnv = coachCourseEnv; this.assessmentCallback = assessmentCallback; + courseEnv = CourseFactory.loadCourse(courseEntry).getCourseEnvironment(); if(courseNode.needsReferenceToARepositoryEntry()) { referenceEntry = courseNode.getReferencedRepositoryEntry(); } else { referenceEntry = null; } - if(courseNode instanceof AssessableCourseNode) { - toolsCtrlCreator = ((AssessableCourseNode)courseNode).getAssessmentToolsCreator(); - } isAdministrativeUser = securityModule.isUserAllowedAdminProps(ureq.getUserSession().getRoles()); userPropertyHandlers = userManager.getUserPropertyHandlersFor(AssessmentToolConstants.usageIdentifyer, isAdministrativeUser); @@ -199,6 +190,23 @@ public class IdentityListCourseNodeController extends FormBasicController implem .registerFor(this, getIdentity(), courseEntry.getOlatResource()); } + public RepositoryEntry getCourseRepositoryEntry() { + return courseEntry; + } + + public RepositoryEntry getReferencedRepositoryEntry() { + return referenceEntry; + } + + public CourseEnvironment getCourseEnvironment() { + return courseEnv; + } + + public AssessmentToolContainer getToolContainer() { + return toolContainer; + } + + @Override public AssessedIdentityListState getListState() { List<FlexiTableFilter> filters = tableEl.getSelectedFilters(); String filter = null; @@ -243,9 +251,7 @@ public class IdentityListCourseNodeController extends FormBasicController implem } ICourse course = CourseFactory.loadCourse(courseEntry); - String select = (courseNode instanceof AssessableCourseNode - && (courseNode.getParent() == null || !(courseNode instanceof STCourseNode))) - ? "select" : null; + String select = isSelectable() ? "select" : null; //add the table FlexiTableSortOptions options = new FlexiTableSortOptions(); @@ -265,59 +271,13 @@ public class IdentityListCourseNodeController extends FormBasicController implem } colIndex++; } - AssessableCourseNode assessableNode = null; - if(courseNode instanceof AssessableCourseNode) { - assessableNode = (AssessableCourseNode)courseNode; - - if(assessableNode.hasAttemptsConfigured()) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.attempts)); - } - if(!(courseNode instanceof CalculatedAssessableCourseNode)) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.userVisibility, - new UserVisibilityCellRenderer(getTranslator()))); - } - if(assessableNode.hasScoreConfigured()) { - if(!(assessableNode instanceof STCourseNode)) { - if(assessableNode.getMinScoreConfiguration() != null) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.min, new ScoreCellRenderer())); - } - if(assessableNode.getMaxScoreConfiguration() != null) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.max, new ScoreCellRenderer())); - } - } - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.score, new ScoreCellRenderer())); - } - if(assessableNode.hasPassedConfigured()) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.passed, new PassedCellRenderer())); - } - if(assessableNode.hasIndividualAsssessmentDocuments()) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.numOfAssessmentDocs)); - } - } - - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.assessmentStatus, new AssessmentStatusCellRenderer(getLocale()))); - if(assessableNode != null && assessableNode.hasCompletion()) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.currentCompletion)); - } - - if(courseNode.getParent() == null) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.initialLaunchDate, select)); - } - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, IdentityCourseElementCols.lastModified, select)); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.lastUserModified, select)); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, IdentityCourseElementCols.lastCoachModified, select)); - - if(course.getCourseConfig().isCertificateEnabled()) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.certificate, new DownloadCertificateCellRenderer(getLocale()))); - if(course.getCourseConfig().isRecertificationEnabled()) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.recertification, new DateFlexiCellRenderer(getLocale()))); - } - } - if(toolsCtrlCreator != null && toolsCtrlCreator.hasCalloutTools()) { - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.tools)); - } + initAssessmentColumns(columnsModel); + initStatusColumns(columnsModel); + initModificationDatesColumns(columnsModel); + initCalloutColumns(columnsModel); + AssessableCourseNode assessableNode = courseNode instanceof AssessableCourseNode ? (AssessableCourseNode)courseNode : null; usersTableModel = new IdentityListCourseNodeTableModel(columnsModel, assessableNode, getLocale()); tableEl = uifactory.addTableElement(getWindowControl(), "table", usersTableModel, 20, false, getTranslator(), formLayout); tableEl.setExportEnabled(true); @@ -355,73 +315,92 @@ public class IdentityListCourseNodeController extends FormBasicController implem } } - tableEl.setAndLoadPersistedPreferences(ureq, "assessment-tool-identity-list"); - - + tableEl.setAndLoadPersistedPreferences(ureq, getTableId()); + } + + protected String getTableId() { + return "assessment-tool-identity-list"; + } + + protected void initAssessmentColumns(FlexiTableColumnModel columnsModel) { + if(courseNode instanceof AssessableCourseNode) { + AssessableCourseNode assessableNode = (AssessableCourseNode)courseNode; + + if(assessableNode.hasAttemptsConfigured()) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.attempts)); + } + if(!(courseNode instanceof CalculatedAssessableCourseNode)) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.userVisibility, + new UserVisibilityCellRenderer(getTranslator()))); + } + if(assessableNode.hasScoreConfigured()) { + if(!(assessableNode instanceof STCourseNode)) { + if(assessableNode.getMinScoreConfiguration() != null) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.min, new ScoreCellRenderer())); + } + if(assessableNode.getMaxScoreConfiguration() != null) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.max, new ScoreCellRenderer())); + } + } + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.score, new ScoreCellRenderer())); + } + if(assessableNode.hasPassedConfigured()) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.passed, new PassedCellRenderer())); + } + if(assessableNode.hasIndividualAsssessmentDocuments()) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.numOfAssessmentDocs)); + } + } + } + + protected void initStatusColumns(FlexiTableColumnModel columnsModel) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.assessmentStatus, new AssessmentStatusCellRenderer(getLocale()))); + } + + protected void initModificationDatesColumns(FlexiTableColumnModel columnsModel) { + String select = getSelectAction(); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, IdentityCourseElementCols.lastModified, select)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.lastUserModified, select)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, IdentityCourseElementCols.lastCoachModified, select)); + } + + protected void initCalloutColumns(FlexiTableColumnModel columnsModel) { + if(courseNode instanceof AssessableCourseNode) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.tools)); + } + } + + protected String getSelectAction() { + return isSelectable() ? "select" : null; + } + + protected boolean isSelectable() { + return courseNode instanceof AssessableCourseNode; } - private void initMultiSelectionTools(UserRequest ureq, FormItemContainer formLayout) { - if(courseNode instanceof AssessableCourseNode && !(courseNode instanceof CalculatedAssessableCourseNode)) { + protected void initMultiSelectionTools(@SuppressWarnings("unused") UserRequest ureq, FormLayoutContainer formLayout) { + if(courseNode instanceof AssessableCourseNode) { bulkDoneButton = uifactory.addFormLink("bulk.done", formLayout, Link.BUTTON); bulkDoneButton.setVisible(!coachCourseEnv.isCourseReadOnly()); bulkVisibleButton = uifactory.addFormLink("bulk.visible", formLayout, Link.BUTTON); bulkVisibleButton.setVisible(!coachCourseEnv.isCourseReadOnly()); } - - List<String> cmpNames = new ArrayList<>(); - if(toolsCtrlCreator != null) { - List<Controller> tools = toolsCtrlCreator.createMultiSelectionTools(ureq, getWindowControl(), coachCourseEnv, this); - cmpNames = putToolsInContainer(tools); - } - flc.contextPut("multiSelectionCmpNames", cmpNames); } - private void updateModel(UserRequest ureq, String searchString, List<FlexiTableFilter> filters, List<FlexiTableFilter> extendedFilters) { - SearchAssessedIdentityParams params = new SearchAssessedIdentityParams(courseEntry, courseNode.getIdent(), referenceEntry, assessmentCallback); - - List<AssessmentEntryStatus> assessmentStatus = null; - if(filters != null && filters.size() > 0) { - assessmentStatus = new ArrayList<>(filters.size()); - for(FlexiTableFilter filter:filters) { - if("passed".equals(filter.getFilter())) { - params.setPassed(true); - } else if("failed".equals(filter.getFilter())) { - params.setFailed(true); - } else if(AssessmentEntryStatus.isValueOf(filter.getFilter())){ - assessmentStatus.add(AssessmentEntryStatus.valueOf(filter.getFilter())); - } - } - } - params.setAssessmentStatus(assessmentStatus); - - List<Long> businessGroupKeys = null; - if(group != null) { - businessGroupKeys = Collections.singletonList(group.getKey()); - } else if(extendedFilters != null && extendedFilters.size() > 0) { - businessGroupKeys = new ArrayList<>(extendedFilters.size()); - for(FlexiTableFilter extendedFilter:extendedFilters) { - if(StringHelper.isLong(extendedFilter.getFilter())) { - businessGroupKeys.add(Long.parseLong(extendedFilter.getFilter())); - } - } - } - params.setBusinessGroupKeys(businessGroupKeys); - params.setSearchString(searchString); - + protected void loadModel(@SuppressWarnings("unused") UserRequest ureq) { + SearchAssessedIdentityParams params = getSearchParameters(); List<Identity> assessedIdentities = assessmentToolManager.getAssessedIdentities(getIdentity(), params); List<AssessmentEntry> assessmentEntries = assessmentToolManager.getAssessmentEntries(getIdentity(), params, null); Map<Long,AssessmentEntry> entryMap = new HashMap<>(); assessmentEntries.stream() .filter(entry -> entry.getIdentity() != null) .forEach((entry) -> entryMap.put(entry.getIdentity().getKey(), entry)); - Map<Long,Date> initialLaunchDates = userInfosMgr.getInitialLaunchDates(courseEntry.getOlatResource()); List<AssessedIdentityElementRow> rows = new ArrayList<>(assessedIdentities.size()); for(Identity assessedIdentity:assessedIdentities) { AssessmentEntry entry = entryMap.get(assessedIdentity.getKey()); - Date initialLaunchDate = initialLaunchDates.get(assessedIdentity.getKey()); - + CompletionItem currentCompletion = new CompletionItem("current-completion-" + (++counter), getLocale()); if(entry != null) { currentCompletion.setCompletion(entry.getCurrentRunCompletion()); @@ -432,69 +411,74 @@ public class IdentityListCourseNodeController extends FormBasicController implem FormLink toolsLink = uifactory.addFormLink("tools_" + (++counter), "tools", "", null, null, Link.NONTRANSLATED); toolsLink.setIconLeftCSS("o_icon o_icon_actions o_icon-lg"); - AssessedIdentityElementRow row = new AssessedIdentityElementRow(assessedIdentity, entry, initialLaunchDate, + AssessedIdentityElementRow row = new AssessedIdentityElementRow(assessedIdentity, entry, currentCompletion, toolsLink, userPropertyHandlers, getLocale()); toolsLink.setUserObject(row); rows.add(row); } - - if(toolContainer.getCertificateMap() == null) { - List<CertificateLight> certificates = certificatesManager.getLastCertificates(courseEntry.getOlatResource()); - ConcurrentMap<Long, CertificateLight> certificateMap = new ConcurrentHashMap<>(); - for(CertificateLight certificate:certificates) { - certificateMap.put(certificate.getIdentityKey(), certificate); - } - toolContainer.setCertificateMap(certificateMap); - } - usersTableModel.setCertificateMap(toolContainer.getCertificateMap()); + usersTableModel.setObjects(rows); + List<FlexiTableFilter> filters = tableEl.getSelectedFilters(); if(filters != null && filters.size() > 0 && filters.get(0) != null) { usersTableModel.filter(Collections.singletonList(filters.get(0))); } tableEl.reset(); tableEl.reloadData(); - - initTopBulkTools(ureq, params, assessedIdentities); - } - - private void initTopBulkTools(UserRequest ureq, SearchAssessedIdentityParams params, List<Identity> assessedIdentities) { - List<String> toolCmpNames = new ArrayList<>(); - if(toolsCtrlCreator != null) { - AssessmentToolOptions options = new AssessmentToolOptions(); - options.setAdmin(assessmentCallback.isAdmin()); - if(group == null) { - if(assessmentCallback.isAdmin()) { - options.setNonMembers(params.isNonMembers()); - } else { - options.setIdentities(assessedIdentities); - fillAlternativeToAssessableIdentityList(options, params); + } + + protected SearchAssessedIdentityParams getSearchParameters() { + SearchAssessedIdentityParams params = new SearchAssessedIdentityParams(courseEntry, courseNode.getIdent(), referenceEntry, assessmentCallback); + + List<FlexiTableFilter> filters = tableEl.getSelectedFilters(); + List<FlexiTableFilter> extendedFilters = tableEl.getSelectedExtendedFilters(); + + List<AssessmentEntryStatus> assessmentStatus = null; + if(filters != null && filters.size() > 0) { + assessmentStatus = new ArrayList<>(filters.size()); + for(FlexiTableFilter filter:filters) { + if("passed".equals(filter.getFilter())) { + params.setPassed(true); + } else if("failed".equals(filter.getFilter())) { + params.setFailed(true); + } else if(AssessmentEntryStatus.isValueOf(filter.getFilter())){ + assessmentStatus.add(AssessmentEntryStatus.valueOf(filter.getFilter())); } - } else { - options.setGroup(group); } - - //TODO qti filter by group? - List<Controller> tools = toolsCtrlCreator.createAssessmentTools(ureq, getWindowControl(), stackPanel, coachCourseEnv, options); - toolCmpNames = putToolsInContainer(tools); - bulkToolsList = tools; - } - flc.contextPut("toolCmpNames", toolCmpNames); - } - - private List<String> putToolsInContainer(List<Controller> tools) { - List<String> toolCmpNames = new ArrayList<>(); - if(tools != null && !tools.isEmpty()) { - for(Controller tool:tools) { - listenTo(tool); - String toolCmpName = "ctrl_" + (counter++); - flc.put(toolCmpName, tool.getInitialComponent()); - toolCmpNames.add(toolCmpName); - if(tool instanceof BreadcrumbPanelAware) { - ((BreadcrumbPanelAware)tool).setBreadcrumbPanel(stackPanel); + } + params.setAssessmentStatus(assessmentStatus); + + List<Long> businessGroupKeys = null; + if(group != null) { + businessGroupKeys = Collections.singletonList(group.getKey()); + } else if(extendedFilters != null && extendedFilters.size() > 0) { + businessGroupKeys = new ArrayList<>(extendedFilters.size()); + for(FlexiTableFilter extendedFilter:extendedFilters) { + if(StringHelper.isLong(extendedFilter.getFilter())) { + businessGroupKeys.add(Long.parseLong(extendedFilter.getFilter())); } } } - return toolCmpNames; + params.setBusinessGroupKeys(businessGroupKeys); + params.setSearchString(tableEl.getQuickSearchString()); + return params; + } + + protected AssessmentToolOptions getOptions() { + SearchAssessedIdentityParams params = getSearchParameters(); + AssessmentToolOptions options = new AssessmentToolOptions(); + options.setAdmin(assessmentCallback.isAdmin()); + if(group == null) { + if(assessmentCallback.isAdmin()) { + options.setNonMembers(params.isNonMembers()); + } else { + List<Identity> assessedIdentities = assessmentToolManager.getAssessedIdentities(getIdentity(), params); + options.setIdentities(assessedIdentities); + fillAlternativeToAssessableIdentityList(options, params); + } + } else { + options.setGroup(group); + } + return options; } private void fillAlternativeToAssessableIdentityList(AssessmentToolOptions options, SearchAssessedIdentityParams params) { @@ -545,7 +529,7 @@ public class IdentityListCourseNodeController extends FormBasicController implem if(extendedFilters != null) { tableEl.setSelectedExtendedFilters(extendedFilters); } - updateModel(ureq, null, tableEl.getSelectedFilters(), tableEl.getSelectedExtendedFilters()); + loadModel(ureq); if(entries != null && entries.size() > 0) { ContextEntry entry = entries.get(0); @@ -586,18 +570,18 @@ public class IdentityListCourseNodeController extends FormBasicController implem if(currentIdentityCtrl == source) { if(event instanceof AssessmentFormEvent) { AssessmentFormEvent aee = (AssessmentFormEvent)event; - updateModel(ureq, null, null, null); + loadModel(ureq); if(aee.isClose()) { stackPanel.popController(currentIdentityCtrl); } } else if(event == Event.CHANGED_EVENT) { - updateModel(ureq, null, null, null); + loadModel(ureq); } else if(event == Event.CANCELLED_EVENT) { stackPanel.popController(currentIdentityCtrl); } } else if(bulkToolsList != null && bulkToolsList.contains(source)) { if(event == Event.CHANGED_EVENT) { - updateModel(ureq, null, null, null); + loadModel(ureq); } } else if(changeUserVisibilityCtrl == source) { if(event == Event.DONE_EVENT) { @@ -611,7 +595,7 @@ public class IdentityListCourseNodeController extends FormBasicController implem toolsCalloutCtrl.deactivate(); cleanUp(); } else if(event == Event.CHANGED_EVENT) { - updateModel(ureq, null, null, null); + loadModel(ureq); toolsCalloutCtrl.deactivate(); cleanUp(); } else if(event == Event.CLOSE_EVENT) { @@ -624,7 +608,7 @@ public class IdentityListCourseNodeController extends FormBasicController implem super.event(ureq, source, event); } - private void cleanUp() { + protected void cleanUp() { removeAsListenerAndDispose(changeUserVisibilityCtrl); removeAsListenerAndDispose(toolsCalloutCtrl); removeAsListenerAndDispose(toolsCtrl); @@ -646,8 +630,7 @@ public class IdentityListCourseNodeController extends FormBasicController implem doSelect(ureq, row); } } else if(event instanceof FlexiTableSearchEvent) { - FlexiTableSearchEvent ftse = (FlexiTableSearchEvent)event; - updateModel(ureq, ftse.getSearch(), ftse.getFilters(), ftse.getExtendedFilters()); + loadModel(ureq); } } else if(bulkDoneButton == source) { doSetDone(ureq); @@ -664,13 +647,13 @@ public class IdentityListCourseNodeController extends FormBasicController implem } private void doOpenTools(UserRequest ureq, AssessedIdentityElementRow row, FormLink link) { - if(toolsCtrlCreator == null) return; + if(toolsCtrl != null) return; removeAsListenerAndDispose(toolsCtrl); removeAsListenerAndDispose(toolsCalloutCtrl); Identity assessedIdentity = securityManager.loadIdentityByKey(row.getIdentityKey()); - toolsCtrl = toolsCtrlCreator.createCalloutController(ureq, getWindowControl(), coachCourseEnv, assessedIdentity); + toolsCtrl = createCalloutController(ureq, assessedIdentity); listenTo(toolsCtrl); toolsCalloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(), @@ -679,6 +662,11 @@ public class IdentityListCourseNodeController extends FormBasicController implem toolsCalloutCtrl.activate(); } + protected Controller createCalloutController(UserRequest ureq, Identity assessedIdentity) { + return new IdentityListCourseNodeToolsController(ureq, getWindowControl(), + (AssessableCourseNode)courseNode, assessedIdentity, coachCourseEnv); + } + private void doNext(UserRequest ureq) { stackPanel.popController(currentIdentityCtrl); @@ -798,7 +786,7 @@ public class IdentityListCourseNodeController extends FormBasicController implem scoreEval.getCurrentRunCompletion(), scoreEval.getCurrentRunStatus(), scoreEval.getAssessmentID()); assessableCourseNode.updateUserScoreEvaluation(doneEval, assessedUserCourseEnv, getIdentity(), false, Role.coach); } - updateModel(ureq, null, null, null); + loadModel(ureq); } private void doSetDone(UserRequest ureq) { @@ -833,7 +821,7 @@ public class IdentityListCourseNodeController extends FormBasicController implem assessableCourseNode.updateUserScoreEvaluation(doneEval, assessedUserCourseEnv, getIdentity(), false, Role.coach); } - updateModel(ureq, null, null, null); + loadModel(ureq); } } diff --git a/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeTableModel.java b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeTableModel.java index 1e009e54a10..0aad3aa841b 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeTableModel.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeTableModel.java @@ -161,6 +161,7 @@ public class IdentityListCourseNodeTableModel extends DefaultFlexiTableDataModel case lastUserModified: return row.getLastUserModified(); case lastCoachModified: return row.getLastCoachModified(); case tools: return row.getToolsLink(); + case details: return row.getDetails(); } } int propPos = col - AssessmentToolConstants.USER_PROPS_OFFSET; @@ -190,7 +191,8 @@ public class IdentityListCourseNodeTableModel extends DefaultFlexiTableDataModel lastCoachModified("table.header.lastCoachModificationDate"), numOfAssessmentDocs("table.header.num.assessmentDocs"), currentCompletion("table.header.completion"), - tools("table.header.tools"); + tools("table.header.tools"), + details("table.header.details"); private final String i18nKey; diff --git a/src/main/java/org/olat/course/assessment/ui/tool/ToolsControllerCreator.java b/src/main/java/org/olat/course/assessment/ui/tool/ToolsControllerCreator.java deleted file mode 100644 index a9979aac45a..00000000000 --- a/src/main/java/org/olat/course/assessment/ui/tool/ToolsControllerCreator.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * <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.course.assessment.ui.tool; - -import java.util.List; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.stack.TooledStackedPanel; -import org.olat.core.gui.control.Controller; -import org.olat.core.gui.control.WindowControl; -import org.olat.core.id.Identity; -import org.olat.course.run.userview.UserCourseEnvironment; -import org.olat.modules.assessment.AssessmentToolOptions; - -/** - * - * Initial date: 23 nov. 2017<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public interface ToolsControllerCreator { - - public boolean hasCalloutTools(); - - public Controller createCalloutController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, Identity assessedIdentity); - - /** - * Factory method to launch course element assessment tools. limitToGroup is optional to skip he the group choose step - */ - public List<Controller> createAssessmentTools(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, - UserCourseEnvironment coachCourseEnv, AssessmentToolOptions options); - - - public List<Controller> createMultiSelectionTools(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, IdentityListCourseNodeProvider provider); - -} diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_courseelement.html b/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_courseelement.html index 1e37f4d0da0..5f498487a26 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_courseelement.html +++ b/src/main/java/org/olat/course/assessment/ui/tool/_content/identity_courseelement.html @@ -1,13 +1,4 @@ <h2><i class="o_icon $courseNodeCssClass"> </i> $r.escapeHtml($courseNodeTitle)#if($r.isNotEmpty($businessGroupName)) <small><i class="o_icon o_icon_group"> </i> $r.escapeHtml($businessGroupName)</small>#end</h2> - -#if ($r.isNotEmpty($toolCmpNames)) - <div class="o_button_group o_button_group_right"> - #foreach($toolCmpName in $toolCmpNames) - $r.render($toolCmpName) - #end - </div> -#end - $r.render("table") <div class="o_button_group"> #if($r.available("bulk.done")) @@ -16,9 +7,4 @@ $r.render("table") #if($r.available("bulk.visible")) $r.render("bulk.visible") #end - #if ($r.isNotEmpty($multiSelectionCmpNames)) - #foreach($multiSelectionCmpName in $multiSelectionCmpNames) - $r.render($multiSelectionCmpName) - #end - #end </div> diff --git a/src/main/java/org/olat/course/groupsandrights/CourseGroupManager.java b/src/main/java/org/olat/course/groupsandrights/CourseGroupManager.java index ef5ab2196ca..0c859e142ce 100644 --- a/src/main/java/org/olat/course/groupsandrights/CourseGroupManager.java +++ b/src/main/java/org/olat/course/groupsandrights/CourseGroupManager.java @@ -56,8 +56,6 @@ public interface CourseGroupManager { public OLATResource getCourseResource(); public RepositoryEntry getCourseEntry(); - - //public void refreshRepositoryEntry(RepositoryEntry entry); /** * Checks users course rights in any of the available right group context of diff --git a/src/main/java/org/olat/course/nodes/AssessableCourseNode.java b/src/main/java/org/olat/course/nodes/AssessableCourseNode.java index 44ee041f6c1..abed1a7f88c 100644 --- a/src/main/java/org/olat/course/nodes/AssessableCourseNode.java +++ b/src/main/java/org/olat/course/nodes/AssessableCourseNode.java @@ -31,15 +31,20 @@ import java.util.List; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; +import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.core.id.Identity; -import org.olat.course.assessment.ui.tool.ToolsControllerCreator; +import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroup; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentRunStatus; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; +import org.olat.repository.RepositoryEntry; /** @@ -189,14 +194,22 @@ public interface AssessableCourseNode extends CourseNode { UserCourseEnvironment coachCourseEnv, UserCourseEnvironment assessedUserCourseEnvironment); /** - * Method to get a factory to create the tools / actions controller + * Returns the controller with the list of assessed identities for + * a specific course node. + * * @param ureq * @param wControl + * @param stackPanel + * @param courseEntry + * @param group * @param coachCourseEnv - * @param assessedIdentity + * @param toolContainer + * @param assessmentCallback * @return */ - public ToolsControllerCreator getAssessmentToolsCreator(); + public AssessmentCourseNodeController getIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback); /** * diff --git a/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java b/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java index 85d4a0e8ac7..b463bc32b4e 100644 --- a/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java +++ b/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java @@ -31,6 +31,7 @@ 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.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.iframe.DeliveryOptions; @@ -44,9 +45,8 @@ import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentManager; -import org.olat.course.assessment.ui.tool.DefaultToolsControllerCreator; -import org.olat.course.assessment.ui.tool.IdentityListCourseNodeToolsController; -import org.olat.course.assessment.ui.tool.ToolsControllerCreator; +import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeController; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; @@ -59,6 +59,7 @@ import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroup; import org.olat.ims.lti.LTIDisplayOptions; import org.olat.ims.lti.LTIManager; import org.olat.ims.lti.ui.LTIResultDetailsController; @@ -66,6 +67,8 @@ import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentRunStatus; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.repository.RepositoryEntry; import org.olat.resource.OLATResource; @@ -449,19 +452,11 @@ public class BasicLTICourseNode extends AbstractAccessableCourseNode implements } @Override - public ToolsControllerCreator getAssessmentToolsCreator() { - return new DefaultToolsControllerCreator() { - @Override - public boolean hasCalloutTools() { - return true; - } - - @Override - public Controller createCalloutController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, Identity assessedIdentity) { - return new IdentityListCourseNodeToolsController(ureq, wControl, BasicLTICourseNode.this, assessedIdentity, coachCourseEnv); - } - }; + public AssessmentCourseNodeController getIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + return new IdentityListCourseNodeController(ureq, wControl, stackPanel, + courseEntry, group, this, coachCourseEnv, toolContainer, assessmentCallback); } @Override diff --git a/src/main/java/org/olat/course/nodes/CheckListCourseNode.java b/src/main/java/org/olat/course/nodes/CheckListCourseNode.java index 37e7b03d82e..5b8ac41f6e0 100644 --- a/src/main/java/org/olat/course/nodes/CheckListCourseNode.java +++ b/src/main/java/org/olat/course/nodes/CheckListCourseNode.java @@ -34,6 +34,7 @@ import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; +import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.messages.MessageUIFactory; @@ -55,9 +56,8 @@ import org.olat.core.util.vfs.VFSItem; import org.olat.core.util.vfs.VFSLeaf; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentManager; -import org.olat.course.assessment.ui.tool.DefaultToolsControllerCreator; -import org.olat.course.assessment.ui.tool.IdentityListCourseNodeToolsController; -import org.olat.course.assessment.ui.tool.ToolsControllerCreator; +import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeController; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; @@ -78,10 +78,13 @@ import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironmentImpl; +import org.olat.group.BusinessGroup; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentRunStatus; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.repository.RepositoryEntry; /** @@ -511,19 +514,11 @@ public class CheckListCourseNode extends AbstractAccessableCourseNode implements } @Override - public ToolsControllerCreator getAssessmentToolsCreator() { - return new DefaultToolsControllerCreator() { - @Override - public boolean hasCalloutTools() { - return true; - } - - @Override - public Controller createCalloutController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, Identity assessedIdentity) { - return new IdentityListCourseNodeToolsController(ureq, wControl, CheckListCourseNode.this, assessedIdentity, coachCourseEnv); - } - }; + public AssessmentCourseNodeController getIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + return new IdentityListCourseNodeController(ureq, wControl, stackPanel, + courseEntry, group, this, coachCourseEnv, toolContainer, assessmentCallback); } /** @@ -531,7 +526,7 @@ public class CheckListCourseNode extends AbstractAccessableCourseNode implements */ @Override public String getDetailsListView(UserCourseEnvironment userCourseEnvironment) { - return "checklist"; + return null; } /** diff --git a/src/main/java/org/olat/course/nodes/GTACourseNode.java b/src/main/java/org/olat/course/nodes/GTACourseNode.java index 8f5f607abd8..4b9bc346e85 100644 --- a/src/main/java/org/olat/course/nodes/GTACourseNode.java +++ b/src/main/java/org/olat/course/nodes/GTACourseNode.java @@ -61,10 +61,7 @@ import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.archiver.ScoreAccountingHelper; import org.olat.course.assessment.AssessmentManager; -import org.olat.course.assessment.bulk.BulkAssessmentToolController; -import org.olat.course.assessment.ui.tool.DefaultToolsControllerCreator; -import org.olat.course.assessment.ui.tool.IdentityListCourseNodeToolsController; -import org.olat.course.assessment.ui.tool.ToolsControllerCreator; +import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; @@ -78,13 +75,11 @@ import org.olat.course.nodes.gta.Task; import org.olat.course.nodes.gta.TaskHelper; import org.olat.course.nodes.gta.TaskList; import org.olat.course.nodes.gta.model.TaskDefinition; -import org.olat.course.nodes.gta.ui.BulkDownloadToolController; import org.olat.course.nodes.gta.ui.GTAAssessmentDetailsController; import org.olat.course.nodes.gta.ui.GTACoachedGroupListController; import org.olat.course.nodes.gta.ui.GTAEditController; -import org.olat.course.nodes.gta.ui.GTAGroupAssessmentToolController; +import org.olat.course.nodes.gta.ui.GTAIdentityListCourseNodeController; import org.olat.course.nodes.gta.ui.GTARunController; -import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.ScoreEvaluation; @@ -93,9 +88,10 @@ import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.group.BusinessGroup; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; -import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentRunStatus; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.repository.RepositoryEntry; import org.olat.user.UserManager; @@ -888,59 +884,12 @@ public class GTACourseNode extends AbstractAccessableCourseNode implements Persi return new GTACoachedGroupListController(ureq, wControl, stackPanel, coachCourseEnv, this, groups); } - - @Override - public ToolsControllerCreator getAssessmentToolsCreator() { - return new DefaultToolsControllerCreator() { - @Override - public boolean hasCalloutTools() { - return true; - } - - @Override - public Controller createCalloutController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, Identity assessedIdentity) { - return new IdentityListCourseNodeToolsController(ureq, wControl, GTACourseNode.this, assessedIdentity, coachCourseEnv); - } - - @Override - public List<Controller> createAssessmentTools(UserRequest ureq, WindowControl wControl, - TooledStackedPanel stackPanel, UserCourseEnvironment coachCourseEnv, - AssessmentToolOptions options) { - return createAssessmentToolList(ureq, wControl, coachCourseEnv, options); - } - }; - } - - private List<Controller> createAssessmentToolList(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, AssessmentToolOptions options) { - ModuleConfiguration config = getModuleConfiguration(); - CourseEnvironment courseEnv = coachCourseEnv.getCourseEnvironment(); - List<Controller> tools = new ArrayList<>(2); - if(GTAType.group.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE)) - && (config.getBooleanSafe(GTASK_ASSIGNMENT) - || config.getBooleanSafe(GTASK_SUBMIT) - || config.getBooleanSafe(GTASK_REVIEW_AND_CORRECTION) - || config.getBooleanSafe(GTASK_REVISION_PERIOD))) { - - if(options.getGroup() != null && !coachCourseEnv.isCourseReadOnly()) { - tools.add(new GTAGroupAssessmentToolController(ureq, wControl, courseEnv, options.getGroup(), this)); - } - tools.add(new BulkDownloadToolController(ureq, wControl, courseEnv, options, this)); - } else if(GTAType.individual.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE))) { - if(!coachCourseEnv.isCourseReadOnly() && (config.getBooleanSafe(GTASK_REVIEW_AND_CORRECTION) || config.getBooleanSafe(GTASK_GRADING))){ - tools.add(new BulkAssessmentToolController(ureq, wControl, courseEnv, this)); - } - - if(config.getBooleanSafe(GTASK_ASSIGNMENT) - || config.getBooleanSafe(GTASK_SUBMIT) - || config.getBooleanSafe(GTASK_REVIEW_AND_CORRECTION) - || config.getBooleanSafe(GTASK_REVISION_PERIOD)) { - tools.add(new BulkDownloadToolController(ureq, wControl, courseEnv, options, this)); - } - } - return tools; + public AssessmentCourseNodeController getIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + return new GTAIdentityListCourseNodeController(ureq, wControl, stackPanel, + courseEntry, group, this, coachCourseEnv, toolContainer, assessmentCallback); } @Override diff --git a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java index 3523bf598ad..96ce9b104fe 100644 --- a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java +++ b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java @@ -28,7 +28,6 @@ package org.olat.course.nodes; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -57,21 +56,17 @@ import org.olat.core.util.resource.OresHelper; import org.olat.course.ICourse; import org.olat.course.archiver.ScoreAccountingHelper; import org.olat.course.assessment.AssessmentManager; -import org.olat.course.assessment.ui.tool.DefaultToolsControllerCreator; -import org.olat.course.assessment.ui.tool.IdentityListCourseNodeProvider; -import org.olat.course.assessment.ui.tool.IdentityListCourseNodeToolsController; -import org.olat.course.assessment.ui.tool.ToolsControllerCreator; +import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; import org.olat.course.editor.StatusDescription; import org.olat.course.nodes.iq.CourseIQSecurityCallback; -import org.olat.course.nodes.iq.QTI21ExtraTimeController; import org.olat.course.nodes.iq.IQEditController; +import org.olat.course.nodes.iq.IQIdentityListCourseNodeController; import org.olat.course.nodes.iq.IQPreviewController; import org.olat.course.nodes.iq.IQRunController; import org.olat.course.nodes.iq.QTI21AssessmentRunController; -import org.olat.course.nodes.iq.QTI21IdentityListCourseNodeToolsController; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.navigation.NodeRunConstructionResult; @@ -83,6 +78,7 @@ import org.olat.course.statistic.StatisticResourceOption; import org.olat.course.statistic.StatisticResourceResult; import org.olat.fileresource.FileResourceManager; import org.olat.fileresource.types.ImsQTI21Resource; +import org.olat.group.BusinessGroup; import org.olat.ims.qti.QTI12ResultDetailsController; import org.olat.ims.qti.QTIResultManager; import org.olat.ims.qti.QTIResultSet; @@ -99,35 +95,27 @@ import org.olat.ims.qti.export.QTIExportSCQItemFormatConfig; import org.olat.ims.qti.fileresource.TestFileResource; import org.olat.ims.qti.process.AssessmentInstance; import org.olat.ims.qti.process.FilePersister; -import org.olat.ims.qti.resultexport.QTI12ExportResultsReportController; import org.olat.ims.qti.resultexport.QTI12ResultsExportMediaResource; import org.olat.ims.qti.statistics.QTIStatisticResourceResult; import org.olat.ims.qti.statistics.QTIStatisticSearchParams; import org.olat.ims.qti.statistics.QTIType; -import org.olat.ims.qti.statistics.ui.QTI12PullTestsToolController; -import org.olat.ims.qti.statistics.ui.QTI12StatisticsToolController; import org.olat.ims.qti21.AssessmentTestSession; 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.resultexport.QTI21ExportResultsReportController; import org.olat.ims.qti21.resultexport.QTI21ResultsExportMediaResource; import org.olat.ims.qti21.ui.QTI21AssessmentDetailsController; -import org.olat.ims.qti21.ui.QTI21ResetToolController; -import org.olat.ims.qti21.ui.QTI21RetrieveTestsToolController; -import org.olat.ims.qti21.ui.assessment.QTI21CorrectionToolController; -import org.olat.ims.qti21.ui.assessment.QTI21ValidationToolController; import org.olat.ims.qti21.ui.statistics.QTI21StatisticResourceResult; import org.olat.ims.qti21.ui.statistics.QTI21StatisticsSecurityCallback; -import org.olat.ims.qti21.ui.statistics.QTI21StatisticsToolController; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; -import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentEntryStatus; import org.olat.modules.assessment.model.AssessmentRunStatus; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.modules.iq.IQSecurityCallback; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryImportExport; @@ -238,20 +226,49 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements Pe */ public boolean hasQTI21TimeLimit(RepositoryEntry testEntry) { boolean timeLimit = false; - - ModuleConfiguration config = getModuleConfiguration(); - boolean configRef = config.getBooleanSafe(IQEditController.CONFIG_KEY_CONFIG_REF, false); - if(!configRef && config.getIntegerSafe(IQEditController.CONFIG_KEY_TIME_LIMIT, -1) > 0) { - timeLimit = true; - } else { - File unzippedDirRoot = FileResourceManager.getInstance().unzipFileResource(testEntry.getOlatResource()); - ResolvedAssessmentTest resolvedAssessmentTest = CoreSpringFactory.getImpl(QTI21Service.class) - .loadAndResolveAssessmentTest(unzippedDirRoot, false, false); - AssessmentTest assessmentTest = resolvedAssessmentTest.getRootNodeLookup().extractIfSuccessful(); - if(assessmentTest.getTimeLimits() != null && assessmentTest.getTimeLimits().getMaximum() != null) { + if(ImsQTI21Resource.TYPE_NAME.equals(testEntry.getOlatResource().getResourceableTypeName())) { + ModuleConfiguration config = getModuleConfiguration(); + boolean configRef = config.getBooleanSafe(IQEditController.CONFIG_KEY_CONFIG_REF, false); + if(!configRef && config.getIntegerSafe(IQEditController.CONFIG_KEY_TIME_LIMIT, -1) > 0) { timeLimit = true; + } else { + File unzippedDirRoot = FileResourceManager.getInstance().unzipFileResource(testEntry.getOlatResource()); + ResolvedAssessmentTest resolvedAssessmentTest = CoreSpringFactory.getImpl(QTI21Service.class) + .loadAndResolveAssessmentTest(unzippedDirRoot, false, false); + AssessmentTest assessmentTest = resolvedAssessmentTest.getRootNodeLookup().extractIfSuccessful(); + if(assessmentTest.getTimeLimits() != null && assessmentTest.getTimeLimits().getMaximum() != null) { + timeLimit = true; + } } - } + } + return timeLimit; + } + + /** + * If the course element override the test configuration, the value is from + * the course element's configuration. Else, the value is from the assessment + * test. + * + * @param testEntry The test repository entry + * @return the maximum time limit in seconds or -1 if no time limit is configured + */ + public int getQTI21TimeLimitMaxInSeconds(RepositoryEntry testEntry) { + int timeLimit = -1; + if(ImsQTI21Resource.TYPE_NAME.equals(testEntry.getOlatResource().getResourceableTypeName())) { + ModuleConfiguration config = getModuleConfiguration(); + boolean configRef = config.getBooleanSafe(IQEditController.CONFIG_KEY_CONFIG_REF, false); + if(!configRef && config.getIntegerSafe(IQEditController.CONFIG_KEY_TIME_LIMIT, -1) > 0) { + timeLimit = config.getIntegerSafe(IQEditController.CONFIG_KEY_TIME_LIMIT, -1); + } else { + File unzippedDirRoot = FileResourceManager.getInstance().unzipFileResource(testEntry.getOlatResource()); + ResolvedAssessmentTest resolvedAssessmentTest = CoreSpringFactory.getImpl(QTI21Service.class) + .loadAndResolveAssessmentTest(unzippedDirRoot, false, false); + AssessmentTest assessmentTest = resolvedAssessmentTest.getRootNodeLookup().extractIfSuccessful(); + if(assessmentTest.getTimeLimits() != null && assessmentTest.getTimeLimits().getMaximum() != null) { + timeLimit = assessmentTest.getTimeLimits().getMaximum().intValue(); + } + } + } return timeLimit; } @@ -275,81 +292,11 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements Pe } @Override - public ToolsControllerCreator getAssessmentToolsCreator() { - return new DefaultToolsControllerCreator() { - @Override - public boolean hasCalloutTools() { - return true; - } - - @Override - public Controller createCalloutController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, Identity assessedIdentity) { - boolean qti21 = IQEditController.CONFIG_VALUE_QTI21.equals(getModuleConfiguration().get(IQEditController.CONFIG_KEY_TYPE_QTI)); - if (qti21) { - return new QTI21IdentityListCourseNodeToolsController(ureq, wControl, IQTESTCourseNode.this, assessedIdentity, coachCourseEnv); - } - return new IdentityListCourseNodeToolsController(ureq, wControl, IQTESTCourseNode.this, assessedIdentity, coachCourseEnv); - } - - @Override - public List<Controller> createAssessmentTools(UserRequest ureq, WindowControl wControl, - TooledStackedPanel stackPanel, UserCourseEnvironment coachCourseEnv, - AssessmentToolOptions options) { - return createAssessmentToolList(ureq, wControl, stackPanel, coachCourseEnv, options); - } - - @Override - public List<Controller> createMultiSelectionTools(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, IdentityListCourseNodeProvider provider) { - return createMultiSelectionToolList(ureq, wControl, coachCourseEnv, provider); - } - }; - } - - private List<Controller> createMultiSelectionToolList(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, IdentityListCourseNodeProvider provider) { - List<Controller> tools = new ArrayList<>(2); - RepositoryEntry qtiTestEntry = getReferencedRepositoryEntry(); - if(ImsQTI21Resource.TYPE_NAME.equals(qtiTestEntry.getOlatResource().getResourceableTypeName()) && hasQTI21TimeLimit(qtiTestEntry)) { - tools.add(new QTI21ExtraTimeController(ureq, wControl, coachCourseEnv.getCourseEnvironment(), this, provider)); - } - return tools; - } - - private List<Controller> createAssessmentToolList(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, - UserCourseEnvironment coachCourseEnv, AssessmentToolOptions options) { - List<Controller> tools = new ArrayList<>(); - - CourseEnvironment courseEnv = coachCourseEnv.getCourseEnvironment(); - - RepositoryEntry qtiTestEntry = getReferencedRepositoryEntry(); - if(ImsQTI21Resource.TYPE_NAME.equals(qtiTestEntry.getOlatResource().getResourceableTypeName())) { - tools.add(new QTI21StatisticsToolController(ureq, wControl, stackPanel, courseEnv, options, this)); - if(!coachCourseEnv.isCourseReadOnly()) { - QTI21Service qtiService = CoreSpringFactory.getImpl(QTI21Service.class); - tools.add(new QTI21RetrieveTestsToolController(ureq, wControl, courseEnv, options, this)); - if(options.isAdmin()) { - tools.add(new QTI21ResetToolController(ureq, wControl, courseEnv, options, this)); - } - if(qtiService.needManualCorrection(qtiTestEntry) - || IQEditController.CORRECTION_MANUAL.equals(getModuleConfiguration().getStringValue(IQEditController.CONFIG_CORRECTION_MODE))) { - tools.add(new QTI21CorrectionToolController(ureq, wControl, courseEnv, options, this)); - } - if(getModuleConfiguration().getBooleanSafe(IQEditController.CONFIG_DIGITAL_SIGNATURE, false)) { - tools.add(new QTI21ValidationToolController(ureq, wControl)); - } - } - tools.add(new QTI21ExportResultsReportController(ureq, wControl, courseEnv, options, this)); - - } else { - tools.add(new QTI12StatisticsToolController(ureq, wControl, stackPanel, courseEnv, options, this)); - if(!coachCourseEnv.isCourseReadOnly()) { - tools.add(new QTI12PullTestsToolController(ureq, wControl, courseEnv, options, this)); - } - tools.add(new QTI12ExportResultsReportController(ureq, wControl, courseEnv, options, this)); - } - return tools; + public AssessmentCourseNodeController getIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + return new IQIdentityListCourseNodeController(ureq, wControl, stackPanel, + courseEntry, group, this, coachCourseEnv, toolContainer, assessmentCallback); } public boolean isQTI12TestRunning(Identity assessedIdentity, CourseEnvironment courseEnv) { diff --git a/src/main/java/org/olat/course/nodes/MSCourseNode.java b/src/main/java/org/olat/course/nodes/MSCourseNode.java index b1f4b679fd4..629641a2ba2 100644 --- a/src/main/java/org/olat/course/nodes/MSCourseNode.java +++ b/src/main/java/org/olat/course/nodes/MSCourseNode.java @@ -26,7 +26,6 @@ package org.olat.course.nodes; import java.io.File; -import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -47,10 +46,7 @@ import org.olat.core.logging.OLATRuntimeException; import org.olat.core.util.Util; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentManager; -import org.olat.course.assessment.bulk.BulkAssessmentToolController; -import org.olat.course.assessment.ui.tool.DefaultToolsControllerCreator; -import org.olat.course.assessment.ui.tool.IdentityListCourseNodeToolsController; -import org.olat.course.assessment.ui.tool.ToolsControllerCreator; +import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; @@ -58,19 +54,21 @@ import org.olat.course.editor.StatusDescription; import org.olat.course.nodes.ms.MSCourseNodeEditController; import org.olat.course.nodes.ms.MSCourseNodeRunController; import org.olat.course.nodes.ms.MSEditFormController; +import org.olat.course.nodes.ms.MSIdentityListCourseNodeController; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.properties.PersistingCoursePropertyManager; -import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroup; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; -import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentRunStatus; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; import org.olat.resource.OLATResource; @@ -526,31 +524,11 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Persis } @Override - public ToolsControllerCreator getAssessmentToolsCreator() { - return new DefaultToolsControllerCreator() { - @Override - public boolean hasCalloutTools() { - return true; - } - - @Override - public Controller createCalloutController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, Identity assessedIdentity) { - return new IdentityListCourseNodeToolsController(ureq, wControl, MSCourseNode.this, assessedIdentity, coachCourseEnv); - } - - @Override - public List<Controller> createAssessmentTools(UserRequest ureq, WindowControl wControl, - TooledStackedPanel stackPanel, UserCourseEnvironment coachCourseEnv, - AssessmentToolOptions options) { - List<Controller> tools = new ArrayList<>(1); - if(!coachCourseEnv.isCourseReadOnly()) { - CourseEnvironment courseEnv = coachCourseEnv.getCourseEnvironment(); - tools.add(new BulkAssessmentToolController(ureq, wControl, courseEnv, MSCourseNode.this)); - } - return tools; - } - }; + public AssessmentCourseNodeController getIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + return new MSIdentityListCourseNodeController(ureq, wControl, stackPanel, + courseEntry, group, this, coachCourseEnv, toolContainer, assessmentCallback); } @Override diff --git a/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java b/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java index 718c1b54325..fb583f15ca3 100644 --- a/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java +++ b/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java @@ -29,6 +29,7 @@ import java.util.Locale; import org.olat.core.CoreSpringFactory; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; +import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.messages.MessageUIFactory; @@ -44,9 +45,8 @@ import org.olat.core.util.Util; import org.olat.core.util.ValidationStatus; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentManager; -import org.olat.course.assessment.ui.tool.DefaultToolsControllerCreator; -import org.olat.course.assessment.ui.tool.IdentityListCourseNodeToolsController; -import org.olat.course.assessment.ui.tool.ToolsControllerCreator; +import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeController; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.condition.Condition; import org.olat.course.condition.interpreter.ConditionInterpreter; @@ -64,10 +64,13 @@ import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroup; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentRunStatus; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.modules.portfolio.PortfolioService; import org.olat.modules.portfolio.handler.BinderTemplateResource; import org.olat.modules.portfolio.ui.PortfolioAssessmentDetailsController; @@ -169,19 +172,11 @@ public class PortfolioCourseNode extends AbstractAccessableCourseNode implements } @Override - public ToolsControllerCreator getAssessmentToolsCreator() { - return new DefaultToolsControllerCreator() { - @Override - public boolean hasCalloutTools() { - return true; - } - - @Override - public Controller createCalloutController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, Identity assessedIdentity) { - return new IdentityListCourseNodeToolsController(ureq, wControl, PortfolioCourseNode.this, assessedIdentity, coachCourseEnv); - } - }; + public AssessmentCourseNodeController getIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + return new IdentityListCourseNodeController(ureq, wControl, stackPanel, + courseEntry, group, this, coachCourseEnv, toolContainer, assessmentCallback); } /** @@ -476,7 +471,7 @@ public class PortfolioCourseNode extends AbstractAccessableCourseNode implements @Override public String getDetailsListViewHeaderKey() { - return "table.header.details.ta"; + return null; } @Override diff --git a/src/main/java/org/olat/course/nodes/ProjectBrokerCourseNode.java b/src/main/java/org/olat/course/nodes/ProjectBrokerCourseNode.java index d8c193ea398..e5b32279356 100644 --- a/src/main/java/org/olat/course/nodes/ProjectBrokerCourseNode.java +++ b/src/main/java/org/olat/course/nodes/ProjectBrokerCourseNode.java @@ -78,9 +78,7 @@ import org.olat.core.util.vfs.VFSManager; import org.olat.core.util.xml.XStreamHelper; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentManager; -import org.olat.course.assessment.bulk.BulkAssessmentToolController; -import org.olat.course.assessment.ui.tool.DefaultToolsControllerCreator; -import org.olat.course.assessment.ui.tool.ToolsControllerCreator; +import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.condition.Condition; import org.olat.course.condition.interpreter.ConditionExpression; @@ -92,6 +90,7 @@ import org.olat.course.export.CourseEnvironmentMapper; import org.olat.course.nodes.ms.MSEditFormController; import org.olat.course.nodes.projectbroker.ProjectBrokerControllerFactory; import org.olat.course.nodes.projectbroker.ProjectBrokerCourseEditorController; +import org.olat.course.nodes.projectbroker.ProjectBrokerIdentityListCourseNodeController; import org.olat.course.nodes.projectbroker.ProjectListController; import org.olat.course.nodes.projectbroker.datamodel.Project; import org.olat.course.nodes.projectbroker.datamodel.ProjectBroker; @@ -103,7 +102,6 @@ import org.olat.course.nodes.ta.ReturnboxController; import org.olat.course.nodes.ta.TaskController; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.properties.PersistingCoursePropertyManager; -import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.ScoreEvaluation; @@ -114,9 +112,10 @@ import org.olat.group.BusinessGroupService; import org.olat.group.model.BusinessGroupReference; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; -import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentRunStatus; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; import org.olat.resource.OLATResource; @@ -745,25 +744,13 @@ public class ProjectBrokerCourseNode extends GenericCourseNode implements Persis } @Override - public ToolsControllerCreator getAssessmentToolsCreator() { - return new DefaultToolsControllerCreator() { - @Override - public List<Controller> createAssessmentTools(UserRequest ureq, WindowControl wControl, - TooledStackedPanel stackPanel, UserCourseEnvironment coachCourseEnv, - AssessmentToolOptions options) { - List<Controller> tools = new ArrayList<>(1); - if(!coachCourseEnv.isCourseReadOnly()) { - CourseEnvironment courseEnv = coachCourseEnv.getCourseEnvironment(); - tools.add(new BulkAssessmentToolController(ureq, wControl, courseEnv, ProjectBrokerCourseNode.this)); - } - return tools; - } - }; + public AssessmentCourseNodeController getIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + return new ProjectBrokerIdentityListCourseNodeController(ureq, wControl, stackPanel, + courseEntry, group, this, coachCourseEnv, toolContainer, assessmentCallback); } - /** - * @see org.olat.course.nodes.AssessableCourseNode#getDetailsListView(org.olat.course.run.userview.UserCourseEnvironment) - */ @Override public String getDetailsListView(UserCourseEnvironment userCourseEnvironment) { Identity identity = userCourseEnvironment.getIdentityEnvironment().getIdentity(); @@ -775,7 +762,7 @@ public class ProjectBrokerCourseNode extends GenericCourseNode implements Persis @Override public String getDetailsListViewHeaderKey() { - return "table.header.details.ta"; + return null; } @Override diff --git a/src/main/java/org/olat/course/nodes/STCourseNode.java b/src/main/java/org/olat/course/nodes/STCourseNode.java index 05b28cbfdbe..7e6583749c6 100644 --- a/src/main/java/org/olat/course/nodes/STCourseNode.java +++ b/src/main/java/org/olat/course/nodes/STCourseNode.java @@ -36,6 +36,7 @@ import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory; import org.olat.core.commons.modules.singlepage.SinglePageController; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; +import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.creator.ControllerCreator; @@ -56,8 +57,7 @@ import org.olat.core.util.resource.OresHelper; import org.olat.course.CourseFactory; import org.olat.course.CourseModule; import org.olat.course.ICourse; -import org.olat.course.assessment.ui.tool.DefaultToolsControllerCreator; -import org.olat.course.assessment.ui.tool.ToolsControllerCreator; +import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; import org.olat.course.condition.Condition; import org.olat.course.condition.KeyAndNameConverter; import org.olat.course.condition.interpreter.ConditionExpression; @@ -72,6 +72,7 @@ import org.olat.course.nodes.sp.SPEditController; import org.olat.course.nodes.sp.SPPeekviewController; import org.olat.course.nodes.st.STCourseNodeEditController; import org.olat.course.nodes.st.STCourseNodeRunController; +import org.olat.course.nodes.st.STIdentityListCourseNodeController; import org.olat.course.nodes.st.STPeekViewController; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.scoring.AssessmentEvaluation; @@ -81,10 +82,13 @@ import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.tree.CourseInternalLinkTreeModel; +import org.olat.group.BusinessGroup; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentRunStatus; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.repository.RepositoryEntry; import org.olat.util.logging.activity.LoggingResourceable; @@ -653,10 +657,13 @@ public class STCourseNode extends AbstractAccessableCourseNode implements Calcul public String getDetailsListViewHeaderKey() { throw new OLATRuntimeException(STCourseNode.class, "Details not available in ST nodes", null); } - + @Override - public ToolsControllerCreator getAssessmentToolsCreator() { - return new DefaultToolsControllerCreator(); + public AssessmentCourseNodeController getIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + return new STIdentityListCourseNodeController(ureq, wControl, stackPanel, + courseEntry, group, this, coachCourseEnv, toolContainer, assessmentCallback); } /** diff --git a/src/main/java/org/olat/course/nodes/ScormCourseNode.java b/src/main/java/org/olat/course/nodes/ScormCourseNode.java index 789c0673ac7..faea014f590 100644 --- a/src/main/java/org/olat/course/nodes/ScormCourseNode.java +++ b/src/main/java/org/olat/course/nodes/ScormCourseNode.java @@ -36,6 +36,7 @@ import java.util.zip.ZipOutputStream; import org.apache.commons.io.IOUtils; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; +import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.iframe.DeliveryOptions; @@ -50,9 +51,8 @@ import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentManager; -import org.olat.course.assessment.ui.tool.DefaultToolsControllerCreator; -import org.olat.course.assessment.ui.tool.IdentityListCourseNodeToolsController; -import org.olat.course.assessment.ui.tool.ToolsControllerCreator; +import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeController; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; @@ -67,10 +67,13 @@ import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.fileresource.types.ScormCPFileResource; +import org.olat.group.BusinessGroup; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentRunStatus; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.modules.scorm.ScormMainManager; import org.olat.modules.scorm.ScormPackageConfig; import org.olat.modules.scorm.archiver.ScormExportManager; @@ -148,20 +151,11 @@ public class ScormCourseNode extends AbstractAccessableCourseNode implements Per } @Override - public ToolsControllerCreator getAssessmentToolsCreator() { - return new DefaultToolsControllerCreator() { - - @Override - public boolean hasCalloutTools() { - return true; - } - - @Override - public Controller createCalloutController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, Identity assessedIdentity) { - return new IdentityListCourseNodeToolsController(ureq, wControl, ScormCourseNode.this, assessedIdentity, coachCourseEnv); - } - }; + public AssessmentCourseNodeController getIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + return new IdentityListCourseNodeController(ureq, wControl, stackPanel, + courseEntry, group, this, coachCourseEnv, toolContainer, assessmentCallback); } /** diff --git a/src/main/java/org/olat/course/nodes/TACourseNode.java b/src/main/java/org/olat/course/nodes/TACourseNode.java index 6674c612430..d047e2acc08 100644 --- a/src/main/java/org/olat/course/nodes/TACourseNode.java +++ b/src/main/java/org/olat/course/nodes/TACourseNode.java @@ -68,10 +68,7 @@ import org.olat.core.util.vfs.VFSManager; import org.olat.course.ICourse; import org.olat.course.archiver.ScoreAccountingHelper; import org.olat.course.assessment.AssessmentManager; -import org.olat.course.assessment.bulk.BulkAssessmentToolController; -import org.olat.course.assessment.ui.tool.DefaultToolsControllerCreator; -import org.olat.course.assessment.ui.tool.IdentityListCourseNodeToolsController; -import org.olat.course.assessment.ui.tool.ToolsControllerCreator; +import org.olat.course.assessment.ui.tool.AssessmentCourseNodeController; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.condition.Condition; import org.olat.course.condition.interpreter.ConditionExpression; @@ -81,13 +78,13 @@ import org.olat.course.editor.NodeEditController; import org.olat.course.editor.StatusDescription; import org.olat.course.export.CourseEnvironmentMapper; import org.olat.course.nodes.ms.MSEditFormController; -import org.olat.course.nodes.ta.BulkDownloadToolController; import org.olat.course.nodes.ta.ConvertToGTACourseNode; import org.olat.course.nodes.ta.DropboxController; import org.olat.course.nodes.ta.DropboxScoringViewController; import org.olat.course.nodes.ta.ReturnboxController; import org.olat.course.nodes.ta.TACourseNodeEditController; import org.olat.course.nodes.ta.TACourseNodeRunController; +import org.olat.course.nodes.ta.TAIdentityListCourseNodeController; import org.olat.course.nodes.ta.TaskController; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.properties.PersistingCoursePropertyManager; @@ -97,11 +94,13 @@ import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroup; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; -import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentRunStatus; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; import org.olat.resource.OLATResource; @@ -786,32 +785,11 @@ public class TACourseNode extends GenericCourseNode implements PersistentAssessa } @Override - public ToolsControllerCreator getAssessmentToolsCreator() { - return new DefaultToolsControllerCreator() { - @Override - public boolean hasCalloutTools() { - return true; - } - - @Override - public Controller createCalloutController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment coachCourseEnv, Identity assessedIdentity) { - return new IdentityListCourseNodeToolsController(ureq, wControl, TACourseNode.this, assessedIdentity, coachCourseEnv); - } - - @Override - public List<Controller> createAssessmentTools(UserRequest ureq, WindowControl wControl, - TooledStackedPanel stackPanel, UserCourseEnvironment coachCourseEnv, - AssessmentToolOptions options) { - List<Controller> tools = new ArrayList<Controller>(1); - CourseEnvironment courseEnv = coachCourseEnv.getCourseEnvironment(); - if(!coachCourseEnv.isCourseReadOnly()) { - tools.add(new BulkAssessmentToolController(ureq, wControl, courseEnv, TACourseNode.this)); - } - tools.add(new BulkDownloadToolController(ureq, wControl, courseEnv, options, TACourseNode.this)); - return tools; - } - }; + public AssessmentCourseNodeController getIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + return new TAIdentityListCourseNodeController(ureq, wControl, stackPanel, + courseEntry, group, this, coachCourseEnv, toolContainer, assessmentCallback); } /** diff --git a/src/main/java/org/olat/course/nodes/gta/ui/BulkDownloadToolController.java b/src/main/java/org/olat/course/nodes/gta/ui/BulkDownloadToolController.java deleted file mode 100644 index ad5983b25b0..00000000000 --- a/src/main/java/org/olat/course/nodes/gta/ui/BulkDownloadToolController.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * <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.course.nodes.gta.ui; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; -import org.olat.core.gui.control.Event; -import org.olat.core.gui.control.WindowControl; -import org.olat.core.gui.control.controller.BasicController; -import org.olat.course.archiver.ArchiveResource; -import org.olat.course.nodes.ArchiveOptions; -import org.olat.course.nodes.GTACourseNode; -import org.olat.course.run.environment.CourseEnvironment; -import org.olat.modules.assessment.AssessmentToolOptions; -import org.olat.resource.OLATResource; - -/** - * - * Initial date: 07.05.2015<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class BulkDownloadToolController extends BasicController { - - private final Link downloadButton; - - private final ArchiveOptions options; - private final OLATResource courseOres; - private final GTACourseNode courseNode; - - public BulkDownloadToolController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, - AssessmentToolOptions asOptions, GTACourseNode courseNode) { - super(ureq, wControl); - this.options = new ArchiveOptions(); - this.options.setGroup(asOptions.getGroup()); - this.options.setIdentities(asOptions.getIdentities()); - this.courseNode = courseNode; - courseOres = courseEnv.getCourseGroupManager().getCourseResource(); - - downloadButton = LinkFactory.createButton("bulk.download.title", null, this); - downloadButton.setTranslator(getTranslator()); - putInitialPanel(downloadButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout - } - - @Override - protected void doDispose() { - // - } - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - if(downloadButton == source) { - doDownload(ureq); - } - } - - private void doDownload(UserRequest ureq) { - ArchiveResource resource = new ArchiveResource(courseNode, courseOres, options, getLocale()); - ureq.getDispatchResult().setResultingMediaResource(resource); - } -} diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedGroupGradingController.java b/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedGroupGradingController.java index 0e83fbaef72..4bf62cf6b9d 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedGroupGradingController.java +++ b/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedGroupGradingController.java @@ -73,6 +73,7 @@ import org.olat.group.BusinessGroupService; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.Role; +import org.olat.repository.RepositoryEntry; import org.olat.user.UserManager; import org.olat.user.propertyhandlers.UserPropertyHandler; import org.springframework.beans.factory.annotation.Autowired; @@ -330,7 +331,8 @@ public class GTACoachedGroupGradingController extends FormBasicController { private void doOpenAssessmentForm(UserRequest ureq) { removeAsListenerAndDispose(assessmentCtrl); - assessmentCtrl = new GroupAssessmentController(ureq, getWindowControl(), courseEnv, gtaNode, assessedGroup); + RepositoryEntry courseEntry = courseEnv.getCourseGroupManager().getCourseEntry(); + assessmentCtrl = new GroupAssessmentController(ureq, getWindowControl(), courseEntry, gtaNode, assessedGroup); listenTo(assessmentCtrl); String title = translate("grading"); diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GTAGroupAssessmentToolController.java b/src/main/java/org/olat/course/nodes/gta/ui/GTAGroupAssessmentToolController.java deleted file mode 100644 index 0588a68bece..00000000000 --- a/src/main/java/org/olat/course/nodes/gta/ui/GTAGroupAssessmentToolController.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * <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.course.nodes.gta.ui; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; -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.gui.control.controller.BasicController; -import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; -import org.olat.course.nodes.GTACourseNode; -import org.olat.course.run.environment.CourseEnvironment; -import org.olat.group.BusinessGroup; - -/** - * - * Initial date: 30.03.2015<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class GTAGroupAssessmentToolController extends BasicController { - - private final Link statsButton; - - private CloseableModalController cmc; - private GroupAssessmentController assessmentCtrl; - - private final GTACourseNode gtaNode; - private final CourseEnvironment courseEnv; - private final BusinessGroup assessedGroup; - - public GTAGroupAssessmentToolController(UserRequest ureq, WindowControl wControl, - CourseEnvironment courseEnv, BusinessGroup assessedGroup, GTACourseNode gtaNode) { - super(ureq, wControl); - - this.gtaNode = gtaNode; - this.courseEnv = courseEnv; - this.assessedGroup = assessedGroup; - - statsButton = LinkFactory.createButton("assessment.group.tool", null, this); - statsButton.setTranslator(getTranslator()); - putInitialPanel(statsButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout - } - - @Override - protected void doDispose() { - // - } - - @Override - protected void event(UserRequest ureq, Controller source, Event event) { - if(assessmentCtrl == source) { - if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { - doGrading(); - fireEvent(ureq, Event.CHANGED_EVENT); - } - cmc.deactivate(); - cleanUp(); - } else if(cmc == source) { - cleanUp(); - } - - super.event(ureq, source, event); - } - - private void cleanUp() { - removeAsListenerAndDispose(assessmentCtrl); - removeAsListenerAndDispose(cmc); - assessmentCtrl = null; - cmc = null; - } - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - if(statsButton == source) { - doOpenAssessmentForm(ureq); - } - } - - private void doGrading() { - //assignedTask = gtaManager.updateTask(assignedTask, TaskProcess.graded); - } - - private void doOpenAssessmentForm(UserRequest ureq) { - removeAsListenerAndDispose(assessmentCtrl); - - assessmentCtrl = new GroupAssessmentController(ureq, getWindowControl(), courseEnv, gtaNode, assessedGroup); - listenTo(assessmentCtrl); - - String title = translate("grading"); - cmc = new CloseableModalController(getWindowControl(), "close", assessmentCtrl.getInitialComponent(), true, title, true); - listenTo(cmc); - cmc.activate(); - } -} \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GTAIdentityListCourseNodeController.java b/src/main/java/org/olat/course/nodes/gta/ui/GTAIdentityListCourseNodeController.java new file mode 100644 index 00000000000..35ca370aca0 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/gta/ui/GTAIdentityListCourseNodeController.java @@ -0,0 +1,224 @@ +/** + * <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.course.nodes.gta.ui; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; +import org.olat.core.gui.components.form.flexible.elements.FormLink; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.stack.TooledStackedPanel; +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.gui.control.generic.closablewrapper.CloseableModalController; +import org.olat.core.util.StringHelper; +import org.olat.course.archiver.ArchiveResource; +import org.olat.course.assessment.bulk.BulkAssessmentToolController; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeController; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeTableModel.IdentityCourseElementCols; +import org.olat.course.nodes.ArchiveOptions; +import org.olat.course.nodes.AssessableCourseNode; +import org.olat.course.nodes.GTACourseNode; +import org.olat.course.nodes.gta.GTAManager; +import org.olat.course.nodes.gta.GTAType; +import org.olat.course.nodes.gta.Task; +import org.olat.course.nodes.gta.TaskList; +import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroup; +import org.olat.modules.ModuleConfiguration; +import org.olat.modules.assessment.AssessmentToolOptions; +import org.olat.modules.assessment.ui.AssessedIdentityElementRow; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; +import org.olat.repository.RepositoryEntry; +import org.olat.resource.OLATResource; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * This specialized view of the list assessed identities show the chosen task + * and some bulk actions if they are configured in the course element. + * + * Initial date: 18 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class GTAIdentityListCourseNodeController extends IdentityListCourseNodeController { + + private FormLink downloadButton, groupAssessmentButton; + + private GroupAssessmentController assessmentCtrl; + + @Autowired + private GTAManager gtaManager; + + public GTAIdentityListCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, GTACourseNode courseNode, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + super(ureq, wControl, stackPanel, courseEntry, group, courseNode, coachCourseEnv, toolContainer, assessmentCallback); + } + + @Override + protected String getTableId() { + return "gta-assessment-tool-identity-list"; + } + + @Override + protected void initStatusColumns(FlexiTableColumnModel columnsModel) { + ModuleConfiguration config = courseNode.getModuleConfiguration(); + if(GTAType.individual.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE)) && config.getBooleanSafe(GTACourseNode.GTASK_ASSIGNMENT)) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.header.details.gta", IdentityCourseElementCols.details.ordinal())); + } + super.initStatusColumns(columnsModel); + } + + @Override + protected void initMultiSelectionTools(UserRequest ureq, FormLayoutContainer formLayout) { + ModuleConfiguration config = courseNode.getModuleConfiguration(); + if(GTAType.group.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE)) + && (config.getBooleanSafe(GTACourseNode.GTASK_ASSIGNMENT) + || config.getBooleanSafe(GTACourseNode.GTASK_SUBMIT) + || config.getBooleanSafe(GTACourseNode.GTASK_REVIEW_AND_CORRECTION) + || config.getBooleanSafe(GTACourseNode.GTASK_REVISION_PERIOD))) { + + if(!coachCourseEnv.isCourseReadOnly() && group != null) { + groupAssessmentButton = uifactory.addFormLink("assessment.group.tool", formLayout, Link.BUTTON); + } + + initBulkDownloadController(formLayout); + } else if(GTAType.individual.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE))) { + if(!coachCourseEnv.isCourseReadOnly() + && (config.getBooleanSafe(GTACourseNode.GTASK_REVIEW_AND_CORRECTION) || config.getBooleanSafe(GTACourseNode.GTASK_GRADING))){ + initBulkAsssessmentTool(ureq, formLayout); + } + + if(config.getBooleanSafe(GTACourseNode.GTASK_ASSIGNMENT) + || config.getBooleanSafe(GTACourseNode.GTASK_SUBMIT) + || config.getBooleanSafe(GTACourseNode.GTASK_REVIEW_AND_CORRECTION) + || config.getBooleanSafe(GTACourseNode.GTASK_REVISION_PERIOD)) { + initBulkDownloadController(formLayout); + } + } + super.initMultiSelectionTools(ureq, formLayout); + } + + private void initBulkDownloadController(FormLayoutContainer formLayout) { + downloadButton = uifactory.addFormLink("bulk.download.title", formLayout, Link.BUTTON); + } + + private void initBulkAsssessmentTool(UserRequest ureq, FormLayoutContainer formLayout) { + BulkAssessmentToolController bulkAssessmentTollCtrl = new BulkAssessmentToolController(ureq, getWindowControl(), + getCourseEnvironment(), (AssessableCourseNode)courseNode); + listenTo(bulkAssessmentTollCtrl); + formLayout.put("bulk.assessment", bulkAssessmentTollCtrl.getInitialComponent()); + } + + @Override + protected void loadModel(UserRequest ureq) { + super.loadModel(ureq); + + ModuleConfiguration config = courseNode.getModuleConfiguration(); + if(GTAType.individual.name().equals(config.getStringValue(GTACourseNode.GTASK_TYPE)) && config.getBooleanSafe(GTACourseNode.GTASK_ASSIGNMENT)) { + TaskList taskList = gtaManager.getTaskList(getCourseRepositoryEntry(), (GTACourseNode)courseNode); + if(taskList != null) { + loadTasksInModel(taskList); + } + } + } + + private void loadTasksInModel(TaskList taskList) { + List<Task> tasks = gtaManager.getTasks(taskList, (GTACourseNode)courseNode); + Map<Long,Task> identityToTask = new HashMap<>(); + for(Task task:tasks) { + if(task.getIdentity() != null) { + identityToTask.put(task.getIdentity().getKey(), task); + } + } + + List<AssessedIdentityElementRow> rows = usersTableModel.getObjects(); + for(AssessedIdentityElementRow row:rows) { + Task task = identityToTask.get(row.getIdentityKey()); + if(task != null && StringHelper.containsNonWhitespace(task.getTaskName())) { + row.setDetails(task.getTaskName()); + } + } + } + + @Override + public void event(UserRequest ureq, Controller source, Event event) { + if(assessmentCtrl == source) { + if(event == Event.CHANGED_EVENT || event == Event.DONE_EVENT) { + loadModel(ureq); + } + cmc.deactivate(); + cleanUp(); + } + super.event(ureq, source, event); + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(downloadButton == source) { + doDownload(ureq); + } else if(groupAssessmentButton == source) { + doOpenAssessmentForm(ureq); + } else { + super.formInnerEvent(ureq, source, event); + } + } + + @Override + protected void cleanUp() { + removeAsListenerAndDispose(assessmentCtrl); + assessmentCtrl = null; + super.cleanUp(); + } + + private void doDownload(UserRequest ureq) { + AssessmentToolOptions asOptions = getOptions(); + OLATResource courseOres = getCourseRepositoryEntry().getOlatResource(); + + ArchiveOptions options = new ArchiveOptions(); + options.setGroup(asOptions.getGroup()); + options.setIdentities(asOptions.getIdentities()); + + ArchiveResource resource = new ArchiveResource(courseNode, courseOres, options, getLocale()); + ureq.getDispatchResult().setResultingMediaResource(resource); + } + + private void doOpenAssessmentForm(UserRequest ureq) { + removeAsListenerAndDispose(assessmentCtrl); + + assessmentCtrl = new GroupAssessmentController(ureq, getWindowControl(), getCourseRepositoryEntry(), (GTACourseNode)courseNode, group); + listenTo(assessmentCtrl); + + String title = translate("grading"); + cmc = new CloseableModalController(getWindowControl(), "close", assessmentCtrl.getInitialComponent(), true, title, true); + listenTo(cmc); + cmc.activate(); + } +} diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GroupAssessmentController.java b/src/main/java/org/olat/course/nodes/gta/ui/GroupAssessmentController.java index ecd684d6dcb..e4783c9b7d1 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/GroupAssessmentController.java +++ b/src/main/java/org/olat/course/nodes/gta/ui/GroupAssessmentController.java @@ -50,7 +50,6 @@ import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowController; import org.olat.core.id.Identity; -import org.olat.core.id.OLATResourceable; import org.olat.core.id.Roles; import org.olat.core.id.UserConstants; import org.olat.core.util.CodeHelper; @@ -61,7 +60,6 @@ import org.olat.course.assessment.AssessmentHelper; import org.olat.course.nodes.GTACourseNode; import org.olat.course.nodes.gta.GTAManager; import org.olat.course.nodes.gta.ui.GroupAssessmentModel.Cols; -import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.group.BusinessGroup; @@ -69,6 +67,8 @@ import org.olat.group.BusinessGroupService; import org.olat.modules.assessment.AssessmentEntry; import org.olat.modules.assessment.Role; import org.olat.modules.assessment.model.AssessmentEntryStatus; +import org.olat.repository.RepositoryEntry; +import org.olat.resource.OLATResource; import org.olat.user.UserManager; import org.olat.user.propertyhandlers.UserPropertyHandler; import org.springframework.beans.factory.annotation.Autowired; @@ -101,7 +101,7 @@ public class GroupAssessmentController extends FormBasicController { private Float cutValue; private final boolean withScore, withPassed, withComment; private final GTACourseNode gtaNode; - private final CourseEnvironment courseEnv; + private final RepositoryEntry courseEntry; private final BusinessGroup assessedGroup; @Autowired @@ -116,10 +116,10 @@ public class GroupAssessmentController extends FormBasicController { private final List<Long> duplicateMemberKeys; public GroupAssessmentController(UserRequest ureq, WindowControl wControl, - CourseEnvironment courseEnv, GTACourseNode courseNode, BusinessGroup assessedGroup) { + RepositoryEntry courseEntry, GTACourseNode courseNode, BusinessGroup assessedGroup) { super(ureq, wControl, "assessment_per_group"); this.gtaNode = courseNode; - this.courseEnv = courseEnv; + this.courseEntry = courseEntry; this.assessedGroup = assessedGroup; withScore = courseNode.hasScoreConfigured(); @@ -293,7 +293,7 @@ public class GroupAssessmentController extends FormBasicController { */ private ModelInfos loadModel() { //load participants, load datas - ICourse course = CourseFactory.loadCourse(courseEnv.getCourseResourceableId()); + ICourse course = CourseFactory.loadCourse(courseEntry); List<Identity> identities = businessGroupService.getMembers(assessedGroup, GroupRoles.participant.name()); Map<Identity, AssessmentEntry> identityToEntryMap = new HashMap<>(); @@ -525,7 +525,7 @@ public class GroupAssessmentController extends FormBasicController { } private void applyChangesForEveryMemberGroup(List<AssessmentRow> rows, boolean setAsDone, boolean userVisible) { - ICourse course = CourseFactory.loadCourse(courseEnv.getCourseResourceableId()); + ICourse course = CourseFactory.loadCourse(courseEntry); for(AssessmentRow row:rows) { UserCourseEnvironment userCourseEnv = row.getUserCourseEnvironment(course); @@ -565,7 +565,7 @@ public class GroupAssessmentController extends FormBasicController { } private void applyChangesForTheWholeGroup(List<AssessmentRow> rows, boolean setAsDone, boolean userVisible) { - ICourse course = CourseFactory.loadCourse(courseEnv.getCourseResourceableId()); + ICourse course = CourseFactory.loadCourse(courseEntry); Float score = null; if(withScore) { @@ -614,7 +614,7 @@ public class GroupAssessmentController extends FormBasicController { private void doEditComment(UserRequest ureq, AssessmentRow row) { removeAsListenerAndDispose(commentCalloutCtrl); - OLATResourceable courseOres = courseEnv.getCourseGroupManager().getCourseResource(); + OLATResource courseOres = courseEntry.getOlatResource(); editCommentCtrl = new EditCommentController(ureq, getWindowControl(), courseOres, gtaNode, row); listenTo(editCommentCtrl); commentCalloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(), diff --git a/src/main/java/org/olat/course/nodes/gta/ui/_content/identity_courseelement.html b/src/main/java/org/olat/course/nodes/gta/ui/_content/identity_courseelement.html new file mode 100644 index 00000000000..780df08442b --- /dev/null +++ b/src/main/java/org/olat/course/nodes/gta/ui/_content/identity_courseelement.html @@ -0,0 +1,13 @@ +<h2><i class="o_icon o_gta_icon"> </i> $r.escapeHtml($courseNodeTitle)#if($r.isNotEmpty($businessGroupName)) <small><i class="o_icon o_icon_group"> </i> $r.escapeHtml($businessGroupName)</small>#end</h2> +<div class="o_button_group o_button_group_right"> + #if($r.available("bulk.assessment")) + $r.render("bulk.assessment") + #end + #if($r.available("bulk.download.title")) + $r.render("bulk.download.title") + #end + #if($r.available("group.assessment")) + $r.render("group.assessment") + #end +</div> +$r.render("table") diff --git a/src/main/java/org/olat/course/nodes/iq/ConfirmResetController.java b/src/main/java/org/olat/course/nodes/iq/ConfirmResetController.java deleted file mode 100644 index 53fc59f2263..00000000000 --- a/src/main/java/org/olat/course/nodes/iq/ConfirmResetController.java +++ /dev/null @@ -1,173 +0,0 @@ -/** - * <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.course.nodes.iq; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.zip.ZipOutputStream; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.form.flexible.FormItemContainer; -import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; -import org.olat.core.gui.components.form.flexible.impl.FormBasicController; -import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; -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.Identity; -import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; -import org.olat.core.util.Formatter; -import org.olat.core.util.StringHelper; -import org.olat.core.util.Util; -import org.olat.course.CourseFactory; -import org.olat.course.ICourse; -import org.olat.course.assessment.AssessmentHelper; -import org.olat.course.nodes.ArchiveOptions; -import org.olat.course.nodes.AssessableCourseNode; -import org.olat.course.run.scoring.ScoreEvaluation; -import org.olat.course.run.userview.UserCourseEnvironment; -import org.olat.ims.qti21.QTI21LoggingAction; -import org.olat.ims.qti21.QTI21Service; -import org.olat.ims.qti21.ui.AssessmentTestDisplayController; -import org.olat.modules.assessment.Role; -import org.olat.modules.assessment.model.AssessmentRunStatus; -import org.olat.repository.RepositoryEntry; -import org.olat.user.UserManager; -import org.springframework.beans.factory.annotation.Autowired; - -/** - * - * Initial date: 27 nov. 2017<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class ConfirmResetController extends FormBasicController { - - private final String[] onKeys = new String[]{ "on" }; - - private MultipleSelectionElement acknowledgeEl; - - private final Identity assessedIdentity; - private final AssessableCourseNode courseNode; - private final RepositoryEntry courseEntry, testEntry; - - @Autowired - private UserManager userManager; - @Autowired - private QTI21Service qtiService; - - public ConfirmResetController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry, - AssessableCourseNode courseNode, RepositoryEntry testEntry, Identity assessedIdentity) { - super(ureq, wControl, "confirm_reset_data"); - setTranslator(Util.createPackageTranslator(AssessmentTestDisplayController.class, getLocale(), getTranslator())); - this.assessedIdentity = assessedIdentity; - this.courseEntry = courseEntry; - this.courseNode = courseNode; - this.testEntry = testEntry; - initForm(ureq); - } - - @Override - protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { - if(formLayout instanceof FormLayoutContainer) { - FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; - String[] args = new String[]{ userManager.getUserDisplayName(assessedIdentity) }; - String msg = translate("reset.test.data.text.identity", args); - layoutCont.contextPut("msg", msg); - } - - FormLayoutContainer confirmCont = FormLayoutContainer.createDefaultFormLayout("confirm", getTranslator()); - formLayout.add("confirm", confirmCont); - confirmCont.setRootForm(mainForm); - - String[] onValues = new String[]{ translate("reset.test.data.acknowledge") }; - acknowledgeEl = uifactory.addCheckboxesHorizontal("acknowledge", "confirmation", confirmCont, onKeys, onValues); - - FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); - buttonsCont.setRootForm(mainForm); - confirmCont.add(buttonsCont); - uifactory.addFormCancelButton("cancel", buttonsCont, ureq, getWindowControl()); - uifactory.addFormSubmitButton("reset.data", buttonsCont); - } - - @Override - protected void doDispose() { - // - } - - @Override - protected boolean validateFormLogic(UserRequest ureq) { - boolean allOk = true; - - acknowledgeEl.clearError(); - if(!acknowledgeEl.isAtLeastSelected(1)) { - acknowledgeEl.setErrorKey("form.legende.mandatory", null); - allOk &= false; - } - - return allOk & super.validateFormLogic(ureq); - } - - @Override - protected void formOK(UserRequest ureq) { - ICourse course = CourseFactory.loadCourse(courseEntry); - UserCourseEnvironment assessedUserCourseEnv = AssessmentHelper - .createAndInitUserCourseEnvironment(assessedIdentity, course.getCourseEnvironment()); - - List<Identity> identities = Collections.singletonList(assessedIdentity); - ArchiveOptions options = new ArchiveOptions(); - options.setIdentities(identities); - - File exportDirectory = CourseFactory.getOrCreateDataExportDirectory(getIdentity(), course.getCourseTitle()); - String archiveName = courseNode.getType() - + "_" + StringHelper.transformDisplayNameToFileSystemName(userManager.getUserDisplayName(assessedIdentity)) - + "_" + StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName()) - + "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis())) + ".zip"; - - File exportFile = new File(exportDirectory, archiveName); - try(FileOutputStream fileStream = new FileOutputStream(exportFile); - ZipOutputStream exportStream = new ZipOutputStream(fileStream)) { - courseNode.archiveNodeData(getLocale(), course, options, exportStream, "UTF-8"); - } catch (IOException e) { - logError("", e); - } - - qtiService.deleteAssessmentTestSession(identities, testEntry, courseEntry, courseNode.getIdent()); - - ScoreEvaluation scoreEval = new ScoreEvaluation(null, null); - courseNode.updateUserScoreEvaluation(scoreEval, assessedUserCourseEnv, getIdentity(), false, Role.coach); - courseNode.updateCurrentCompletion(assessedUserCourseEnv, getIdentity(), null, AssessmentRunStatus.notStarted, Role.coach); - - - ThreadLocalUserActivityLogger.log(QTI21LoggingAction.QTI_RESET_IN_COURSE, getClass()); - - fireEvent(ureq, Event.DONE_EVENT); - } - - @Override - protected void formCancelled(UserRequest ureq) { - fireEvent(ureq, Event.CANCELLED_EVENT); - } - -} diff --git a/src/main/java/org/olat/course/nodes/iq/ExtraTimeCellRenderer.java b/src/main/java/org/olat/course/nodes/iq/ExtraTimeCellRenderer.java new file mode 100644 index 00000000000..8537ef32fab --- /dev/null +++ b/src/main/java/org/olat/course/nodes/iq/ExtraTimeCellRenderer.java @@ -0,0 +1,86 @@ +/** + * <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.course.nodes.iq; + +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponent; +import org.olat.core.gui.render.Renderer; +import org.olat.core.gui.render.StringOutput; +import org.olat.core.gui.render.URLBuilder; +import org.olat.core.gui.translator.Translator; +import org.olat.core.util.Formatter; + +/** + * + * Initial date: 20 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class ExtraTimeCellRenderer implements FlexiCellRenderer { + + private final boolean renderDueDate; + private final int timeLimitInSeconds; + private final Formatter formatter; + + public ExtraTimeCellRenderer(boolean renderDueDate, int timeLimitInSeconds, Locale locale) { + this.renderDueDate = renderDueDate; + this.timeLimitInSeconds = timeLimitInSeconds; + formatter = Formatter.getInstance(locale); + } + + @Override + public void render(Renderer renderer, StringOutput target, Object cellValue, int row, + FlexiTableComponent source, URLBuilder ubu, Translator translator) { + if(cellValue instanceof ExtraTimeInfos) { + ExtraTimeInfos infos = (ExtraTimeInfos)cellValue; + Integer extraTimeInSeconds = infos.getExtraTimeInSeconds(); + + if(renderDueDate) { + if(infos.getStart() != null) { + int totalTime = timeLimitInSeconds; + if(extraTimeInSeconds != null) { + totalTime += extraTimeInSeconds; + } + + Calendar now = Calendar.getInstance(); + Calendar cal = Calendar.getInstance(); + cal.setTime(infos.getStart()); + cal.add(Calendar.SECOND, totalTime); + Date dueDate = cal.getTime(); + + boolean sameDay = now.get(Calendar.YEAR) == cal.get(Calendar.YEAR) + && now.get(Calendar.DAY_OF_YEAR) == cal.get(Calendar.DAY_OF_YEAR); + if(sameDay) { + target.append(formatter.formatTime(dueDate)); + } else { + target.append(formatter.formatDateAndTime(dueDate)); + } + } + } else if(extraTimeInSeconds != null) { + int extraTimeInMinutes = extraTimeInSeconds.intValue() / 60; + target.append("+").append(extraTimeInMinutes).append("m"); + } + } + } +} diff --git a/src/main/java/org/olat/course/nodes/iq/ExtraTimeInfos.java b/src/main/java/org/olat/course/nodes/iq/ExtraTimeInfos.java new file mode 100644 index 00000000000..aebc107d4a3 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/iq/ExtraTimeInfos.java @@ -0,0 +1,56 @@ +/** + * <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.course.nodes.iq; + +import java.util.Date; + +/** + * + * Initial date: 20 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class ExtraTimeInfos implements Comparable<ExtraTimeInfos> { + + private final Integer extraTimeInSeconds; + private final Date start; + + public ExtraTimeInfos(Integer extraTimeInSeconds, Date start) { + this.extraTimeInSeconds = extraTimeInSeconds; + this.start = start; + } + + public Integer getExtraTimeInSeconds() { + return extraTimeInSeconds; + } + + public Date getStart() { + return start; + } + + @Override + public int compareTo(ExtraTimeInfos o) { + if(o == null) return -1; + if(extraTimeInSeconds == null && o.extraTimeInSeconds == null) return 0; + if(extraTimeInSeconds != null && o.extraTimeInSeconds == null) return -1; + if(extraTimeInSeconds == null && o.extraTimeInSeconds != null) return 1; + return extraTimeInSeconds.compareTo(o.extraTimeInSeconds); + } +} diff --git a/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java b/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java new file mode 100644 index 00000000000..a7ce92cad8d --- /dev/null +++ b/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java @@ -0,0 +1,513 @@ +/** + * <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.course.nodes.iq; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.olat.basesecurity.GroupRoles; +import org.olat.basesecurity.IdentityRef; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; +import org.olat.core.gui.components.form.flexible.elements.FormLink; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.stack.TooledStackedPanel; +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.gui.control.generic.closablewrapper.CloseableModalController; +import org.olat.core.gui.media.MediaResource; +import org.olat.core.id.Identity; +import org.olat.course.archiver.ScoreAccountingHelper; +import org.olat.course.assessment.AssessmentHelper; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeController; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeTableModel.IdentityCourseElementCols; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeToolsController; +import org.olat.course.nodes.IQTESTCourseNode; +import org.olat.course.nodes.iq.QTI21IdentityListCourseNodeToolsController.AssessmentTestSessionDetailsComparator; +import org.olat.course.run.environment.CourseEnvironment; +import org.olat.course.run.scoring.ScoreEvaluation; +import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.fileresource.types.ImsQTI21Resource; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupService; +import org.olat.ims.qti.resultexport.QTI12ResultsExportMediaResource; +import org.olat.ims.qti.statistics.ui.QTI12PullTestsToolController; +import org.olat.ims.qti.statistics.ui.QTI12StatisticsToolController; +import org.olat.ims.qti21.AssessmentTestSession; +import org.olat.ims.qti21.QTI21DeliveryOptions; +import org.olat.ims.qti21.QTI21Service; +import org.olat.ims.qti21.model.jpa.AssessmentTestSessionStatistics; +import org.olat.ims.qti21.resultexport.QTI21ResultsExportMediaResource; +import org.olat.ims.qti21.ui.QTI21ResetDataController; +import org.olat.ims.qti21.ui.QTI21RetrieveTestsController; +import org.olat.ims.qti21.ui.assessment.AssessmentTestCorrection; +import org.olat.ims.qti21.ui.assessment.IdentitiesAssessmentTestCorrectionController; +import org.olat.ims.qti21.ui.assessment.ValidationXmlSignatureController; +import org.olat.ims.qti21.ui.statistics.QTI21StatisticsToolController; +import org.olat.modules.ModuleConfiguration; +import org.olat.modules.assessment.AssessmentToolOptions; +import org.olat.modules.assessment.Role; +import org.olat.modules.assessment.ui.AssessedIdentityElementRow; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; +import org.olat.modules.assessment.ui.event.CompleteAssessmentTestSessionEvent; +import org.olat.repository.RepositoryEntry; +import org.springframework.beans.factory.annotation.Autowired; + +import de.bps.onyx.plugin.OnyxModule; + +/** + * + * Initial date: 18 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class IQIdentityListCourseNodeController extends IdentityListCourseNodeController { + + private FormLink extraTimeButton; + private FormLink exportResultsButton, statsButton, validateButton, correctionButton, pullButton, resetButton; + + private Controller statisticsCtrl; + private Controller retrieveConfirmationCtr; + private QTI21ResetDataController resetDataCtrl; + private ConfirmExtraTimeController extraTimeCtrl; + private ValidationXmlSignatureController validationCtrl; + private IdentitiesAssessmentTestCorrectionController correctionCtrl; + + @Autowired + private QTI21Service qtiService; + @Autowired + private BusinessGroupService groupService; + + + public IQIdentityListCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, IQTESTCourseNode courseNode, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + super(ureq, wControl, stackPanel, courseEntry, group, courseNode, coachCourseEnv, toolContainer, assessmentCallback); + } + + @Override + protected String getTableId() { + if(isTestQTI21()) { + return"qti21-assessment-tool-identity-list"; + } + return "qti-assessment-tool-identity-list"; + } + + @Override + protected void initStatusColumns(FlexiTableColumnModel columnsModel) { + super.initStatusColumns(columnsModel); + IQTESTCourseNode testCourseNode = (IQTESTCourseNode)courseNode; + if(testCourseNode != null && testCourseNode.hasCompletion()) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.currentCompletion)); + } + + RepositoryEntry qtiTestEntry = getReferencedRepositoryEntry(); + if(testCourseNode != null && testCourseNode.hasQTI21TimeLimit(qtiTestEntry)) { + int timeLimitInSeconds = testCourseNode.getQTI21TimeLimitMaxInSeconds(qtiTestEntry); + boolean suspendEnabled = isSuspendEnable(); + FlexiCellRenderer renderer = new ExtraTimeCellRenderer(!suspendEnabled, timeLimitInSeconds, getLocale()); + String header = suspendEnabled ? "table.header.extra.time" : "table.header.end.date"; + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(header, IdentityCourseElementCols.details.ordinal(), renderer)); + } + } + + private boolean isSuspendEnable() { + ModuleConfiguration config = courseNode.getModuleConfiguration(); + QTI21DeliveryOptions testOptions = qtiService.getDeliveryOptions(getReferencedRepositoryEntry()); + boolean configRef = config.getBooleanSafe(IQEditController.CONFIG_KEY_CONFIG_REF, false); + boolean suspendEnabled = false; + if(!configRef) { + suspendEnabled = config.getBooleanSafe(IQEditController.CONFIG_KEY_ENABLESUSPEND, testOptions.isEnableSuspend()); + } else { + suspendEnabled = testOptions.isEnableSuspend(); + } + return suspendEnabled; + } + + @Override + protected void initMultiSelectionTools(UserRequest ureq, FormLayoutContainer formLayout) { + //bulk + super.initMultiSelectionTools(ureq, formLayout); + + RepositoryEntry testEntry = getReferencedRepositoryEntry(); + if(((IQTESTCourseNode)courseNode).hasQTI21TimeLimit(testEntry)) { + extraTimeButton = uifactory.addFormLink("extra.time", formLayout, Link.BUTTON); + extraTimeButton.setIconLeftCSS("o_icon o_icon_extra_time"); + } + boolean qti21 = isTestQTI21(); + boolean onyx = !qti21 && OnyxModule.isOnyxTest(testEntry.getOlatResource()); + + statsButton = uifactory.addFormLink("button.stats", formLayout, Link.BUTTON); + statsButton.setIconLeftCSS("o_icon o_icon-fw o_icon_statistics_tool"); + + if(!coachCourseEnv.isCourseReadOnly()) { + if(!onyx) { + pullButton = uifactory.addFormLink("retrieve.tests.title", formLayout, Link.BUTTON); + pullButton.setIconLeftCSS("o_icon o_icon_pull"); + } + + if(qti21) { + if(assessmentCallback.isAdmin()) { + resetButton = uifactory.addFormLink("tool.delete.data", formLayout, Link.BUTTON); + resetButton.setIconLeftCSS("o_icon o_icon_delete_item"); + } + if(qtiService.needManualCorrection(testEntry) + || IQEditController.CORRECTION_MANUAL.equals(courseNode.getModuleConfiguration().getStringValue(IQEditController.CONFIG_CORRECTION_MODE))) { + correctionButton = uifactory.addFormLink("correction.test.title", formLayout, Link.BUTTON); + correctionButton.setIconLeftCSS("o_icon o_icon-fw o_icon_correction"); + } + if(courseNode.getModuleConfiguration().getBooleanSafe(IQEditController.CONFIG_DIGITAL_SIGNATURE, false)) { + validateButton = uifactory.addFormLink("validate.xml.signature", formLayout, Link.BUTTON); + validateButton.setIconLeftCSS("o_icon o_icon-fw o_icon_correction"); + } + } + } + + if(!onyx) { + exportResultsButton = uifactory.addFormLink("button.export", formLayout, Link.BUTTON); + exportResultsButton.setIconLeftCSS("o_icon o_icon-fw o_icon_export"); + } + } + + private boolean isTestRunning() { + List<Identity> identities = getIdentities(); + if(isTestQTI21()) { + return qtiService.isRunningAssessmentTestSession(getCourseRepositoryEntry(), + courseNode.getIdent(), getReferencedRepositoryEntry(), identities); + } + + for(Identity assessedIdentity:identities) { + if(((IQTESTCourseNode)courseNode).isQTI12TestRunning(assessedIdentity, getCourseEnvironment())) { + return true; + } + } + return false; + } + + private boolean isTestQTI21() { + return ImsQTI21Resource.TYPE_NAME.equals(getReferencedRepositoryEntry().getOlatResource().getResourceableTypeName()); + } + + @Override + protected void loadModel(UserRequest ureq) { + super.loadModel(ureq); + + if(((IQTESTCourseNode)courseNode).hasQTI21TimeLimit(getReferencedRepositoryEntry())) { + Map<Long,ExtraTimeInfos> extraTimeInfos = getExtraTimes(); + List<AssessedIdentityElementRow> rows = usersTableModel.getObjects(); + for(AssessedIdentityElementRow row:rows) { + row.setDetails(extraTimeInfos.get(row.getIdentityKey())); + } + } + + if(pullButton != null) { + boolean enabled = isTestRunning(); + pullButton.setEnabled(enabled); + } + } + + /** + * @return A map identity key to extra time + */ + private Map<Long,ExtraTimeInfos> getExtraTimes() { + Map<Long,ExtraTimeInfos> identityToExtraTime = new HashMap<>(); + List<AssessmentTestSession> sessions = qtiService + .getAssessmentTestSessions(getCourseRepositoryEntry(), courseNode.getIdent(), getReferencedRepositoryEntry()); + //sort by identity, then by creation date + Collections.sort(sessions, new AssessmentTestSessionComparator()); + + Long currentIdentityKey = null; + for(AssessmentTestSession session:sessions) { + Long identityKey = session.getIdentity().getKey(); + if(currentIdentityKey == null || !currentIdentityKey.equals(identityKey)) { + if(session.getFinishTime() == null && session.getExtraTime() != null) { + Integer extraTimeInSeconds = session.getExtraTime(); + Date start = session.getCreationDate(); + ExtraTimeInfos infos = new ExtraTimeInfos(extraTimeInSeconds, start); + identityToExtraTime.put(identityKey, infos); + } + currentIdentityKey = identityKey; + } + } + return identityToExtraTime; + } + + + @Override + public void event(UserRequest ureq, Controller source, Event event) { + if(validationCtrl == source) { + cmc.deactivate(); + cleanUp(); + } else if(correctionCtrl == source) { + if(event instanceof CompleteAssessmentTestSessionEvent) { + CompleteAssessmentTestSessionEvent catse = (CompleteAssessmentTestSessionEvent)event; + List<AssessmentTestSession> testSessionsToComplete = catse.getTestSessions(); + doUpdateCourseNode(correctionCtrl.getTestCorrections(), testSessionsToComplete); + loadModel(ureq); + fireEvent(ureq, Event.CHANGED_EVENT); + } + cmc.deactivate(); + cleanUp(); + } else if(retrieveConfirmationCtr == source || resetDataCtrl == source || extraTimeCtrl == source) { + if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { + loadModel(ureq); + } + cmc.deactivate(); + cleanUp(); + } + super.event(ureq, source, event); + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(extraTimeButton == source) { + doConfirmExtraTime(ureq); + } else if(exportResultsButton == source) { + doExportResults(ureq); + } else if(statsButton == source) { + doLaunchStatistics(ureq); + } else if(validateButton == source) { + doValidateSignature(ureq); + } else if(correctionButton == source) { + doStartCorrection(ureq); + } else if(pullButton == source) { + doConfirmPull(ureq); + } else if(resetButton == source) { + doConfirmResetData(ureq); + } else { + super.formInnerEvent(ureq, source, event); + } + } + + @Override + protected void cleanUp() { + removeAsListenerAndDispose(retrieveConfirmationCtr); + removeAsListenerAndDispose(validationCtrl); + removeAsListenerAndDispose(extraTimeCtrl); + removeAsListenerAndDispose(resetDataCtrl); + retrieveConfirmationCtr = null; + validationCtrl = null; + extraTimeCtrl = null; + resetDataCtrl = null; + super.cleanUp(); + } + + @Override + protected Controller createCalloutController(UserRequest ureq, Identity assessedIdentity) { + if(isTestQTI21()) { + return new QTI21IdentityListCourseNodeToolsController(ureq, getWindowControl(), + (IQTESTCourseNode)courseNode, assessedIdentity, coachCourseEnv); + } + return new IdentityListCourseNodeToolsController(ureq, getWindowControl(), + (IQTESTCourseNode)courseNode, assessedIdentity, coachCourseEnv); + } + + private List<Identity> getIdentities() { + AssessmentToolOptions asOptions = getOptions(); + List<Identity> identities = asOptions.getIdentities(); + if (group != null) { + identities = groupService.getMembers(group, GroupRoles.participant.toString()); + } else if (identities != null) { + identities = asOptions.getIdentities(); + } else if (asOptions.isAdmin()){ + identities = ScoreAccountingHelper.loadUsers(getCourseEnvironment()); + } + return identities; + } + + private void doExportResults(UserRequest ureq) { + List<Identity> identities = getIdentities(); + if (identities != null && identities.size() > 0) { + MediaResource resource; + CourseEnvironment courseEnv = getCourseEnvironment(); + if(isTestQTI21()) { + resource = new QTI21ResultsExportMediaResource(courseEnv, identities, (IQTESTCourseNode)courseNode, getLocale()); + } else { + resource = new QTI12ResultsExportMediaResource(courseEnv, getLocale(), identities, (IQTESTCourseNode)courseNode); + } + ureq.getDispatchResult().setResultingMediaResource(resource); + } else { + showWarning("error.no.assessed.users"); + } + } + + private void doValidateSignature(UserRequest ureq) { + if(validationCtrl != null) return; + + validationCtrl = new ValidationXmlSignatureController(ureq, getWindowControl()); + listenTo(validationCtrl); + cmc = new CloseableModalController(getWindowControl(), "close", validationCtrl.getInitialComponent(), + true, translate("validate.xml.signature")); + cmc.activate(); + listenTo(cmc); + } + + public void doStartCorrection(UserRequest ureq) { + AssessmentToolOptions asOptions = getOptions(); + correctionCtrl = new IdentitiesAssessmentTestCorrectionController(ureq, getWindowControl(), + getCourseEnvironment(), asOptions, (IQTESTCourseNode)courseNode); + if(correctionCtrl.getNumberOfAssessedIdentities() == 0) { + showWarning("grade.nobody"); + correctionCtrl = null; + } else { + listenTo(correctionCtrl); + cmc = new CloseableModalController(getWindowControl(), "close", correctionCtrl.getInitialComponent(), + true, translate("correction.test.title")); + cmc.activate(); + listenTo(cmc); + } + } + + private void doUpdateCourseNode(AssessmentTestCorrection corrections, List<AssessmentTestSession> testSessionsToComplete) { + Set<AssessmentTestSession> selectedSessions = new HashSet<>(testSessionsToComplete); + for(AssessmentTestSession testSession:corrections.getTestSessions()) { + if(selectedSessions.contains(testSession)) { + UserCourseEnvironment assessedUserCourseEnv = AssessmentHelper + .createAndInitUserCourseEnvironment(testSession.getIdentity(), getCourseEnvironment()); + ScoreEvaluation scoreEval = ((IQTESTCourseNode)courseNode).getUserScoreEvaluation(assessedUserCourseEnv); + + BigDecimal finalScore = testSession.getFinalScore(); + Float score = finalScore == null ? null : finalScore.floatValue(); + ScoreEvaluation manualScoreEval = new ScoreEvaluation(score, scoreEval.getPassed(), + scoreEval.getAssessmentStatus(), scoreEval.getUserVisible(), scoreEval.getFullyAssessed(), + scoreEval.getCurrentRunCompletion(), scoreEval.getCurrentRunStatus(), testSession.getKey()); + ((IQTESTCourseNode)courseNode).updateUserScoreEvaluation(manualScoreEval, assessedUserCourseEnv, getIdentity(), false, Role.coach); + } + } + } + + private void doConfirmPull(UserRequest ureq) { + AssessmentToolOptions asOptions = getOptions(); + RepositoryEntry testEntry = getReferencedRepositoryEntry(); + CourseEnvironment courseEnv = getCourseEnvironment(); + if(ImsQTI21Resource.TYPE_NAME.equals(testEntry.getOlatResource().getResourceableTypeName())) { + retrieveConfirmationCtr = new QTI21RetrieveTestsController(ureq, getWindowControl(), + courseEnv, asOptions, (IQTESTCourseNode)courseNode); + } else { + retrieveConfirmationCtr = new QTI12PullTestsToolController(ureq, getWindowControl(), + courseEnv, asOptions, (IQTESTCourseNode)courseNode); + } + listenTo(retrieveConfirmationCtr); + + String title = translate("tool.pull"); + cmc = new CloseableModalController(getWindowControl(), null, retrieveConfirmationCtr.getInitialComponent(), true, title, true); + listenTo(cmc); + cmc.activate(); + } + + private void doConfirmResetData(UserRequest ureq) { + AssessmentToolOptions asOptions = getOptions(); + CourseEnvironment courseEnv = getCourseEnvironment(); + resetDataCtrl = new QTI21ResetDataController(ureq, getWindowControl(), courseEnv, asOptions, (IQTESTCourseNode)courseNode); + listenTo(resetDataCtrl); + + String title = translate("tool.reset"); + cmc = new CloseableModalController(getWindowControl(), null, resetDataCtrl.getInitialComponent(), true, title, true); + listenTo(cmc); + cmc.activate(); + } + + private void doLaunchStatistics(UserRequest ureq) { + RepositoryEntry testEntry = getReferencedRepositoryEntry(); + if(ImsQTI21Resource.TYPE_NAME.equals(testEntry.getOlatResource().getResourceableTypeName())) { + statisticsCtrl = new QTI21StatisticsToolController(ureq, getWindowControl(), + stackPanel, getCourseEnvironment(), getOptions(), (IQTESTCourseNode)courseNode); + } else { + statisticsCtrl = new QTI12StatisticsToolController(ureq, getWindowControl(), + stackPanel, getCourseEnvironment(), getOptions(), (IQTESTCourseNode)courseNode); + } + listenTo(statisticsCtrl); + stackPanel.pushController(translate("button.stats"), statisticsCtrl); + } + + private void doConfirmExtraTime(UserRequest ureq) { + List<IdentityRef> identities = getSelectedIdentities(); + if(identities == null || identities.isEmpty()) { + showWarning("warning.users.extra.time"); + return; + } + + List<AssessmentTestSession> testSessions = new ArrayList<>(identities.size()); + for(IdentityRef identity:identities) { + List<AssessmentTestSessionStatistics> sessionsStatistics = qtiService + .getAssessmentTestSessionsStatistics(getCourseRepositoryEntry(), courseNode.getIdent(), identity); + if(!sessionsStatistics.isEmpty()) { + if(sessionsStatistics.size() > 1) { + Collections.sort(sessionsStatistics, new AssessmentTestSessionDetailsComparator()); + } + AssessmentTestSession lastSession = sessionsStatistics.get(0).getTestSession(); + if(lastSession != null && lastSession.getFinishTime() == null) { + testSessions.add(lastSession); + } + } + } + + if(testSessions == null || testSessions.isEmpty()) { + showWarning("warning.users.extra.time"); + return; + } + + extraTimeCtrl = new ConfirmExtraTimeController(ureq, getWindowControl(), getCourseRepositoryEntry(), testSessions); + listenTo(extraTimeCtrl); + + String title = translate("extra.time"); + cmc = new CloseableModalController(getWindowControl(), null, extraTimeCtrl.getInitialComponent(), true, title, true); + listenTo(cmc); + cmc.activate(); + } + + /** + * Sort by identity + * Initial date: 20 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ + private static final class AssessmentTestSessionComparator implements Comparator<AssessmentTestSession> { + + @Override + public int compare(AssessmentTestSession session1, AssessmentTestSession session2) { + Long id1 = session1.getIdentity().getKey(); + Long id2 = session2.getIdentity().getKey(); + + int c = id1.compareTo(id2); + if(c == 0) { + Date start1 = session1.getCreationDate(); + Date start2 = session2.getCreationDate(); + c = start2.compareTo(start1); + } + return c; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/iq/QTI21ExtraTimeController.java b/src/main/java/org/olat/course/nodes/iq/QTI21ExtraTimeController.java deleted file mode 100644 index 3e273ad0f6c..00000000000 --- a/src/main/java/org/olat/course/nodes/iq/QTI21ExtraTimeController.java +++ /dev/null @@ -1,146 +0,0 @@ -/** - * <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.course.nodes.iq; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.olat.basesecurity.IdentityRef; -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; -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.gui.control.controller.BasicController; -import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; -import org.olat.course.assessment.ui.tool.IdentityListCourseNodeProvider; -import org.olat.course.nodes.IQTESTCourseNode; -import org.olat.course.nodes.iq.QTI21IdentityListCourseNodeToolsController.AssessmentTestSessionDetailsComparator; -import org.olat.course.run.environment.CourseEnvironment; -import org.olat.ims.qti21.AssessmentTestSession; -import org.olat.ims.qti21.QTI21Service; -import org.olat.ims.qti21.model.jpa.AssessmentTestSessionStatistics; -import org.olat.repository.RepositoryEntry; -import org.springframework.beans.factory.annotation.Autowired; - -/** - * - * Button for to extend time in a test. - * - * Initial date: 4 déc. 2017<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class QTI21ExtraTimeController extends BasicController { - - private final Link extraTimeLink; - - private final IQTESTCourseNode courseNode; - private final CourseEnvironment courseEnv; - private final IdentityListCourseNodeProvider provider; - - private CloseableModalController cmc; - private ConfirmExtraTimeController extraTimeCtrl; - - @Autowired - private QTI21Service qtiService; - - public QTI21ExtraTimeController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, - IQTESTCourseNode courseNode, IdentityListCourseNodeProvider provider) { - super(ureq, wControl); - this.provider = provider; - this.courseEnv = courseEnv; - this.courseNode = courseNode; - - extraTimeLink = LinkFactory.createButton("extra.time", null, this); - extraTimeLink.setIconLeftCSS("o_icon o_icon_extra_time"); - extraTimeLink.setTranslator(getTranslator()); - putInitialPanel(extraTimeLink); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout - } - - @Override - protected void doDispose() { - // - } - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - if(extraTimeLink == source) { - doConfirmExtraTime(ureq); - } - } - - @Override - protected void event(UserRequest ureq, Controller source, Event event) { - if(extraTimeCtrl == source) { - cmc.deactivate(); - cleanUp(); - } else if(cmc == source) { - cleanUp(); - } - } - - private void cleanUp() { - removeAsListenerAndDispose(extraTimeCtrl); - removeAsListenerAndDispose(cmc); - extraTimeCtrl = null; - cmc = null; - } - - private void doConfirmExtraTime(UserRequest ureq) { - List<IdentityRef> identities = provider.getSelectedIdentities(); - if(identities == null || identities.isEmpty()) { - showWarning("warning.users.extra.time"); - return; - } - - RepositoryEntry courseEntry = courseEnv.getCourseGroupManager().getCourseEntry(); - List<AssessmentTestSession> testSessions = new ArrayList<>(identities.size()); - for(IdentityRef identity:identities) { - List<AssessmentTestSessionStatistics> sessionsStatistics = qtiService.getAssessmentTestSessionsStatistics(courseEntry, courseNode.getIdent(), identity); - if(!sessionsStatistics.isEmpty()) { - if(sessionsStatistics.size() > 1) { - Collections.sort(sessionsStatistics, new AssessmentTestSessionDetailsComparator()); - } - AssessmentTestSession lastSession = sessionsStatistics.get(0).getTestSession(); - if(lastSession != null && lastSession.getFinishTime() == null) { - testSessions.add(lastSession); - } - } - } - - if(testSessions == null || testSessions.isEmpty()) { - showWarning("warning.users.extra.time"); - return; - } - - extraTimeCtrl = new ConfirmExtraTimeController(ureq, getWindowControl(), courseEntry, testSessions); - listenTo(extraTimeCtrl); - - String title = translate("extra.time"); - cmc = new CloseableModalController(getWindowControl(), null, extraTimeCtrl.getInitialComponent(), true, title, true); - listenTo(cmc); - cmc.activate(); - } -} diff --git a/src/main/java/org/olat/course/nodes/iq/QTI21IdentityListCourseNodeToolsController.java b/src/main/java/org/olat/course/nodes/iq/QTI21IdentityListCourseNodeToolsController.java index 1522b2a748c..d668ba8cb77 100644 --- a/src/main/java/org/olat/course/nodes/iq/QTI21IdentityListCourseNodeToolsController.java +++ b/src/main/java/org/olat/course/nodes/iq/QTI21IdentityListCourseNodeToolsController.java @@ -30,27 +30,20 @@ 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.gui.control.generic.closablewrapper.CloseableModalController; -import org.olat.core.gui.control.generic.modal.DialogBoxController; -import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.id.Identity; import org.olat.core.util.Util; -import org.olat.course.CourseFactory; import org.olat.course.assessment.ui.tool.tools.AbstractToolsController; import org.olat.course.nodes.IQTESTCourseNode; -import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.ims.qti21.AssessmentTestSession; -import org.olat.ims.qti21.QTI21DeliveryOptions; import org.olat.ims.qti21.QTI21Service; -import org.olat.ims.qti21.model.DigitalSignatureOptions; import org.olat.ims.qti21.model.jpa.AssessmentTestSessionStatistics; import org.olat.ims.qti21.ui.AssessmentTestDisplayController; import org.olat.ims.qti21.ui.AssessmentTestSessionComparator; +import org.olat.ims.qti21.ui.QTI21ResetDataController; +import org.olat.ims.qti21.ui.QTI21RetrieveTestsController; import org.olat.ims.qti21.ui.assessment.IdentityAssessmentTestCorrectionController; -import org.olat.modules.ModuleConfiguration; -import org.olat.modules.assessment.Role; import org.olat.repository.RepositoryEntry; -import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; @@ -66,9 +59,9 @@ public class QTI21IdentityListCourseNodeToolsController extends AbstractToolsCon private CloseableModalController cmc; private ConfirmReopenController reopenCtrl; - private ConfirmResetController confirmResetCtrl; + private QTI21ResetDataController resetDataCtrl; private ConfirmExtraTimeController extraTimeCtrl; - private DialogBoxController retrieveConfirmationCtr; + private QTI21RetrieveTestsController retrieveConfirmationCtr; private IdentityAssessmentTestCorrectionController correctionCtrl; private RepositoryEntry testEntry; @@ -80,8 +73,6 @@ public class QTI21IdentityListCourseNodeToolsController extends AbstractToolsCon @Autowired private QTI21Service qtiService; - @Autowired - private UserManager userManager; public QTI21IdentityListCourseNodeToolsController(UserRequest ureq, WindowControl wControl, IQTESTCourseNode courseNode, Identity assessedIdentity, UserCourseEnvironment coachCourseEnv) { @@ -170,19 +161,20 @@ public class QTI21IdentityListCourseNodeToolsController extends AbstractToolsCon @Override protected void event(UserRequest ureq, Controller source, Event event) { if(retrieveConfirmationCtr == source) { - if(DialogBoxUIFactory.isYesEvent(event) || DialogBoxUIFactory.isOkEvent(event)) { - doPullSession(lastSession); + if(event == Event.DONE_EVENT) { fireEvent(ureq, Event.CHANGED_EVENT); } else { fireEvent(ureq, Event.DONE_EVENT); } + cmc.deactivate(); + cleanUp(); } else if(correctionCtrl == source) { if(event == Event.DONE_EVENT || event == Event.CANCELLED_EVENT) { cmc.deactivate(); cleanUp(); fireEvent(ureq, Event.CHANGED_EVENT); } - } else if(confirmResetCtrl == source || extraTimeCtrl == source || reopenCtrl == source) { + } else if(resetDataCtrl == source || extraTimeCtrl == source || reopenCtrl == source) { cmc.deactivate(); cleanUp(); fireAlteredEvent(ureq, event); @@ -204,13 +196,13 @@ public class QTI21IdentityListCourseNodeToolsController extends AbstractToolsCon } private void cleanUp() { - removeAsListenerAndDispose(confirmResetCtrl); removeAsListenerAndDispose(correctionCtrl); removeAsListenerAndDispose(extraTimeCtrl); + removeAsListenerAndDispose(resetDataCtrl); removeAsListenerAndDispose(cmc); - confirmResetCtrl = null; correctionCtrl = null; extraTimeCtrl = null; + resetDataCtrl = null; cmc = null; } @@ -225,45 +217,21 @@ public class QTI21IdentityListCourseNodeToolsController extends AbstractToolsCon } private void doConfirmPullSession(UserRequest ureq, AssessmentTestSession session) { - String title = translate("tool.pull"); - String fullname = userManager.getUserDisplayName(session.getIdentity()); - String text = translate("retrievetest.confirm.text", new String[]{ fullname }); - retrieveConfirmationCtr = activateOkCancelDialog(ureq, title, text, retrieveConfirmationCtr); - retrieveConfirmationCtr.setUserObject(session); - } - - private void doPullSession(AssessmentTestSession session) { - qtiService.pullSession(session, getSignatureOptions(session), getIdentity()); - testCourseNode.pullAssessmentTestSession(session, assessedUserCourseEnv, getIdentity(), Role.coach); - } - - private DigitalSignatureOptions getSignatureOptions(AssessmentTestSession session) { - RepositoryEntry sessionTestEntry = session.getTestEntry(); - QTI21DeliveryOptions deliveryOptions = qtiService.getDeliveryOptions(sessionTestEntry); + retrieveConfirmationCtr = new QTI21RetrieveTestsController(ureq, getWindowControl(), session, (IQTESTCourseNode)courseNode); + listenTo(retrieveConfirmationCtr); - boolean digitalSignature = deliveryOptions.isDigitalSignature(); - boolean sendMail = deliveryOptions.isDigitalSignatureMail(); - - ModuleConfiguration config = courseNode.getModuleConfiguration(); - digitalSignature = config.getBooleanSafe(IQEditController.CONFIG_DIGITAL_SIGNATURE, - deliveryOptions.isDigitalSignature()); - sendMail = config.getBooleanSafe(IQEditController.CONFIG_DIGITAL_SIGNATURE_SEND_MAIL, - deliveryOptions.isDigitalSignatureMail()); - - DigitalSignatureOptions options = new DigitalSignatureOptions(digitalSignature, sendMail, courseEntry, testEntry); - if(digitalSignature) { - CourseEnvironment courseEnv = CourseFactory.loadCourse(courseEntry).getCourseEnvironment(); - QTI21AssessmentRunController.decorateCourseConfirmation(session, options, courseEnv, courseNode, sessionTestEntry, null, getLocale()); - } - return options; + String title = translate("tool.pull"); + cmc = new CloseableModalController(getWindowControl(), null, retrieveConfirmationCtr.getInitialComponent(), true, title, true); + listenTo(cmc); + cmc.activate(); } private void doConfirmDeleteData(UserRequest ureq) { - confirmResetCtrl = new ConfirmResetController(ureq, getWindowControl(), courseEntry, courseNode, testEntry, assessedIdentity); - listenTo(confirmResetCtrl); + resetDataCtrl = new QTI21ResetDataController(ureq, getWindowControl(), courseEntry, (IQTESTCourseNode)courseNode, assessedIdentity); + listenTo(resetDataCtrl); String title = translate("reset.test.data.title"); - cmc = new CloseableModalController(getWindowControl(), null, confirmResetCtrl.getInitialComponent(), true, title, true); + cmc = new CloseableModalController(getWindowControl(), null, resetDataCtrl.getInitialComponent(), true, title, true); listenTo(cmc); cmc.activate(); } diff --git a/src/main/java/org/olat/course/nodes/iq/_content/identity_courseelement.html b/src/main/java/org/olat/course/nodes/iq/_content/identity_courseelement.html new file mode 100644 index 00000000000..e9df974738d --- /dev/null +++ b/src/main/java/org/olat/course/nodes/iq/_content/identity_courseelement.html @@ -0,0 +1,31 @@ +<h2><i class="o_icon $courseNodeCssClass"> </i> $r.escapeHtml($courseNodeTitle)#if($r.isNotEmpty($businessGroupName)) <small><i class="o_icon o_icon_group"> </i> $r.escapeHtml($businessGroupName)</small>#end</h2> +<div class="o_button_group o_button_group_right"> + $r.render("button.stats") + #if($r.available("button.export")) + $r.render("button.export") + #end + #if($r.available("retrieve.tests.title")) + $r.render("retrieve.tests.title") + #end + #if($r.available("correction.test.title")) + $r.render("correction.test.title") + #end + #if($r.available("validate.xml.signature")) + $r.render("validate.xml.signature") + #end + #if($r.available("tool.delete.data")) + $r.render("tool.delete.data") + #end +</div> +$r.render("table") +<div class="o_button_group"> + #if($r.available("bulk.done")) + $r.render("bulk.done") + #end + #if($r.available("bulk.visible")) + $r.render("bulk.visible") + #end + #if($r.available("extra.time")) + $r.render("extra.time") + #end +</div> diff --git a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_de.properties index 97f2f927c91..1f57d8e15e0 100644 --- a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_de.properties @@ -5,6 +5,8 @@ Intro.surv=Dr\u00FCcken Sie Start, um mit dem Fragebogen zu beginnen. Intro.test=Dr\u00FCcken Sie Start, um den Test zu beginnen. attempts.nomoreattempts=Es stehen Ihnen keine weiteren Versuche zur Verf\u00FCgung. attempts.yourattempts=Anzahl gemachter Versuche +button.export=Resultate exportieren +button.stats=$org.olat.ims.qti.statistics.ui\:menu.title changelog.title=\u00C4nderungsverlauf der Ressource choosenfile.self=Selbsttest choosenfile.surv=Fragebogen @@ -30,6 +32,7 @@ correction.auto=Auto correction.manual=Manuell correction.mode=Korrektur correction.mode.help=Bei einer Auto Korrektur wird das Resultat sofort angezeigt. Bei der Manuell Korrektur muss die Sichtbarkeit im Bewertungswerkzeug ge\u00E4ndert werden. F\u00FCr die Fragetypen Freitext, Zeichnen und Datei hochladen ist Manuell Korrektur zwingend. +correction.test.title=Korrigieren correcttest=Test korrigieren coursefolder=Ablageordner Kurs "{0}" digital.signature=Testquittung erstellen @@ -151,6 +154,8 @@ replace.wizard.title.step1=Neue Lernressource ausw\u00E4hlen replace.wizard.title.step2=Informationen zu den Resultaten replace.wizard.title.step3=Benutzer informieren reporter.unavailable=$de.bps.onyx.plugin.course.nodes.iq\:reporter.unavailable +reset.test.data.title=Daten von Test zur\u00FCcksetzen +retrieve.tests.title=$org.olat.ims.qti.statistics.ui\:menu.pull.tests.title retrievetest.confirm.text=$org.olat.ims.qti\:retrievetest.confirm.text score.cut=Notwendige Punktzahl f\u00FCr "Bestanden" score.max=Punktemaximum @@ -166,6 +171,8 @@ showResults.visibility.future=Die Resultate werden angezeigt sobald die Korrektu start=Start table.header.lastModified=Last modified table.header.results=Resultaten +table.header.extra.time=Zusatz +table.header.end.date=Enddatum tool.delete.data=Alle Daten zur\u00FCcksetzen tool.extra.time=Testzeit verl\u00E4ngern tool.pull=Laufender test einziehen diff --git a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_en.properties index 05b84e96240..5875bc220cb 100644 --- a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_en.properties @@ -5,6 +5,7 @@ Intro.test=Press the start button to begin with your test. assessment.documents.title=Assessment documents attempts.nomoreattempts=There are no more attempts at your disposal. attempts.yourattempts=Number of attempts +button.stats=$org.olat.ims.qti.statistics.ui\:menu.title changelog.title=Resource change log choosenfile.self=Self-test choosenfile.surv=Questionnaire @@ -30,6 +31,7 @@ correction.auto=Auto correction.manual=Manual correction.mode=Correction correction.mode.help=For Auto correction the result is shown immediately. For Manual correction the visibility need to be changed in the assessment tool. For the question types essay, drawing and file upload a manual correction is mandatory. +correction.test.title=Grade correcttest=Correct test coursefolder=Storage folder of course "{0}" digital.signature=Test receipt @@ -151,6 +153,8 @@ replace.wizard.title.step1=Select new learning resource replace.wizard.title.step2=Information on results replace.wizard.title.step3=Notify users reporter.unavailable=$de.bps.onyx.plugin.course.nodes.iq\:reporter.unavailable +reset.test.data.title=Reset data of test +retrieve.tests.title=$org.olat.ims.qti.statistics.ui\:menu.pull.tests.title retrievetest.confirm.text=$org.olat.ims.qti\:retrievetest.confirm.text score.cut=Passed cut value score.max=Maximum score @@ -164,6 +168,8 @@ showResults.title=Results showResults.visibility=Your results will be displayed from "{0}" until "{1}" showResults.visibility.future=Your results will be displayed here as soon as the correction has been completed. start=Start +table.header.end.date=End date +table.header.extra.time=Extra table.header.lastModified=Last modified table.header.results=Results time.limit.max=Time limit diff --git a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_fr.properties index 2fbccdc6e2b..7919af21e0b 100644 --- a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_fr.properties @@ -30,7 +30,8 @@ correction.auto=Automatique correction.manual=Manuelle correction.mode=Correction correction.mode.help=Pour la correction automatique, le r\u00E9sultat s'affiche imm\u00E9diatement. Pour la correction manuelle, la visibilit\u00E9 doit \u00EAtre modifi\u00E9e dans l'outil d'\u00E9valuation. Pour les types de questions texte libre, dessin et t\u00E9l\u00E9chargement de fichier, une correction manuelle est obligatoire. -correcttest=Corriger le Test +correction.test.title=Corriger +correcttest=Corriger le test coursefolder=Dossier stockage cours "{0}" digital.signature=Signature digitale digital.signature.download=Signature digitale diff --git a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_it.properties b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_it.properties index 0aea979b95e..f2b8316a925 100644 --- a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_it.properties +++ b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_it.properties @@ -25,6 +25,7 @@ condition.accessibility.title=Accesso correction.auto=Automatica correction.manual=Manuale correction.mode=Correzione +correction.test.title=Valutazione correcttest=Correggi test coursefolder=Cartella di deposito del corso "{0}" digital.signature=Ricevuta test diff --git a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_pt_BR.properties b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_pt_BR.properties index 72ef7f15c3a..9cc65d05854 100644 --- a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_pt_BR.properties +++ b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_pt_BR.properties @@ -30,6 +30,7 @@ correction.auto=Auto correction.manual=Manual correction.mode=Corre\u00E7\u00E3o correction.mode.help=Para a corre\u00E7\u00E3o autom\u00E1tica, o resultado \u00E9 mostrado imediatamente. Para corre\u00E7\u00E3o manual, a visibilidade precisa ser alterada na ferramenta de avalia\u00E7\u00E3o. Para os tipos de perguntas ensaio, desenho e carregamento de arquivos, \u00E9 obrigat\u00F3ria uma corre\u00E7\u00E3o manual. +correction.test.title=Grau correcttest=Teste correto coursefolder=Pasta de armazenamento do curso "{0}" digital.signature=Recibo de teste diff --git a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_zh_CN.properties b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_zh_CN.properties index 0e1dc78d509..9a9788fe8f8 100644 --- a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_zh_CN.properties +++ b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_zh_CN.properties @@ -5,6 +5,7 @@ Intro.test=\u70B9\u51FB"\u5F00\u59CB"\u6309\u94AE\u53C2\u4E0E\u6D4B\u8BD5 attempts.nomoreattempts=\u4F60\u4E0D\u80FD\u518D\u6B21\u542F\u52A8\u8BE5\u6D4B\u8BD5 attempts.yourattempts=\u5C1D\u8BD5\u6B21\u6570 changelog.title=\u8D44\u6E90\u53D8\u66F4\u65E5\u5FD7 +correction.test.title=\u8BC4\u5206 diff --git a/src/main/java/org/olat/course/nodes/ms/MSIdentityListCourseNodeController.java b/src/main/java/org/olat/course/nodes/ms/MSIdentityListCourseNodeController.java new file mode 100644 index 00000000000..6dbd106eb9b --- /dev/null +++ b/src/main/java/org/olat/course/nodes/ms/MSIdentityListCourseNodeController.java @@ -0,0 +1,59 @@ +/** + * <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.course.nodes.ms; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.components.stack.TooledStackedPanel; +import org.olat.core.gui.control.WindowControl; +import org.olat.course.assessment.bulk.BulkAssessmentToolController; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeController; +import org.olat.course.nodes.MSCourseNode; +import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroup; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; +import org.olat.repository.RepositoryEntry; + +/** + * + * Initial date: 18 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class MSIdentityListCourseNodeController extends IdentityListCourseNodeController { + + public MSIdentityListCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, MSCourseNode courseNode, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + super(ureq, wControl, stackPanel, courseEntry, group, courseNode, coachCourseEnv, toolContainer, assessmentCallback); + } + + @Override + protected void initMultiSelectionTools(UserRequest ureq, FormLayoutContainer formLayout) { + if(!coachCourseEnv.isCourseReadOnly()) { + BulkAssessmentToolController bulkAssessmentTollCtrl = new BulkAssessmentToolController(ureq, getWindowControl(), + coachCourseEnv.getCourseEnvironment(), (MSCourseNode)courseNode); + listenTo(bulkAssessmentTollCtrl); + formLayout.put("bulk.assessment", bulkAssessmentTollCtrl.getInitialComponent()); + } + super.initMultiSelectionTools(ureq, formLayout); + } +} diff --git a/src/main/java/org/olat/course/nodes/ms/_content/identity_courseelement.html b/src/main/java/org/olat/course/nodes/ms/_content/identity_courseelement.html new file mode 100644 index 00000000000..4af4ef27719 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/ms/_content/identity_courseelement.html @@ -0,0 +1,15 @@ +<h2><i class="o_icon o_st_icon"> </i> $r.escapeHtml($courseNodeTitle)#if($r.isNotEmpty($businessGroupName)) <small><i class="o_icon o_icon_group"> </i> $r.escapeHtml($businessGroupName)</small>#end</h2> +#if($r.available("bulk.assessment")) +<div class="o_button_group o_button_group_right"> + $r.render("bulk.assessment") +</div> +#end +$r.render("table") +<div class="o_button_group"> + #if($r.available("bulk.done")) + $r.render("bulk.done") + #end + #if($r.available("bulk.visible")) + $r.render("bulk.visible") + #end +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerIdentityListCourseNodeController.java b/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerIdentityListCourseNodeController.java new file mode 100644 index 00000000000..747bd1112c2 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerIdentityListCourseNodeController.java @@ -0,0 +1,65 @@ +/** + * <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.course.nodes.projectbroker; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.components.stack.TooledStackedPanel; +import org.olat.core.gui.control.WindowControl; +import org.olat.course.assessment.bulk.BulkAssessmentToolController; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeController; +import org.olat.course.nodes.ProjectBrokerCourseNode; +import org.olat.course.nodes.TACourseNode; +import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroup; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; +import org.olat.repository.RepositoryEntry; + +/** + * + * This view is not used. The project broker is excluded from + * the assessment tool. + * + * Initial date: 18 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class ProjectBrokerIdentityListCourseNodeController extends IdentityListCourseNodeController { + + public ProjectBrokerIdentityListCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, ProjectBrokerCourseNode courseNode, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + super(ureq, wControl, stackPanel, courseEntry, group, courseNode, coachCourseEnv, toolContainer, assessmentCallback); + } + + @Override + protected void initMultiSelectionTools(UserRequest ureq, FormLayoutContainer formLayout) { + if(!coachCourseEnv.isCourseReadOnly()) { + BulkAssessmentToolController bulkAssessmentTollCtrl = new BulkAssessmentToolController(ureq, getWindowControl(), + getCourseEnvironment(), (TACourseNode)courseNode); + listenTo(bulkAssessmentTollCtrl); + formLayout.put("bulk.assessment", bulkAssessmentTollCtrl.getInitialComponent()); + } + //no other batch functions + tableEl.setMultiSelect(false); + tableEl.setSelectAllEnable(false); + } +} diff --git a/src/main/java/org/olat/course/nodes/projectbroker/_content/identity_courseelement.html b/src/main/java/org/olat/course/nodes/projectbroker/_content/identity_courseelement.html new file mode 100644 index 00000000000..8bac11618b2 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/projectbroker/_content/identity_courseelement.html @@ -0,0 +1,8 @@ +<h2><i class="o_icon o_st_icon"> </i> $r.escapeHtml($courseNodeTitle)#if($r.isNotEmpty($businessGroupName)) <small><i class="o_icon o_icon_group"> </i> $r.escapeHtml($businessGroupName)</small>#end</h2> +#if($r.available("bulk.assessment")) +<div class="o_button_group o_button_right"> + $r.render("bulk.assessment") +</div> +#end + +$r.render("table") diff --git a/src/main/java/org/olat/course/nodes/st/STIdentityListCourseNodeController.java b/src/main/java/org/olat/course/nodes/st/STIdentityListCourseNodeController.java new file mode 100644 index 00000000000..db6e1182198 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/st/STIdentityListCourseNodeController.java @@ -0,0 +1,134 @@ +/** + * <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.course.nodes.st; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.components.form.flexible.impl.elements.table.DateFlexiCellRenderer; +import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; +import org.olat.core.gui.components.stack.TooledStackedPanel; +import org.olat.core.gui.control.WindowControl; +import org.olat.course.CourseFactory; +import org.olat.course.ICourse; +import org.olat.course.assessment.manager.UserCourseInformationsManager; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeController; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeTableModel.IdentityCourseElementCols; +import org.olat.course.certificate.CertificateLight; +import org.olat.course.certificate.CertificatesManager; +import org.olat.course.certificate.ui.DownloadCertificateCellRenderer; +import org.olat.course.nodes.STCourseNode; +import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroup; +import org.olat.modules.assessment.ui.AssessedIdentityElementRow; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; +import org.olat.repository.RepositoryEntry; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * This specialized list of assessed identities show the certificates + * and recertification date if configured. + * + * Initial date: 18 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class STIdentityListCourseNodeController extends IdentityListCourseNodeController { + + @Autowired + private CertificatesManager certificatesManager; + @Autowired + private UserCourseInformationsManager userInfosMgr; + + public STIdentityListCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, STCourseNode courseNode, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + super(ureq, wControl, stackPanel, courseEntry, group, courseNode, coachCourseEnv, toolContainer, assessmentCallback); + } + + @Override + protected boolean isSelectable() { + return courseNode.getParent() == null; + } + + @Override + protected void initModificationDatesColumns(FlexiTableColumnModel columnsModel) { + if(courseNode.getParent() == null) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.initialLaunchDate, getSelectAction())); + } + super.initModificationDatesColumns(columnsModel); + } + + @Override + protected void initCalloutColumns(FlexiTableColumnModel columnsModel) { + ICourse course = CourseFactory.loadCourse(getCourseRepositoryEntry()); + if(course.getCourseConfig().isCertificateEnabled()) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.certificate, new DownloadCertificateCellRenderer(getLocale()))); + if(course.getCourseConfig().isRecertificationEnabled()) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.recertification, new DateFlexiCellRenderer(getLocale()))); + } + } + //no callout + } + + @Override + protected String getTableId() { + return "st-assessment-tool-identity-list"; + } + + @Override + protected void initMultiSelectionTools(UserRequest ureq, FormLayoutContainer formLayout) { + //no batch tools based on selected identities + tableEl.setMultiSelect(false); + tableEl.setSelectAllEnable(false); + } + + @Override + protected void loadModel(UserRequest ureq) { + Map<Long, Date> initialLaunchDates = userInfosMgr + .getInitialLaunchDates(getCourseRepositoryEntry().getOlatResource()); + super.loadModel(ureq); + + List<AssessedIdentityElementRow> rows = usersTableModel.getObjects(); + for(AssessedIdentityElementRow row:rows) { + Date initialLaunchDate = initialLaunchDates.get(row.getIdentityKey()); + row.setInitialCourseLaunchDate(initialLaunchDate); + } + + AssessmentToolContainer toolContainer = getToolContainer(); + if(toolContainer.getCertificateMap() == null) { + List<CertificateLight> certificates = certificatesManager.getLastCertificates(getCourseRepositoryEntry().getOlatResource()); + ConcurrentMap<Long, CertificateLight> certificateMap = new ConcurrentHashMap<>(); + for(CertificateLight certificate:certificates) { + certificateMap.put(certificate.getIdentityKey(), certificate); + } + toolContainer.setCertificateMap(certificateMap); + } + usersTableModel.setCertificateMap(toolContainer.getCertificateMap()); + } +} diff --git a/src/main/java/org/olat/course/nodes/st/_content/identity_courseelement.html b/src/main/java/org/olat/course/nodes/st/_content/identity_courseelement.html new file mode 100644 index 00000000000..a1b4bcfe449 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/st/_content/identity_courseelement.html @@ -0,0 +1,2 @@ +<h2><i class="o_icon o_st_icon"> </i> $r.escapeHtml($courseNodeTitle)#if($r.isNotEmpty($businessGroupName)) <small><i class="o_icon o_icon_group"> </i> $r.escapeHtml($businessGroupName)</small>#end</h2> +$r.render("table") diff --git a/src/main/java/org/olat/course/nodes/ta/BulkDownloadToolController.java b/src/main/java/org/olat/course/nodes/ta/BulkDownloadToolController.java deleted file mode 100644 index 7a3fa8ba924..00000000000 --- a/src/main/java/org/olat/course/nodes/ta/BulkDownloadToolController.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * <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.course.nodes.ta; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; -import org.olat.core.gui.control.Event; -import org.olat.core.gui.control.WindowControl; -import org.olat.core.gui.control.controller.BasicController; -import org.olat.course.archiver.ArchiveResource; -import org.olat.course.nodes.ArchiveOptions; -import org.olat.course.nodes.TACourseNode; -import org.olat.course.run.environment.CourseEnvironment; -import org.olat.modules.assessment.AssessmentToolOptions; -import org.olat.resource.OLATResource; - -/** - * - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class BulkDownloadToolController extends BasicController { - - private final Link downloadButton; - - private final ArchiveOptions options; - private final OLATResource courseOres; - private final TACourseNode courseNode; - - public BulkDownloadToolController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, - AssessmentToolOptions asOptions, TACourseNode courseNode) { - super(ureq, wControl); - this.options = new ArchiveOptions(); - this.options.setGroup(asOptions.getGroup()); - this.options.setIdentities(asOptions.getIdentities()); - this.courseNode = courseNode; - courseOres = courseEnv.getCourseGroupManager().getCourseResource(); - - downloadButton = LinkFactory.createButton("bulk.download.title", null, this); - downloadButton.setTranslator(getTranslator()); - putInitialPanel(downloadButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout - } - - @Override - protected void doDispose() { - // - } - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - if(downloadButton == source) { - doDownload(ureq); - } - } - - private void doDownload(UserRequest ureq) { - ArchiveResource resource = new ArchiveResource(courseNode, courseOres, options, getLocale()); - ureq.getDispatchResult().setResultingMediaResource(resource); - } -} diff --git a/src/main/java/org/olat/course/nodes/ta/TAIdentityListCourseNodeController.java b/src/main/java/org/olat/course/nodes/ta/TAIdentityListCourseNodeController.java new file mode 100644 index 00000000000..de00d34312f --- /dev/null +++ b/src/main/java/org/olat/course/nodes/ta/TAIdentityListCourseNodeController.java @@ -0,0 +1,95 @@ +/** + * <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.course.nodes.ta; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; +import org.olat.core.gui.components.form.flexible.elements.FormLink; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.stack.TooledStackedPanel; +import org.olat.core.gui.control.WindowControl; +import org.olat.course.archiver.ArchiveResource; +import org.olat.course.assessment.bulk.BulkAssessmentToolController; +import org.olat.course.assessment.ui.tool.IdentityListCourseNodeController; +import org.olat.course.nodes.ArchiveOptions; +import org.olat.course.nodes.TACourseNode; +import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroup; +import org.olat.modules.assessment.AssessmentToolOptions; +import org.olat.modules.assessment.ui.AssessmentToolContainer; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; +import org.olat.repository.RepositoryEntry; +import org.olat.resource.OLATResource; + +/** + * This specialized list of assessed identities has 2 bulk actions, download + * and bulk assessment. + * + * Initial date: 18 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class TAIdentityListCourseNodeController extends IdentityListCourseNodeController { + + private FormLink downloadButton; + + public TAIdentityListCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry courseEntry, BusinessGroup group, TACourseNode courseNode, UserCourseEnvironment coachCourseEnv, + AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { + super(ureq, wControl, stackPanel, courseEntry, group, courseNode, coachCourseEnv, toolContainer, assessmentCallback); + } + + @Override + protected void initMultiSelectionTools(UserRequest ureq, FormLayoutContainer formLayout) { + if(!coachCourseEnv.isCourseReadOnly()) { + BulkAssessmentToolController bulkAssessmentTollCtrl = new BulkAssessmentToolController(ureq, getWindowControl(), + getCourseEnvironment(), (TACourseNode)courseNode); + listenTo(bulkAssessmentTollCtrl); + formLayout.put("bulk.assessment", bulkAssessmentTollCtrl.getInitialComponent()); + } + + downloadButton = uifactory.addFormLink("bulk.download.title", formLayout, Link.BUTTON); + + super.initMultiSelectionTools(ureq, formLayout); + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(downloadButton == source) { + doDownload(ureq); + } else { + super.formInnerEvent(ureq, source, event); + } + } + + private void doDownload(UserRequest ureq) { + OLATResource courseOres = getCourseRepositoryEntry().getOlatResource(); + AssessmentToolOptions asOptions = getOptions(); + + ArchiveOptions options = new ArchiveOptions(); + options.setGroup(asOptions.getGroup()); + options.setIdentities(asOptions.getIdentities()); + + ArchiveResource resource = new ArchiveResource(courseNode, courseOres, options, getLocale()); + ureq.getDispatchResult().setResultingMediaResource(resource); + } +} diff --git a/src/main/java/org/olat/course/nodes/ta/_content/identity_courseelement.html b/src/main/java/org/olat/course/nodes/ta/_content/identity_courseelement.html new file mode 100644 index 00000000000..9faeefebeea --- /dev/null +++ b/src/main/java/org/olat/course/nodes/ta/_content/identity_courseelement.html @@ -0,0 +1,10 @@ +<h2><i class="o_icon o_gta_icon"> </i> $r.escapeHtml($courseNodeTitle)#if($r.isNotEmpty($businessGroupName)) <small><i class="o_icon o_icon_group"> </i> $r.escapeHtml($businessGroupName)</small>#end</h2> +<div class="o_button_group o_button_group_right"> + #if($r.available("bulk.assessment")) + $r.render("bulk.assessment") + #end + #if($r.available("bulk.download.title")) + $r.render("bulk.download.title") + #end +</div> +$r.render("table") diff --git a/src/main/java/org/olat/ims/qti/resultexport/QTI12ExportResultsReportController.java b/src/main/java/org/olat/ims/qti/resultexport/QTI12ExportResultsReportController.java deleted file mode 100644 index fd95f31b42e..00000000000 --- a/src/main/java/org/olat/ims/qti/resultexport/QTI12ExportResultsReportController.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * <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.qti.resultexport; - -import java.util.List; - -import org.olat.basesecurity.GroupRoles; -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; -import org.olat.core.gui.control.Event; -import org.olat.core.gui.control.WindowControl; -import org.olat.core.gui.control.controller.BasicController; -import org.olat.core.gui.media.MediaResource; -import org.olat.core.id.Identity; -import org.olat.course.archiver.ScoreAccountingHelper; -import org.olat.course.nodes.QTICourseNode; -import org.olat.course.run.environment.CourseEnvironment; -import org.olat.group.BusinessGroup; -import org.olat.group.BusinessGroupService; -import org.olat.ims.qti21.QTI21Service; -import org.olat.modules.assessment.AssessmentToolOptions; -import org.springframework.beans.factory.annotation.Autowired; - - -public class QTI12ExportResultsReportController extends BasicController { - - private final Link statsButton; - private AssessmentToolOptions asOptions; - private QTICourseNode courseNode; - private CourseEnvironment courseEnv; - - @Autowired - private BusinessGroupService groupService; - @Autowired - protected QTI21Service qtiService; - - - public QTI12ExportResultsReportController(UserRequest ureq, WindowControl wControl, - CourseEnvironment courseEnv, AssessmentToolOptions asOptions, QTICourseNode courseNode) { - super(ureq, wControl); - this.asOptions = asOptions; - this.courseNode = courseNode; - this.courseEnv = courseEnv; - - statsButton = LinkFactory.createButton("button.export", null, this); - statsButton.setTranslator(getTranslator()); - putInitialPanel(statsButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout - } - - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - // 1) calculate my assessed identities - List<Identity> identities = asOptions.getIdentities(); - BusinessGroup group = asOptions.getGroup(); - if (group != null) { - identities = groupService.getMembers(group, GroupRoles.participant.toString()); - } else if (identities != null) { - identities = asOptions.getIdentities(); - } else if (asOptions.isAdmin()){ - identities = ScoreAccountingHelper.loadUsers(courseEnv); - } - if (identities != null && identities.size() > 0) { - // 2) create export resource - MediaResource resource = new QTI12ResultsExportMediaResource(courseEnv, ureq.getLocale(), identities, courseNode); - // 3) download - ureq.getDispatchResult().setResultingMediaResource(resource); - } else { - showWarning("error.no.assessed.users"); - } - - } - - @Override - protected void doDispose() { - - - } - -} diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12PullTestsToolController.java b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12PullTestsToolController.java index 216f94a737a..6f2f417aacd 100644 --- a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12PullTestsToolController.java +++ b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12PullTestsToolController.java @@ -25,19 +25,13 @@ import java.util.List; import org.dom4j.Document; import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; 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.gui.control.controller.BasicController; -import org.olat.core.gui.control.generic.dtabs.Activateable2; -import org.olat.core.gui.control.generic.modal.DialogBoxController; -import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.id.Identity; -import org.olat.core.id.context.ContextEntry; -import org.olat.core.id.context.StateEntry; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.coordinate.CoordinatorManager; @@ -70,10 +64,8 @@ import org.springframework.beans.factory.annotation.Autowired; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class QTI12PullTestsToolController extends BasicController implements Activateable2 { +public class QTI12PullTestsToolController extends FormBasicController { - private final Link pullButton; - private DialogBoxController retrieveConfirmationCtr; private final IQTESTCourseNode courseNode; private final CourseEnvironment courseEnv; @@ -88,75 +80,16 @@ public class QTI12PullTestsToolController extends BasicController implements Act public QTI12PullTestsToolController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, AssessmentToolOptions asOptions, IQTESTCourseNode courseNode) { - super(ureq, wControl); + super(ureq, wControl, "retrieve_tests"); setTranslator(Util.createPackageTranslator(QTIResultManager.class, getLocale(), getTranslator())); this.courseEnv = courseEnv; this.courseNode = courseNode; this.asOptions = asOptions; - - boolean enabled = false; - for(Identity assessedIdentity:getIdentities()) { - if(courseNode.isQTI12TestRunning(assessedIdentity, courseEnv)) { - enabled = true; - break; - } - } - - pullButton = LinkFactory.createButton("menu.pull.tests.title", null, this); - pullButton.setIconLeftCSS("o_icon o_icon_pull"); - pullButton.setTranslator(getTranslator()); - pullButton.setEnabled(enabled); - putInitialPanel(pullButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout - } - - private List<Identity> getIdentities() { - List<Identity> identities; - if(asOptions.getGroup() == null && asOptions.getIdentities() == null) { - if(courseEnv != null) { - identities = ScoreAccountingHelper.loadUsers(courseEnv); - } else { - identities = Collections.emptyList(); - } - } else if (asOptions.getIdentities() != null) { - identities = asOptions.getIdentities(); - } else { - identities = businessGroupService.getMembers(asOptions.getGroup()); - } - return identities; - } - - @Override - protected void doDispose() { - // - } - - @Override - public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { - // - } - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - if(pullButton == source) { - confirmPull(ureq); - } + initForm(ureq); } @Override - protected void event(UserRequest ureq, Controller source, Event event) { - if(retrieveConfirmationCtr == source) { - if(DialogBoxUIFactory.isYesEvent(event)) { - @SuppressWarnings("unchecked") - List<Identity> assessedIdentities = (List<Identity>)retrieveConfirmationCtr.getUserObject(); - doRetrieveTests(assessedIdentities); - } - removeAsListenerAndDispose(retrieveConfirmationCtr); - retrieveConfirmationCtr = null; - } - } - - private void confirmPull(UserRequest ureq) { + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { int count = 0; StringBuilder fullnames = new StringBuilder(256); List<Identity> assessedIdentities = getIdentities(); @@ -171,19 +104,53 @@ public class QTI12PullTestsToolController extends BasicController implements Act } } + String msg; if(count == 0) { - showInfo("retrievetest.nothing.todo"); + msg = translate("retrievetest.nothing.todo"); } else if(count == 1) { - String title = translate("retrievetest.confirm.title"); - String text = translate("retrievetest.confirm.text", new String[]{ fullnames.toString() }); - retrieveConfirmationCtr = activateYesNoDialog(ureq, title, text, retrieveConfirmationCtr); - retrieveConfirmationCtr.setUserObject(assessedIdentities); + msg = translate("retrievetest.confirm.text", new String[]{ fullnames.toString() }); } else { - String title = translate("retrievetest.confirm.title"); - String text = translate("retrievetest.confirm.text.plural", new String[]{ fullnames.toString() }); - retrieveConfirmationCtr = activateYesNoDialog(ureq, title, text, retrieveConfirmationCtr); - retrieveConfirmationCtr.setUserObject(assessedIdentities); + msg = translate("retrievetest.confirm.text.plural", new String[]{ fullnames.toString() }); + } + if(formLayout instanceof FormLayoutContainer) { + FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; + layoutCont.contextPut("msg", msg); } + + uifactory.addFormCancelButton("cancel", formLayout, ureq, getWindowControl()); + uifactory.addFormSubmitButton("menu.pull.tests.title", formLayout); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formCancelled(UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); + } + + @Override + protected void formOK(UserRequest ureq) { + doRetrieveTests(getIdentities()); + fireEvent(ureq, Event.DONE_EVENT); + } + + private List<Identity> getIdentities() { + List<Identity> identities; + if(asOptions.getGroup() == null && asOptions.getIdentities() == null) { + if(courseEnv != null) { + identities = ScoreAccountingHelper.loadUsers(courseEnv); + } else { + identities = Collections.emptyList(); + } + } else if (asOptions.getIdentities() != null) { + identities = asOptions.getIdentities(); + } else { + identities = businessGroupService.getMembers(asOptions.getGroup()); + } + return identities; } private void doRetrieveTests(List<Identity> assessedIdentities) { diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12StatisticsToolController.java b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12StatisticsToolController.java index b4a8ab9cfe8..08a6b998e35 100644 --- a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12StatisticsToolController.java +++ b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12StatisticsToolController.java @@ -27,8 +27,6 @@ import org.olat.basesecurity.Group; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.panel.Panel; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.components.tree.GenericTreeModel; @@ -44,7 +42,6 @@ import org.olat.core.id.context.ContextEntry; import org.olat.core.id.context.StateEntry; import org.olat.core.util.nodes.INode; import org.olat.core.util.resource.OresHelper; -import org.olat.course.nodes.ArchiveOptions; import org.olat.course.nodes.QTICourseNode; import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.statistic.StatisticResourceNode; @@ -62,14 +59,11 @@ import org.olat.resource.OLATResource; public class QTI12StatisticsToolController extends BasicController implements Activateable2 { private MenuTree courseTree; - private final Link statsButton; private Controller currentCtrl; private final TooledStackedPanel stackPanel; private LayoutMain3ColsController layoutCtr; - private final ArchiveOptions options; private final OLATResource courseRes; - private final QTICourseNode courseNode; private QTIStatisticResourceResult result; private final QTIStatisticSearchParams searchParams; @@ -79,10 +73,6 @@ public class QTI12StatisticsToolController extends BasicController implements Ac AssessmentToolOptions asOptions, QTICourseNode courseNode) { super(ureq, wControl); this.stackPanel = stackPanel; - this.options = new ArchiveOptions(); - this.options.setGroup(asOptions.getGroup()); - this.options.setIdentities(asOptions.getIdentities()); - this.courseNode = courseNode; courseRes = courseEnv.getCourseGroupManager().getCourseResource(); searchParams = new QTIStatisticSearchParams(courseRes.getResourceableId(), courseNode.getIdent()); @@ -94,10 +84,29 @@ public class QTI12StatisticsToolController extends BasicController implements Ac searchParams.setLimitToGroups(asOptions.getGroups()); } - statsButton = LinkFactory.createButton("menu.title", null, this); - statsButton.setTranslator(getTranslator()); - putInitialPanel(statsButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout + RepositoryEntry testEntry = courseNode.getReferencedRepositoryEntry(); + result = new QTIStatisticResourceResult(courseRes, courseNode, testEntry, searchParams); + + GenericTreeModel treeModel = new GenericTreeModel(); + StatisticResourceNode rootTreeNode = new StatisticResourceNode(courseNode, result); + treeModel.setRootNode(rootTreeNode); + + TreeNode subRootNode = result.getSubTreeModel().getRootNode(); + List<INode> subNodes = new ArrayList<>(); + for(int i=0; i<subRootNode.getChildCount(); i++) { + subNodes.add(subRootNode.getChildAt(i)); + } + for(INode subNode:subNodes) { + rootTreeNode.addChild(subNode); + } + + courseTree = new MenuTree("qtiStatisticsTree"); + courseTree.setTreeModel(treeModel); + courseTree.addListener(this); + + layoutCtr = new LayoutMain3ColsController(ureq, wControl, courseTree, new Panel("empty"), null); + putInitialPanel(layoutCtr.getInitialComponent()); + doSelectNode(ureq, courseTree.getTreeModel().getRootNode()); } @Override @@ -123,10 +132,7 @@ public class QTI12StatisticsToolController extends BasicController implements Ac @Override protected void event(UserRequest ureq, Component source, Event event) { - if(statsButton == source) { - doLaunchStatistics(ureq, getWindowControl()); - doSelectNode(ureq, courseTree.getTreeModel().getRootNode()); - } else if(courseTree == source) { + if(courseTree == source) { if(event instanceof TreeEvent) { TreeEvent te = (TreeEvent)event; if(MenuTree.COMMAND_TREENODE_CLICKED.equals(te.getCommand())) { @@ -149,31 +155,4 @@ public class QTI12StatisticsToolController extends BasicController implements Ac layoutCtr.setCol3(new Panel("empty")); } } - - private void doLaunchStatistics(UserRequest ureq, WindowControl wControl) { - if(result == null) { - RepositoryEntry testEntry = courseNode.getReferencedRepositoryEntry(); - result = new QTIStatisticResourceResult(courseRes, courseNode, testEntry, searchParams); - } - - GenericTreeModel treeModel = new GenericTreeModel(); - StatisticResourceNode rootTreeNode = new StatisticResourceNode(courseNode, result); - treeModel.setRootNode(rootTreeNode); - - TreeNode subRootNode = result.getSubTreeModel().getRootNode(); - List<INode> subNodes = new ArrayList<>(); - for(int i=0; i<subRootNode.getChildCount(); i++) { - subNodes.add(subRootNode.getChildAt(i)); - } - for(INode subNode:subNodes) { - rootTreeNode.addChild(subNode); - } - - courseTree = new MenuTree("qtiStatisticsTree"); - courseTree.setTreeModel(treeModel); - courseTree.addListener(this); - - layoutCtr = new LayoutMain3ColsController(ureq, wControl, courseTree, new Panel("empty"), null); - stackPanel.pushController("Stats", layoutCtr); - } } \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/retrieve_tests.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/retrieve_tests.html new file mode 100644 index 00000000000..394bef64351 --- /dev/null +++ b/src/main/java/org/olat/ims/qti/statistics/ui/_content/retrieve_tests.html @@ -0,0 +1,5 @@ +<div>$msg</div> +<div class="o_button_group"> + $r.render("cancel") + $r.render("menu.pull.tests.title") +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/QTI21Service.java b/src/main/java/org/olat/ims/qti21/QTI21Service.java index 19300db2049..356d869fdb1 100644 --- a/src/main/java/org/olat/ims/qti21/QTI21Service.java +++ b/src/main/java/org/olat/ims/qti21/QTI21Service.java @@ -314,7 +314,8 @@ public interface QTI21Service { public AssessmentTestSession getLastAssessmentTestSessions(RepositoryEntryRef courseEntry, String subIdent, RepositoryEntry testEntry, IdentityRef identity); /** - * Retrieve the sessions for a test. + * Retrieve the sessions for a test. It returns only the sessions of authenticated users (fetched). + * The anonymous ones are not included. * * @param courseEntry * @param subIdent 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 9bfbc656bc0..841e6ffbb77 100644 --- a/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java +++ b/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java @@ -315,6 +315,14 @@ public class AssessmentTestSessionDAO { .getResultList(); } + /** + * The assessment test sessions of authenticated users (fetched in the query). + * + * @param courseEntry + * @param courseSubIdent + * @param testEntry + * @return + */ public List<AssessmentTestSession> getTestSessions(RepositoryEntryRef courseEntry, String courseSubIdent, RepositoryEntry testEntry) { StringBuilder sb = new StringBuilder(); sb.append("select session from qtiassessmenttestsession session") diff --git a/src/main/java/org/olat/ims/qti21/resultexport/QTI21ExportResultsReportController.java b/src/main/java/org/olat/ims/qti21/resultexport/QTI21ExportResultsReportController.java deleted file mode 100644 index 4d5d19f164f..00000000000 --- a/src/main/java/org/olat/ims/qti21/resultexport/QTI21ExportResultsReportController.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * <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.resultexport; - -import java.util.List; - -import org.olat.basesecurity.GroupRoles; -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; -import org.olat.core.gui.control.Event; -import org.olat.core.gui.control.WindowControl; -import org.olat.core.gui.control.controller.BasicController; -import org.olat.core.gui.media.MediaResource; -import org.olat.core.id.Identity; -import org.olat.course.archiver.ScoreAccountingHelper; -import org.olat.course.nodes.QTICourseNode; -import org.olat.course.run.environment.CourseEnvironment; -import org.olat.group.BusinessGroup; -import org.olat.group.BusinessGroupService; -import org.olat.modules.assessment.AssessmentToolOptions; -import org.springframework.beans.factory.annotation.Autowired; - - -public class QTI21ExportResultsReportController extends BasicController { - - private final Link statsButton; - private AssessmentToolOptions asOptions; - private QTICourseNode courseNode; - private CourseEnvironment courseEnv; - - @Autowired - private BusinessGroupService groupService; - - public QTI21ExportResultsReportController(UserRequest ureq, WindowControl wControl, - CourseEnvironment courseEnv, AssessmentToolOptions asOptions, QTICourseNode courseNode) { - super(ureq, wControl); - this.asOptions = asOptions; - this.courseNode = courseNode; - this.courseEnv = courseEnv; - - statsButton = LinkFactory.createButton("button.export", null, this); - statsButton.setTranslator(getTranslator()); - statsButton.setIconLeftCSS("o_icon o_icon-fw o_icon_export"); - putInitialPanel(statsButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout - } - - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - // 1) calculate my assessed identities - List<Identity> identities = asOptions.getIdentities(); - BusinessGroup group = asOptions.getGroup(); - if (group != null) { - identities = groupService.getMembers(group, GroupRoles.participant.toString()); - } else if (identities != null) { - identities = asOptions.getIdentities(); - } else if (asOptions.isAdmin()){ - identities = ScoreAccountingHelper.loadUsers(courseEnv); - } - if (identities != null && identities.size() > 0) { - // 2) create export resource - MediaResource resource = new QTI21ResultsExportMediaResource(courseEnv, identities, courseNode, getLocale()); - // 3) download - ureq.getDispatchResult().setResultingMediaResource(resource); - - } else { - showWarning("error.no.assessed.users"); - } - - } - - @Override - protected void doDispose() { - // - } -} diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessableResource.java b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessableResource.java index ce773a7f4e8..5d7ab63656f 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessableResource.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessableResource.java @@ -19,15 +19,12 @@ */ package org.olat.ims.qti21.ui; -import java.util.ArrayList; -import java.util.List; - import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; -import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.modules.assessment.ui.AssessableResource; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.repository.RepositoryEntry; /** @@ -44,15 +41,8 @@ public class QTI21AssessableResource extends AssessableResource { } @Override - public List<Controller> createAssessmentTools(UserRequest ureq, WindowControl wControl, - TooledStackedPanel stackPanel, RepositoryEntry entry, AssessmentToolOptions options) { - - Controller resetToolCtrl = new QTI21ResetToolController(ureq, wControl, entry, options); - List<Controller> toolsCtrl = new ArrayList<>(1); - toolsCtrl.add(resetToolCtrl); - - Controller retrieveToolCtrl = new QTI21RetrieveTestsToolController(ureq, wControl, entry, options); - toolsCtrl.add(retrieveToolCtrl); - return toolsCtrl; + public Controller createIdentityList(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry entry, AssessmentToolSecurityCallback assessmentCallback) { + return new QTI21AssessedIdentityListController(ureq, wControl, stackPanel, entry, this, assessmentCallback); } } diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessedIdentityListController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessedIdentityListController.java new file mode 100644 index 00000000000..60f45310a65 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessedIdentityListController.java @@ -0,0 +1,158 @@ +/** + * <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; + +import java.util.List; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.FlexiTableFilter; +import org.olat.core.gui.components.form.flexible.elements.FormLink; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.stack.TooledStackedPanel; +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.gui.control.generic.closablewrapper.CloseableModalController; +import org.olat.core.id.Identity; +import org.olat.ims.qti21.QTI21Service; +import org.olat.modules.assessment.AssessmentToolOptions; +import org.olat.modules.assessment.ui.AssessableResource; +import org.olat.modules.assessment.ui.AssessedIdentityListController; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; +import org.olat.repository.RepositoryEntry; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 19 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class QTI21AssessedIdentityListController extends AssessedIdentityListController { + + private FormLink resetButton, pullButton; + + private CloseableModalController cmc; + private QTI21ResetDataController resetDataCtrl; + private QTI21RetrieveTestsController pullSessionCtrl; + + @Autowired + private QTI21Service qtiService; + + public QTI21AssessedIdentityListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry testEntry, AssessableResource element, AssessmentToolSecurityCallback assessmentCallback) { + super(ureq, wControl, stackPanel, testEntry, element, assessmentCallback); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + super.initForm(formLayout, listener, ureq); + + resetButton = uifactory.addFormLink("reset.test.data.title", formLayout, Link.BUTTON); + resetButton.setIconLeftCSS("o_icon o_icon_delete_item"); + + pullButton = uifactory.addFormLink("menu.retrieve.tests.title", formLayout, Link.BUTTON); + pullButton.setIconLeftCSS("o_icon o_icon_pull"); + } + + @Override + protected void updateModel(String searchString, List<FlexiTableFilter> filters, List<FlexiTableFilter> extendedFilters) { + super.updateModel(searchString, filters, extendedFilters); + } + + @Override + protected void updateTools(List<Identity> assessedIdentities) { + RepositoryEntry assessedEntry = getRepositoryEntry(); + boolean enabled = false; + if(assessedIdentities != null && !assessedIdentities.isEmpty()) { + enabled = qtiService.isRunningAssessmentTestSession(assessedEntry, null, assessedEntry, assessedIdentities); + } + pullButton.setEnabled(enabled); + } + + @Override + public void event(UserRequest ureq, Controller source, Event event) { + if(resetDataCtrl == source || pullSessionCtrl == source) { + cmc.deactivate(); + cleanUp(); + } else if(cmc == source) { + cleanUp(); + } + super.event(ureq, source, event); + } + + private void cleanUp() { + removeAsListenerAndDispose(resetDataCtrl); + removeAsListenerAndDispose(cmc); + resetDataCtrl = null; + cmc = null; + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(resetButton == source) { + doResetData(ureq); + } else if(pullButton == source) { + doPullSessions(ureq); + } else { + super.formInnerEvent(ureq, source, event); + } + } + + private void doResetData(UserRequest ureq) { + if(resetDataCtrl != null) return; + + /* + if(identities == null || identities.isEmpty()) { + showWarning("warning.reset.test.data.nobody"); + } */ + + AssessmentToolOptions asOptions = getOptions(); + resetDataCtrl = new QTI21ResetDataController(ureq, getWindowControl(), this.getRepositoryEntry(), asOptions); + listenTo(resetDataCtrl); + + String title = translate("reset.test.data.title"); + cmc = new CloseableModalController(getWindowControl(), null, resetDataCtrl.getInitialComponent(), true, title, true); + listenTo(cmc); + cmc.activate(); + } + + private void doPullSessions(UserRequest ureq) { + AssessmentToolOptions asOptions = getOptions(); + pullSessionCtrl = new QTI21RetrieveTestsController(ureq, getWindowControl(), getRepositoryEntry(), asOptions); + listenTo(pullSessionCtrl); + + String title = translate("retrievetest.confirm.title"); + cmc = new CloseableModalController(getWindowControl(), null, pullSessionCtrl.getInitialComponent(), true, title, true); + listenTo(cmc); + cmc.activate(); + } + + private AssessmentToolOptions getOptions() { + AssessmentToolOptions asOptions = new AssessmentToolOptions(); + asOptions.setAdmin(assessmentCallback.isAdmin()); + List<Identity> assessedIdentities = assessmentToolManager.getAssessedIdentities(getIdentity(), getSearchParameters()); + asOptions.setIdentities(assessedIdentities); + return asOptions; + } +} diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java index 249a1e2ea73..09a16dcb690 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java @@ -30,11 +30,11 @@ import java.util.List; import javax.servlet.http.HttpServletResponse; import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; import org.olat.core.gui.components.EscapeMode; import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; +import org.olat.core.gui.components.form.flexible.elements.FormLink; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.FormEvent; import org.olat.core.gui.components.form.flexible.impl.elements.table.BooleanCellRenderer; @@ -44,6 +44,7 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTable import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent; import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlexiCellRenderer; import org.olat.core.gui.components.form.flexible.impl.elements.table.TextFlexiCellRenderer; +import org.olat.core.gui.components.link.Link; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; @@ -99,7 +100,7 @@ import uk.ac.ed.ph.jqtiplus.state.TestSessionState; */ public class QTI21AssessmentDetailsController extends FormBasicController { - private Component resetToolCmp; + private FormLink resetButton; private FlexiTableElement tableEl; private QTI21AssessmentTestSessionTableModel tableModel; @@ -116,7 +117,7 @@ public class QTI21AssessmentDetailsController extends FormBasicController { private CloseableModalController cmc; private AssessmentResultController resultCtrl; - private QTI21ResetToolController resetToolCtrl; + private QTI21ResetDataController resetToolCtrl; private DialogBoxController retrieveConfirmationCtr; private IdentityAssessmentTestCorrectionController correctionCtrl; @@ -216,17 +217,8 @@ public class QTI21AssessmentDetailsController extends FormBasicController { tableEl.setEmtpyTableMessageKey("results.empty"); if(reSecurity.isEntryAdmin() && !readOnly) { - AssessmentToolOptions asOptions = new AssessmentToolOptions(); - asOptions.setAdmin(reSecurity.isEntryAdmin()); - asOptions.setIdentities(Collections.singletonList(assessedIdentity)); - if(courseNode != null) { - resetToolCtrl = new QTI21ResetToolController(ureq, getWindowControl(), - assessedUserCourseEnv.getCourseEnvironment(), asOptions, courseNode); - } else { - resetToolCtrl = new QTI21ResetToolController(ureq, getWindowControl(), entry, asOptions); - } - listenTo(resetToolCtrl); - resetToolCmp = resetToolCtrl.getInitialComponent(); + resetButton = uifactory.addFormLink("menu.reset.title", formLayout, Link.BUTTON); + resetButton.setIconLeftCSS("o_icon o_icon_delete_item"); } } @@ -269,13 +261,9 @@ public class QTI21AssessmentDetailsController extends FormBasicController { tableModel.setObjects(infos); tableEl.reloadData(); tableEl.reset(); - - if(resetToolCmp != null) { - if(sessionsStatistics.size() > 0) { - flc.getFormItemComponent().put("reset.tool", resetToolCmp); - } else { - flc.getFormItemComponent().remove(resetToolCmp); - } + + if(resetButton != null) { + resetButton.setVisible(!sessionsStatistics.isEmpty()); } } @@ -301,26 +289,32 @@ public class QTI21AssessmentDetailsController extends FormBasicController { updateModel(); } } else if(resetToolCtrl == source) { - if(event == Event.DONE_EVENT) { + if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { updateModel(); fireEvent(ureq, Event.CHANGED_EVENT); } + cmc.deactivate(); + cleanUp(); } super.event(ureq, source, event); } private void cleanUp() { removeAsListenerAndDispose(correctionCtrl); + removeAsListenerAndDispose(resetToolCtrl); removeAsListenerAndDispose(resultCtrl); removeAsListenerAndDispose(cmc); correctionCtrl = null; + resetToolCtrl = null; resultCtrl = null; cmc = null; } @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { - if(tableEl == source) { + if(resetButton == source) { + doResetData(ureq); + } else if(tableEl == source) { if(event instanceof SelectionEvent) { SelectionEvent se = (SelectionEvent)event; String cmd = se.getCommand(); @@ -373,6 +367,24 @@ public class QTI21AssessmentDetailsController extends FormBasicController { assessmentService.updateAssessmentEntry(assessmentEntry); } + private void doResetData(UserRequest ureq) { + AssessmentToolOptions asOptions = new AssessmentToolOptions(); + asOptions.setAdmin(reSecurity.isEntryAdmin()); + asOptions.setIdentities(Collections.singletonList(assessedIdentity)); + + if(courseNode != null) { + resetToolCtrl = new QTI21ResetDataController(ureq, getWindowControl(), + assessedUserCourseEnv.getCourseEnvironment(), asOptions, courseNode); + } else { + resetToolCtrl = new QTI21ResetDataController(ureq, getWindowControl(), entry, asOptions); + } + listenTo(resetToolCtrl); + + cmc = new CloseableModalController(getWindowControl(), "close", resetToolCtrl.getInitialComponent(), + true, translate("table.header.results")); + cmc.activate(); + listenTo(cmc); + } private void doConfirmPullSession(UserRequest ureq, AssessmentTestSession session) { String title = translate("pull"); diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21ResetToolController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21ResetDataController.java similarity index 56% rename from src/main/java/org/olat/ims/qti21/ui/QTI21ResetToolController.java rename to src/main/java/org/olat/ims/qti21/ui/QTI21ResetDataController.java index 764f147bb0b..156d5f4c6c1 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21ResetToolController.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21ResetDataController.java @@ -24,6 +24,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.zip.ZipOutputStream; @@ -31,18 +32,13 @@ import java.util.zip.ZipOutputStream; import org.olat.basesecurity.GroupRoles; import org.olat.core.commons.modules.bc.FolderConfig; import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; 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.gui.control.controller.BasicController; -import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.id.Identity; import org.olat.core.id.IdentityEnvironment; import org.olat.core.id.Roles; @@ -76,18 +72,20 @@ import org.springframework.beans.factory.annotation.Autowired; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class QTI21ResetToolController extends BasicController { +public class QTI21ResetDataController extends FormBasicController { private final Roles studentRoles = new Roles(false, false, false, false, false, false, false, false); - private Link resetButton; + + private final String[] onKeys = new String[]{ "on" }; + + private MultipleSelectionElement acknowledgeEl; - private CloseableModalController cmc; - private ConfirmResetController confirmResetCtrl; + private final ArchiveOptions options; + private final List<Identity> identities; private QTICourseNode courseNode; private CourseEnvironment courseEnv; private RepositoryEntry assessedEntry; - private final AssessmentToolOptions asOptions; @Autowired private QTI21Service qtiService; @@ -96,100 +94,107 @@ public class QTI21ResetToolController extends BasicController { @Autowired private RepositoryService repositoryService; - public QTI21ResetToolController(UserRequest ureq, WindowControl wControl, + public QTI21ResetDataController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, AssessmentToolOptions asOptions, QTICourseNode courseNode) { - super(ureq, wControl); + super(ureq, wControl, "confirm_reset_data"); this.courseNode = courseNode; this.courseEnv = courseEnv; - this.asOptions = asOptions; - initButton(); + + options = new ArchiveOptions(); + if(asOptions.getGroup() == null && asOptions.getIdentities() == null) { + identities = ScoreAccountingHelper.loadUsers(courseEnv); + options.setIdentities(identities); + } else if (asOptions.getIdentities() != null) { + identities = asOptions.getIdentities(); + options.setIdentities(identities); + } else { + identities = businessGroupService.getMembers(asOptions.getGroup()); + options.setGroup(asOptions.getGroup()); + } + + initForm(ureq); + } + + public QTI21ResetDataController(UserRequest ureq, WindowControl wControl, RepositoryEntry courseEntry, + IQTESTCourseNode courseNode, Identity assessedIdentity) { + super(ureq, wControl, "confirm_reset_data"); + this.courseNode = courseNode; + courseEnv = CourseFactory.loadCourse(courseEntry).getCourseEnvironment(); + + options = new ArchiveOptions(); + identities = Collections.singletonList(assessedIdentity); + options.setIdentities(identities); + initForm(ureq); } - public QTI21ResetToolController(UserRequest ureq, WindowControl wControl, + public QTI21ResetDataController(UserRequest ureq, WindowControl wControl, RepositoryEntry assessedEntry, AssessmentToolOptions asOptions) { - super(ureq, wControl); + super(ureq, wControl, "confirm_reset_data"); this.assessedEntry = assessedEntry; - this.asOptions = asOptions; - initButton(); + + options = new ArchiveOptions(); + if(asOptions.getGroup() == null && asOptions.getIdentities() == null) { + identities = repositoryService.getMembers(assessedEntry, GroupRoles.participant.name()); + options.setIdentities(identities); + } else if (asOptions.getIdentities() != null) { + identities = asOptions.getIdentities(); + options.setIdentities(identities); + } else { + identities = businessGroupService.getMembers(asOptions.getGroup()); + options.setGroup(asOptions.getGroup()); + } + + initForm(ureq); } - private void initButton() { - resetButton = LinkFactory.createButton("reset.test.data.title", null, this); - resetButton.setIconLeftCSS("o_icon o_icon_delete_item"); - resetButton.setTranslator(getTranslator()); - putInitialPanel(resetButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + if(formLayout instanceof FormLayoutContainer) { + FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; + String[] args = new String[]{ Integer.toString(identities.size()) }; + String msg = translate("reset.test.data.text", args); + layoutCont.contextPut("msg", msg); + } + + FormLayoutContainer confirmCont = FormLayoutContainer.createDefaultFormLayout("confirm", getTranslator()); + formLayout.add("confirm", confirmCont); + confirmCont.setRootForm(mainForm); + + String[] onValues = new String[]{ translate("reset.test.data.acknowledge") }; + acknowledgeEl = uifactory.addCheckboxesHorizontal("acknowledge", "confirmation", confirmCont, onKeys, onValues); + + FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); + buttonsCont.setRootForm(mainForm); + confirmCont.add(buttonsCont); + uifactory.addFormCancelButton("cancel", buttonsCont, ureq, getWindowControl()); + uifactory.addFormSubmitButton("reset.data", buttonsCont); } - + @Override protected void doDispose() { // } - + @Override - protected void event(UserRequest ureq, Component source, Event event) { - if(resetButton == source) { - doConfirmReset(ureq); + protected boolean validateFormLogic(UserRequest ureq) { + boolean allOk = true; + + acknowledgeEl.clearError(); + if(!acknowledgeEl.isAtLeastSelected(1)) { + acknowledgeEl.setErrorKey("form.legende.mandatory", null); + allOk &= false; } + + return allOk & super.validateFormLogic(ureq); } @Override - protected void event(UserRequest ureq, Controller source, Event event) { - if(confirmResetCtrl == source) { - if(event == Event.DONE_EVENT) { - doReset(ureq, confirmResetCtrl.getOptions(), confirmResetCtrl.getIdentities()); - fireEvent(ureq, Event.DONE_EVENT); - } - cmc.deactivate(); - cleanUp(); - } else if (cmc == source) { - cleanUp(); - } - super.event(ureq, source, event); - } - - private void cleanUp() { - removeAsListenerAndDispose(confirmResetCtrl); - removeAsListenerAndDispose(cmc); - confirmResetCtrl = null; - cmc = null; + protected void formCancelled(UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); } - private void doConfirmReset(UserRequest ureq) { - if(confirmResetCtrl != null) return; - - ArchiveOptions options = new ArchiveOptions(); - List<Identity> identities = null; - if(asOptions.getGroup() == null && asOptions.getIdentities() == null) { - if(courseEnv != null) { - identities = ScoreAccountingHelper.loadUsers(courseEnv); - options.setIdentities(identities); - } else { - identities = repositoryService.getMembers(assessedEntry, GroupRoles.participant.name()); - options.setIdentities(identities); - } - } else if (asOptions.getIdentities() != null) { - identities = asOptions.getIdentities(); - options.setIdentities(identities); - } else { - identities = businessGroupService.getMembers(asOptions.getGroup()); - options.setGroup(asOptions.getGroup()); - } - - if(identities == null || identities.isEmpty()) { - showWarning("warning.reset.test.data.nobody"); - } else { - confirmResetCtrl = new ConfirmResetController(ureq, getWindowControl(), options, identities); - listenTo(confirmResetCtrl); - - String title = translate("reset.test.data.title"); - cmc = new CloseableModalController(getWindowControl(), null, confirmResetCtrl.getInitialComponent(), true, title, true); - listenTo(cmc); - cmc.activate(); - } - } - - private void doReset(UserRequest ureq, ArchiveOptions options, List<Identity> identities) { + @Override + protected void formOK(UserRequest ureq) { if(courseNode instanceof IQTESTCourseNode) { IQTESTCourseNode testCourseNode = (IQTESTCourseNode)courseNode; RepositoryEntry testEntry = courseNode.getReferencedRepositoryEntry(); @@ -204,6 +209,7 @@ public class QTI21ResetToolController extends BasicController { IdentityEnvironment ienv = new IdentityEnvironment(identity, studentRoles); UserCourseEnvironment uce = new UserCourseEnvironmentImpl(ienv, courseEnv); testCourseNode.updateUserScoreEvaluation(scoreEval, uce, getIdentity(), false, Role.coach); + testCourseNode.updateCurrentCompletion(uce, getIdentity(), null, AssessmentRunStatus.notStarted, Role.coach); } } else if(assessedEntry != null) { archiveData(assessedEntry); @@ -213,7 +219,7 @@ public class QTI21ResetToolController extends BasicController { fireEvent(ureq, Event.CHANGED_EVENT); } - private void archiveData(ICourse course, ArchiveOptions options) { + private void archiveData(ICourse course, ArchiveOptions archiveOptions) { File exportDirectory = CourseFactory.getOrCreateDataExportDirectory(getIdentity(), course.getCourseTitle()); String archiveName = courseNode.getType() + "_" + StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName()) @@ -223,7 +229,7 @@ public class QTI21ResetToolController extends BasicController { try(FileOutputStream fileStream = new FileOutputStream(exportFile); ZipOutputStream exportStream = new ZipOutputStream(fileStream)) { - courseNode.archiveNodeData(getLocale(), course, options, exportStream, "UTF-8"); + courseNode.archiveNodeData(getLocale(), course, archiveOptions, exportStream, "UTF-8"); } catch (IOException e) { logError("", e); } @@ -249,80 +255,4 @@ public class QTI21ResetToolController extends BasicController { } } - - private class ConfirmResetController extends FormBasicController { - - private final String[] onKeys = new String[]{ "on" }; - - private MultipleSelectionElement acknowledgeEl; - - private final ArchiveOptions options; - private final List<Identity> identities; - - public ConfirmResetController(UserRequest ureq, WindowControl wControl, ArchiveOptions options, List<Identity> identities) { - super(ureq, wControl, "confirm_reset_data"); - this.options = options; - this.identities = identities; - initForm(ureq); - } - - public ArchiveOptions getOptions() { - return options; - } - - public List<Identity> getIdentities() { - return identities; - } - - @Override - protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { - if(formLayout instanceof FormLayoutContainer) { - FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; - String[] args = new String[]{ Integer.toString(identities.size()) }; - String msg = translate("reset.test.data.text", args); - layoutCont.contextPut("msg", msg); - } - - FormLayoutContainer confirmCont = FormLayoutContainer.createDefaultFormLayout("confirm", getTranslator()); - formLayout.add("confirm", confirmCont); - confirmCont.setRootForm(mainForm); - - String[] onValues = new String[]{ translate("reset.test.data.acknowledge") }; - acknowledgeEl = uifactory.addCheckboxesHorizontal("acknowledge", "confirmation", confirmCont, onKeys, onValues); - - FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); - buttonsCont.setRootForm(mainForm); - confirmCont.add(buttonsCont); - uifactory.addFormCancelButton("cancel", buttonsCont, ureq, getWindowControl()); - uifactory.addFormSubmitButton("reset.data", buttonsCont); - } - - @Override - protected void doDispose() { - // - } - - @Override - protected boolean validateFormLogic(UserRequest ureq) { - boolean allOk = true; - - acknowledgeEl.clearError(); - if(!acknowledgeEl.isAtLeastSelected(1)) { - acknowledgeEl.setErrorKey("form.legende.mandatory", null); - allOk &= false; - } - - return allOk & super.validateFormLogic(ureq); - } - - @Override - protected void formOK(UserRequest ureq) { - fireEvent(ureq, Event.DONE_EVENT); - } - - @Override - protected void formCancelled(UserRequest ureq) { - fireEvent(ureq, Event.CANCELLED_EVENT); - } - } } diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsController.java new file mode 100644 index 00000000000..c2128956bdd --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsController.java @@ -0,0 +1,220 @@ +/** + * <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; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.olat.basesecurity.GroupRoles; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +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.Identity; +import org.olat.core.util.StringHelper; +import org.olat.core.util.Util; +import org.olat.course.CourseFactory; +import org.olat.course.archiver.ScoreAccountingHelper; +import org.olat.course.assessment.AssessmentHelper; +import org.olat.course.nodes.IQTESTCourseNode; +import org.olat.course.nodes.iq.IQEditController; +import org.olat.course.nodes.iq.QTI21AssessmentRunController; +import org.olat.course.run.environment.CourseEnvironment; +import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroupService; +import org.olat.ims.qti.QTIResultManager; +import org.olat.ims.qti21.AssessmentTestSession; +import org.olat.ims.qti21.QTI21DeliveryOptions; +import org.olat.ims.qti21.QTI21Service; +import org.olat.ims.qti21.model.DigitalSignatureOptions; +import org.olat.modules.ModuleConfiguration; +import org.olat.modules.assessment.AssessmentToolOptions; +import org.olat.modules.assessment.Role; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryService; +import org.olat.user.UserManager; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 19 déc. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class QTI21RetrieveTestsController extends FormBasicController { + + private RepositoryEntry assessedEntry; + private IQTESTCourseNode courseNode; + + private List<Identity> identities; + private List<AssessmentTestSession> sessions; + + @Autowired + private UserManager userManager; + @Autowired + private QTI21Service qtiService; + @Autowired + private RepositoryService repositoryService; + @Autowired + private BusinessGroupService businessGroupService; + + public QTI21RetrieveTestsController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, + AssessmentToolOptions asOptions, IQTESTCourseNode courseNode) { + super(ureq, wControl, "retrieve_tests"); + setTranslator(Util.createPackageTranslator(QTIResultManager.class, getLocale(), getTranslator())); + + this.courseNode = courseNode; + identities = getIdentities(asOptions, courseEnv); + sessions = qtiService + .getRunningAssessmentTestSession(courseEnv.getCourseGroupManager().getCourseEntry(), courseNode.getIdent(), courseNode.getReferencedRepositoryEntry()); + + initForm(ureq); + } + + public QTI21RetrieveTestsController(UserRequest ureq, WindowControl wControl, + AssessmentTestSession session, IQTESTCourseNode courseNode) { + super(ureq, wControl, "retrieve_tests"); + setTranslator(Util.createPackageTranslator(QTIResultManager.class, getLocale(), getTranslator())); + this.courseNode = courseNode; + identities = Collections.singletonList(session.getIdentity()); + sessions = Collections.singletonList(session); + initForm(ureq); + } + + public QTI21RetrieveTestsController(UserRequest ureq, WindowControl wControl, RepositoryEntry assessedEntry, + AssessmentToolOptions asOptions) { + super(ureq, wControl, "retrieve_tests"); + setTranslator(Util.createPackageTranslator(QTIResultManager.class, getLocale(), getTranslator())); + this.assessedEntry = assessedEntry; + identities = getIdentities(asOptions, null); + sessions = qtiService.getRunningAssessmentTestSession(assessedEntry, null, assessedEntry); + initForm(ureq); + } + + private List<Identity> getIdentities(AssessmentToolOptions asOptions, CourseEnvironment courseEnv) { + List<Identity> identityList; + if(asOptions.getGroup() == null && asOptions.getIdentities() == null) { + if(courseEnv != null) { + identityList = ScoreAccountingHelper.loadUsers(courseEnv); + } else { + identityList = repositoryService.getMembers(assessedEntry, GroupRoles.participant.name()); + } + } else if (asOptions.getIdentities() != null) { + identityList = asOptions.getIdentities(); + } else { + identityList = businessGroupService.getMembers(asOptions.getGroup()); + } + return identityList; + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + StringBuilder fullnames = new StringBuilder(256); + + List<AssessmentTestSession> sessionsToRetrieve = new ArrayList<>(); + Set<Identity> assessedIdentites = new HashSet<>(identities); + for(AssessmentTestSession session:sessions) { + if(assessedIdentites.contains(session.getIdentity())) { + if(fullnames.length() > 0) fullnames.append(", "); + String name = userManager.getUserDisplayName(session.getIdentity()); + if(StringHelper.containsNonWhitespace(name)) { + fullnames.append(name); + sessionsToRetrieve.add(session); + } + } + } + + String msg; + if(sessionsToRetrieve.size() == 0) { + msg = translate("retrievetest.nothing.todo"); + } else if(sessionsToRetrieve.size() == 1) { + msg = translate("retrievetest.confirm.text", new String[]{ fullnames.toString() }); + } else { + msg = translate("retrievetest.confirm.text.plural", new String[]{ fullnames.toString() }); + } + if(formLayout instanceof FormLayoutContainer) { + FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; + layoutCont.contextPut("msg", msg); + } + + uifactory.addFormCancelButton("cancel", formLayout, ureq, getWindowControl()); + uifactory.addFormSubmitButton("menu.retrieve.tests.title", formLayout); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formOK(UserRequest ureq) { + for(AssessmentTestSession session:sessions) { + doRetrieveTest(session); + } + fireEvent(ureq, Event.DONE_EVENT); + } + + @Override + protected void formCancelled(UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); + } + + private void doRetrieveTest(AssessmentTestSession session) { + session = qtiService.getAssessmentTestSession(session.getKey()); + session = qtiService.pullSession(session, getSignatureOptions(session), getIdentity()); + if(courseNode != null) { + RepositoryEntry courseEntry = session.getRepositoryEntry(); + CourseEnvironment courseEnv = CourseFactory.loadCourse(courseEntry).getCourseEnvironment(); + UserCourseEnvironment assessedUserCourseEnv = AssessmentHelper + .createAndInitUserCourseEnvironment(session.getIdentity(), courseEnv); + courseNode.pullAssessmentTestSession(session, assessedUserCourseEnv, getIdentity(), Role.coach); + } + } + + private DigitalSignatureOptions getSignatureOptions(AssessmentTestSession session) { + if(courseNode == null) return null; + + RepositoryEntry testEntry = session.getTestEntry(); + RepositoryEntry courseEntry = session.getRepositoryEntry(); + QTI21DeliveryOptions deliveryOptions = qtiService.getDeliveryOptions(testEntry); + + boolean digitalSignature = deliveryOptions.isDigitalSignature(); + boolean sendMail = deliveryOptions.isDigitalSignatureMail(); + + ModuleConfiguration config = courseNode.getModuleConfiguration(); + digitalSignature = config.getBooleanSafe(IQEditController.CONFIG_DIGITAL_SIGNATURE, + deliveryOptions.isDigitalSignature()); + sendMail = config.getBooleanSafe(IQEditController.CONFIG_DIGITAL_SIGNATURE_SEND_MAIL, + deliveryOptions.isDigitalSignatureMail()); + + DigitalSignatureOptions options = new DigitalSignatureOptions(digitalSignature, sendMail, courseEntry, testEntry); + if(digitalSignature) { + CourseEnvironment courseEnv = CourseFactory.loadCourse(courseEntry).getCourseEnvironment(); + QTI21AssessmentRunController.decorateCourseConfirmation(session, options, courseEnv, courseNode, testEntry, null, getLocale()); + } + return options; + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsToolController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsToolController.java deleted file mode 100644 index 7b0561251c5..00000000000 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21RetrieveTestsToolController.java +++ /dev/null @@ -1,239 +0,0 @@ -/** - * <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; - -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.olat.basesecurity.GroupRoles; -import org.olat.core.commons.persistence.DB; -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; -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.gui.control.controller.BasicController; -import org.olat.core.gui.control.generic.dtabs.Activateable2; -import org.olat.core.gui.control.generic.modal.DialogBoxController; -import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; -import org.olat.core.id.Identity; -import org.olat.core.id.OLATResourceable; -import org.olat.core.id.context.ContextEntry; -import org.olat.core.id.context.StateEntry; -import org.olat.core.util.StringHelper; -import org.olat.core.util.Util; -import org.olat.core.util.coordinate.CoordinatorManager; -import org.olat.core.util.resource.OresHelper; -import org.olat.course.archiver.ScoreAccountingHelper; -import org.olat.course.nodes.IQTESTCourseNode; -import org.olat.course.run.environment.CourseEnvironment; -import org.olat.group.BusinessGroupService; -import org.olat.ims.qti.QTIResultManager; -import org.olat.ims.qti21.AssessmentSessionAuditLogger; -import org.olat.ims.qti21.AssessmentTestSession; -import org.olat.ims.qti21.QTI21Service; -import org.olat.ims.qti21.ui.event.RetrieveAssessmentTestSessionEvent; -import org.olat.modules.assessment.AssessmentToolOptions; -import org.olat.repository.RepositoryEntry; -import org.olat.repository.RepositoryService; -import org.olat.user.UserManager; -import org.springframework.beans.factory.annotation.Autowired; - -/** - * - * Initial date: 07.07.2015<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class QTI21RetrieveTestsToolController extends BasicController implements Activateable2 { - - private Link pullButton; - private DialogBoxController retrieveConfirmationCtr; - - /** Not used, tool not implemented */ - private RepositoryEntry assessedEntry; - private IQTESTCourseNode courseNode; - private CourseEnvironment courseEnv; - private final AssessmentToolOptions asOptions; - - @Autowired - private DB dbInstance; - @Autowired - private UserManager userManager; - @Autowired - private QTI21Service qtiService; - @Autowired - private RepositoryService repositoryService; - @Autowired - private BusinessGroupService businessGroupService; - - public QTI21RetrieveTestsToolController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, - AssessmentToolOptions asOptions, IQTESTCourseNode courseNode) { - super(ureq, wControl); - setTranslator(Util.createPackageTranslator(QTIResultManager.class, getLocale(), getTranslator())); - - this.courseEnv = courseEnv; - this.courseNode = courseNode; - this.asOptions = asOptions; - initButton(); - } - - public QTI21RetrieveTestsToolController(UserRequest ureq, WindowControl wControl, RepositoryEntry assessedEntry, - AssessmentToolOptions asOptions) { - super(ureq, wControl); - setTranslator(Util.createPackageTranslator(QTIResultManager.class, getLocale(), getTranslator())); - this.assessedEntry = assessedEntry; - this.asOptions = asOptions; - initButton(); - } - - private List<Identity> getIdentities() { - List<Identity> identities; - if(asOptions.getGroup() == null && asOptions.getIdentities() == null) { - if(courseEnv != null) { - identities = ScoreAccountingHelper.loadUsers(courseEnv); - } else { - identities = repositoryService.getMembers(assessedEntry, GroupRoles.participant.name()); - } - } else if (asOptions.getIdentities() != null) { - identities = asOptions.getIdentities(); - } else { - identities = businessGroupService.getMembers(asOptions.getGroup()); - } - return identities; - } - - private void initButton() { - boolean enabled; - List<Identity> identities = getIdentities(); - if(identities == null || identities.isEmpty()) { - enabled = false; - } else if(courseEnv != null) { - enabled = qtiService.isRunningAssessmentTestSession(courseEnv.getCourseGroupManager().getCourseEntry(), - courseNode.getIdent(), courseNode.getReferencedRepositoryEntry(), getIdentities()); - } else { - enabled = qtiService.isRunningAssessmentTestSession(assessedEntry, null, assessedEntry, getIdentities()); - } - - pullButton = LinkFactory.createButton("menu.retrieve.tests.title", null, this); - pullButton.setIconLeftCSS("o_icon o_icon_pull"); - pullButton.setTranslator(getTranslator()); - pullButton.setEnabled(enabled); - putInitialPanel(pullButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout - } - - @Override - protected void doDispose() { - // - } - - @Override - public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { - // - } - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - if(pullButton == source) { - confirmPull(ureq); - } - } - - @Override - protected void event(UserRequest ureq, Controller source, Event event) { - if(retrieveConfirmationCtr == source) { - if(DialogBoxUIFactory.isYesEvent(event)) { - @SuppressWarnings("unchecked") - List<AssessmentTestSession> sessionsToRetrieve = (List<AssessmentTestSession>)retrieveConfirmationCtr.getUserObject(); - doRetrieveTests(sessionsToRetrieve); - } - removeAsListenerAndDispose(retrieveConfirmationCtr); - retrieveConfirmationCtr = null; - } - } - - private void confirmPull(UserRequest ureq) { - StringBuilder fullnames = new StringBuilder(256); - - List<AssessmentTestSession> sessions; - if(courseEnv != null) { - sessions = qtiService - .getRunningAssessmentTestSession(courseEnv.getCourseGroupManager().getCourseEntry(), courseNode.getIdent(), courseNode.getReferencedRepositoryEntry()); - } else { - sessions = qtiService.getRunningAssessmentTestSession(assessedEntry, null, assessedEntry); - } - - List<Identity> identities = getIdentities(); - List<AssessmentTestSession> sessionsToRetrieve = new ArrayList<>(); - Set<Identity> assessedIdentites = new HashSet<>(identities); - for(AssessmentTestSession session:sessions) { - if(assessedIdentites.contains(session.getIdentity())) { - if(fullnames.length() > 0) fullnames.append(", "); - String name = userManager.getUserDisplayName(session.getIdentity()); - if(StringHelper.containsNonWhitespace(name)) { - fullnames.append(name); - sessionsToRetrieve.add(session); - } - } - } - - if(sessionsToRetrieve.size() == 0) { - showInfo("retrievetest.nothing.todo"); - } else if(sessionsToRetrieve.size() == 1) { - String title = translate("retrievetest.confirm.title"); - String text = translate("retrievetest.confirm.text", new String[]{ fullnames.toString() }); - retrieveConfirmationCtr = activateYesNoDialog(ureq, title, text, retrieveConfirmationCtr); - retrieveConfirmationCtr.setUserObject(sessionsToRetrieve); - } else { - String title = translate("retrievetest.confirm.title"); - String text = translate("retrievetest.confirm.text.plural", new String[]{ fullnames.toString() }); - retrieveConfirmationCtr = activateYesNoDialog(ureq, title, text, retrieveConfirmationCtr); - retrieveConfirmationCtr.setUserObject(sessionsToRetrieve); - } - } - - private void doRetrieveTests(List<AssessmentTestSession> sessionsToRetrieve) { - for(AssessmentTestSession sessionToRetrieve:sessionsToRetrieve) { - doRetrieveTest(sessionToRetrieve); - } - } - - private void doRetrieveTest(AssessmentTestSession session) { - if(session.getFinishTime() == null) { - session.setFinishTime(new Date()); - } - session.setTerminationTime(new Date()); - session = qtiService.updateAssessmentTestSession(session); - dbInstance.commit();//make sure that the changes committed before sending the event - - AssessmentSessionAuditLogger candidateAuditLogger = qtiService.getAssessmentSessionAuditLogger(session, false); - candidateAuditLogger.logTestRetrieved(session, getIdentity()); - - OLATResourceable sessionOres = OresHelper.createOLATResourceableInstance(AssessmentTestSession.class, session.getKey()); - CoordinatorManager.getInstance().getCoordinator().getEventBus() - .fireEventToListenersOf(new RetrieveAssessmentTestSessionEvent(session.getKey()), sessionOres); - } -} \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/ui/_content/assessment_details.html b/src/main/java/org/olat/ims/qti21/ui/_content/assessment_details.html index 06f44a101c1..268c4971709 100644 --- a/src/main/java/org/olat/ims/qti21/ui/_content/assessment_details.html +++ b/src/main/java/org/olat/ims/qti21/ui/_content/assessment_details.html @@ -1,6 +1,6 @@ $r.render("sessions") -#if($r.available("reset.tool")) +#if($r.available("menu.reset.title")) <div class="o_button_group"> - $r.render("reset.tool") + $r.render("menu.reset.title") </div> #end \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/ui/_content/identity_element.html b/src/main/java/org/olat/ims/qti21/ui/_content/identity_element.html new file mode 100644 index 00000000000..db6110d6743 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/ui/_content/identity_element.html @@ -0,0 +1,8 @@ +<h2><i class="o_icon $cssClass"> </i> $r.escapeHtml($title)</h2> +<div class="o_button_group o_button_group_right"> +#if($r.available("menu.retrieve.tests.title")) + $r.render("menu.retrieve.tests.title") +#end +$r.render("reset.test.data.title") +</div> +$r.render("table") \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/ui/_content/retrieve_tests.html b/src/main/java/org/olat/ims/qti21/ui/_content/retrieve_tests.html new file mode 100644 index 00000000000..7392881f148 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/ui/_content/retrieve_tests.html @@ -0,0 +1,5 @@ +<div>$msg</div> +<div class="o_button_group"> + $r.render("cancel") + $r.render("menu.retrieve.tests.title") +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/QTI21CorrectionToolController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/QTI21CorrectionToolController.java deleted file mode 100644 index d312a73f476..00000000000 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/QTI21CorrectionToolController.java +++ /dev/null @@ -1,141 +0,0 @@ -/** - * <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.assessment; - -import java.math.BigDecimal; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; -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.gui.control.controller.BasicController; -import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; -import org.olat.course.assessment.AssessmentHelper; -import org.olat.course.nodes.IQTESTCourseNode; -import org.olat.course.run.environment.CourseEnvironment; -import org.olat.course.run.scoring.ScoreEvaluation; -import org.olat.course.run.userview.UserCourseEnvironment; -import org.olat.ims.qti21.AssessmentTestSession; -import org.olat.modules.assessment.AssessmentToolOptions; -import org.olat.modules.assessment.Role; -import org.olat.modules.assessment.ui.event.CompleteAssessmentTestSessionEvent; - -/** - * - * Initial date: 08.08.2016<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class QTI21CorrectionToolController extends BasicController { - - private final Link correctionButton; - - private CloseableModalController cmc; - private IdentitiesAssessmentTestCorrectionController correctionCtrl; - - private final IQTESTCourseNode courseNode; - private final CourseEnvironment courseEnv; - private final AssessmentToolOptions asOptions; - - public QTI21CorrectionToolController(UserRequest ureq, WindowControl wControl, - CourseEnvironment courseEnv, AssessmentToolOptions asOptions, IQTESTCourseNode courseNode) { - super(ureq, wControl); - this.courseNode = courseNode; - this.courseEnv = courseEnv; - this.asOptions = asOptions; - - correctionButton = LinkFactory.createButton("correction.test.title", null, this); - correctionButton.setIconLeftCSS("o_icon o_icon-fw o_icon_correction"); - correctionButton.setTranslator(getTranslator()); - putInitialPanel(correctionButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout - } - - @Override - protected void doDispose() { - // - } - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - if(correctionButton == source) { - doStartCorrection(ureq); - } - } - - @Override - protected void event(UserRequest ureq, Controller source, Event event) { - if(correctionCtrl == source) { - if(event instanceof CompleteAssessmentTestSessionEvent) { - CompleteAssessmentTestSessionEvent catse = (CompleteAssessmentTestSessionEvent)event; - List<AssessmentTestSession> testSessionsToComplete = catse.getTestSessions(); - doUpdateCourseNode(correctionCtrl.getTestCorrections(), testSessionsToComplete); - fireEvent(ureq, Event.CHANGED_EVENT); - } - cmc.deactivate(); - cleanUp(); - } - } - - private void cleanUp() { - removeAsListenerAndDispose(correctionCtrl); - removeAsListenerAndDispose(cmc); - correctionCtrl = null; - cmc = null; - } - - public void doStartCorrection(UserRequest ureq) { - correctionCtrl = new IdentitiesAssessmentTestCorrectionController(ureq, getWindowControl(), courseEnv, asOptions, courseNode); - if(correctionCtrl.getNumberOfAssessedIdentities() == 0) { - showWarning("grade.nobody"); - correctionCtrl = null; - } else { - listenTo(correctionCtrl); - cmc = new CloseableModalController(getWindowControl(), "close", correctionCtrl.getInitialComponent(), - true, translate("correction")); - cmc.activate(); - listenTo(cmc); - } - } - - private void doUpdateCourseNode(AssessmentTestCorrection corrections, List<AssessmentTestSession> testSessionsToComplete) { - Set<AssessmentTestSession> selectedSessions = new HashSet<>(testSessionsToComplete); - for(AssessmentTestSession testSession:corrections.getTestSessions()) { - if(selectedSessions.contains(testSession)) { - UserCourseEnvironment assessedUserCourseEnv = AssessmentHelper - .createAndInitUserCourseEnvironment(testSession.getIdentity(), courseEnv); - ScoreEvaluation scoreEval = courseNode.getUserScoreEvaluation(assessedUserCourseEnv); - - BigDecimal finalScore = testSession.getFinalScore(); - Float score = finalScore == null ? null : finalScore.floatValue(); - ScoreEvaluation manualScoreEval = new ScoreEvaluation(score, scoreEval.getPassed(), - scoreEval.getAssessmentStatus(), scoreEval.getUserVisible(), scoreEval.getFullyAssessed(), - scoreEval.getCurrentRunCompletion(), scoreEval.getCurrentRunStatus(), testSession.getKey()); - courseNode.updateUserScoreEvaluation(manualScoreEval, assessedUserCourseEnv, getIdentity(), false, Role.coach); - } - } - } -} diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/QTI21ValidationToolController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/QTI21ValidationToolController.java deleted file mode 100644 index 8c0f5be219f..00000000000 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/QTI21ValidationToolController.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * <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.assessment; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; -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.gui.control.controller.BasicController; -import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; -import org.olat.core.util.Util; -import org.olat.ims.qti21.ui.QTI21RuntimeController; - -/** - * - * Initial date: 28 févr. 2017<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class QTI21ValidationToolController extends BasicController { - - private CloseableModalController cmc; - private ValidationXmlSignatureController validationCtrl; - - private final Link validateButton; - - public QTI21ValidationToolController(UserRequest ureq, WindowControl wControl) { - super(ureq, wControl, Util.createPackageTranslator(QTI21RuntimeController.class, ureq.getLocale())); - - validateButton = LinkFactory.createButton("validate.xml.signature", null, this); - validateButton.setIconLeftCSS("o_icon o_icon-fw o_icon_correction"); - validateButton.setTranslator(getTranslator()); - putInitialPanel(validateButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout - } - - @Override - protected void doDispose() { - // - } - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - if(validateButton == source) { - doValidate(ureq); - } - } - - @Override - protected void event(UserRequest ureq, Controller source, Event event) { - if(validationCtrl == source) { - cmc.deactivate(); - cleanUp(); - } else if(cmc == source) { - cleanUp(); - } - } - - private void cleanUp() { - removeAsListenerAndDispose(validationCtrl); - removeAsListenerAndDispose(cmc); - validationCtrl = null; - cmc = null; - } - - private void doValidate(UserRequest ureq) { - if(validationCtrl != null) return; - - validationCtrl = new ValidationXmlSignatureController(ureq, getWindowControl()); - listenTo(validationCtrl); - cmc = new CloseableModalController(getWindowControl(), "close", validationCtrl.getInitialComponent(), - true, translate("validate.xml.signature")); - cmc.activate(); - listenTo(cmc); - } -} diff --git a/src/main/java/org/olat/ims/qti21/ui/statistics/QTI21StatisticsToolController.java b/src/main/java/org/olat/ims/qti21/ui/statistics/QTI21StatisticsToolController.java index e6e8deedc55..6e353990746 100644 --- a/src/main/java/org/olat/ims/qti21/ui/statistics/QTI21StatisticsToolController.java +++ b/src/main/java/org/olat/ims/qti21/ui/statistics/QTI21StatisticsToolController.java @@ -27,8 +27,6 @@ import org.olat.basesecurity.Group; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.panel.Panel; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.components.tree.GenericTreeModel; @@ -64,13 +62,11 @@ import org.springframework.beans.factory.annotation.Autowired; public class QTI21StatisticsToolController extends BasicController implements Activateable2 { private MenuTree courseTree; - private final Link statsButton; private Controller currentCtrl; private final TooledStackedPanel stackPanel; private LayoutMain3ColsController layoutCtr; private final ArchiveOptions options; - private final QTICourseNode courseNode; private final RepositoryEntry testEntry; private final RepositoryEntry courseEntry; private QTI21StatisticResourceResult result; @@ -96,10 +92,9 @@ public class QTI21StatisticsToolController extends BasicController implements Ac AssessmentToolOptions asOptions, QTICourseNode courseNode) { super(ureq, wControl); this.stackPanel = stackPanel; - this.options = new ArchiveOptions(); - this.options.setGroup(asOptions.getGroup()); - this.options.setIdentities(asOptions.getIdentities()); - this.courseNode = courseNode; + options = new ArchiveOptions(); + options.setGroup(asOptions.getGroup()); + options.setIdentities(asOptions.getIdentities()); courseEntry = courseEnv.getCourseGroupManager().getCourseEntry(); testEntry = courseNode.getReferencedRepositoryEntry(); @@ -119,11 +114,29 @@ public class QTI21StatisticsToolController extends BasicController implements Ac searchParams.setViewNonMembers(asOptions.isNonMembers()); } - statsButton = LinkFactory.createButton("menu.title", null, this); - statsButton.setIconLeftCSS("o_icon o_icon-fw o_icon_statistics_tool"); - statsButton.setTranslator(getTranslator()); - putInitialPanel(statsButton); - getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout + result = new QTI21StatisticResourceResult(testEntry, courseEntry, courseNode, searchParams, secCallback); + result.setWithFilter(false); + + GenericTreeModel treeModel = new GenericTreeModel(); + StatisticResourceNode rootTreeNode = new StatisticResourceNode(courseNode, result); + treeModel.setRootNode(rootTreeNode); + + TreeNode subRootNode = result.getSubTreeModel().getRootNode(); + List<INode> subNodes = new ArrayList<>(); + for(int i=0; i<subRootNode.getChildCount(); i++) { + subNodes.add(subRootNode.getChildAt(i)); + } + for(INode subNode:subNodes) { + rootTreeNode.addChild(subNode); + } + + courseTree = new MenuTree("qti21StatisticsTree"); + courseTree.setTreeModel(treeModel); + courseTree.addListener(this); + + layoutCtr = new LayoutMain3ColsController(ureq, wControl, courseTree, new Panel("empty"), null); + putInitialPanel(layoutCtr.getInitialComponent()); + doSelectNode(ureq, courseTree.getTreeModel().getRootNode()); } @Override @@ -149,10 +162,7 @@ public class QTI21StatisticsToolController extends BasicController implements Ac @Override protected void event(UserRequest ureq, Component source, Event event) { - if(statsButton == source) { - doLaunchStatistics(ureq, getWindowControl()); - doSelectNode(ureq, courseTree.getTreeModel().getRootNode()); - } else if(courseTree == source) { + if(courseTree == source) { if(event instanceof TreeEvent) { TreeEvent te = (TreeEvent)event; if(MenuTree.COMMAND_TREENODE_CLICKED.equals(te.getCommand())) { @@ -175,31 +185,4 @@ public class QTI21StatisticsToolController extends BasicController implements Ac layoutCtr.setCol3(new Panel("empty")); } } - - private void doLaunchStatistics(UserRequest ureq, WindowControl wControl) { - if(result == null) { - result = new QTI21StatisticResourceResult(testEntry, courseEntry, courseNode, searchParams, secCallback); - result.setWithFilter(false); - } - - GenericTreeModel treeModel = new GenericTreeModel(); - StatisticResourceNode rootTreeNode = new StatisticResourceNode(courseNode, result); - treeModel.setRootNode(rootTreeNode); - - TreeNode subRootNode = result.getSubTreeModel().getRootNode(); - List<INode> subNodes = new ArrayList<>(); - for(int i=0; i<subRootNode.getChildCount(); i++) { - subNodes.add(subRootNode.getChildAt(i)); - } - for(INode subNode:subNodes) { - rootTreeNode.addChild(subNode); - } - - courseTree = new MenuTree("qti21StatisticsTree"); - courseTree.setTreeModel(treeModel); - courseTree.addListener(this); - - layoutCtr = new LayoutMain3ColsController(ureq, wControl, courseTree, new Panel("empty"), null); - stackPanel.pushController("Stats", layoutCtr); - } } \ No newline at end of file diff --git a/src/main/java/org/olat/modules/assessment/ui/AssessableResource.java b/src/main/java/org/olat/modules/assessment/ui/AssessableResource.java index 6bce24068d3..4a1002b20a2 100644 --- a/src/main/java/org/olat/modules/assessment/ui/AssessableResource.java +++ b/src/main/java/org/olat/modules/assessment/ui/AssessableResource.java @@ -19,13 +19,10 @@ */ package org.olat.modules.assessment.ui; -import java.util.List; - import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; -import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.repository.RepositoryEntry; /** @@ -84,7 +81,7 @@ public abstract class AssessableResource { return hasComments; } - public abstract List<Controller> createAssessmentTools(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, - RepositoryEntry entry, AssessmentToolOptions options); + public abstract Controller createIdentityList(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry entry, AssessmentToolSecurityCallback assessmentCallback); } diff --git a/src/main/java/org/olat/modules/assessment/ui/AssessedIdentityElementRow.java b/src/main/java/org/olat/modules/assessment/ui/AssessedIdentityElementRow.java index b7485712e2c..ba8487545bc 100644 --- a/src/main/java/org/olat/modules/assessment/ui/AssessedIdentityElementRow.java +++ b/src/main/java/org/olat/modules/assessment/ui/AssessedIdentityElementRow.java @@ -44,18 +44,19 @@ public class AssessedIdentityElementRow extends UserPropertiesRow { private final Boolean userVisibility; private final BigDecimal score; private final Boolean passed; - private final Date initialCourseLaunchDate; private final Date lastModified, lastUserModified, lastCoachModified; private final int numOfAssessmentDocs; private final AssessmentEntryStatus status; + private Object details; + private Date initialCourseLaunchDate; + private FormLink toolsLink; private CompletionItem currentCompletion; - public AssessedIdentityElementRow(Identity identity, AssessmentEntry entry, Date initialCourseLaunchDate, + public AssessedIdentityElementRow(Identity identity, AssessmentEntry entry, CompletionItem currentCompletion, FormLink toolsLink, List<UserPropertyHandler> userPropertyHandlers, Locale locale) { super(identity, userPropertyHandlers, locale); - this.initialCourseLaunchDate = initialCourseLaunchDate; this.currentCompletion = currentCompletion; this.toolsLink = toolsLink; @@ -96,7 +97,10 @@ public class AssessedIdentityElementRow extends UserPropertiesRow { return initialCourseLaunchDate; } - + public void setInitialCourseLaunchDate(Date initialCourseLaunchDate) { + this.initialCourseLaunchDate = initialCourseLaunchDate; + } + public Date getLastModified() { return lastModified; } @@ -128,4 +132,12 @@ public class AssessedIdentityElementRow extends UserPropertiesRow { public Boolean getUserVisibility() { return userVisibility; } + + public Object getDetails() { + return details; + } + + public void setDetails(Object details) { + this.details = details; + } } \ No newline at end of file diff --git a/src/main/java/org/olat/modules/assessment/ui/AssessedIdentityListController.java b/src/main/java/org/olat/modules/assessment/ui/AssessedIdentityListController.java index 77bf1f953a0..3652bb0415b 100644 --- a/src/main/java/org/olat/modules/assessment/ui/AssessedIdentityListController.java +++ b/src/main/java/org/olat/modules/assessment/ui/AssessedIdentityListController.java @@ -45,7 +45,6 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTable import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; -import org.olat.core.gui.components.stack.BreadcrumbPanelAware; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.components.stack.TooledStackedPanel.Align; import org.olat.core.gui.control.Controller; @@ -93,7 +92,7 @@ public class AssessedIdentityListController extends FormBasicController implemen private final boolean isAdministrativeUser; private SearchAssessedIdentityParams searchParams; private final List<UserPropertyHandler> userPropertyHandlers; - private final AssessmentToolSecurityCallback assessmentCallback; + protected final AssessmentToolSecurityCallback assessmentCallback; private Link nextLink, previousLink; private FlexiTableElement tableEl; @@ -114,7 +113,7 @@ public class AssessedIdentityListController extends FormBasicController implemen @Autowired private UserCourseInformationsManager userInfosMgr; @Autowired - private AssessmentToolManager assessmentToolManager; + protected AssessmentToolManager assessmentToolManager; @Autowired private RepositoryHandlerFactory repositoryHandlerFactory; @@ -122,6 +121,7 @@ public class AssessedIdentityListController extends FormBasicController implemen RepositoryEntry testEntry, AssessableResource element, AssessmentToolSecurityCallback assessmentCallback) { super(ureq, wControl, "identity_element"); setTranslator(Util.createPackageTranslator(AssessmentModule.class, getLocale(), getTranslator())); + setTranslator(Util.createPackageTranslator(AssessedIdentityListController.class, getLocale(), getTranslator())); setTranslator(userManager.getPropertyHandlerTranslator(getTranslator())); this.element = element; @@ -134,6 +134,14 @@ public class AssessedIdentityListController extends FormBasicController implemen initForm(ureq); } + + public RepositoryEntry getRepositoryEntry() { + return testEntry; + } + + public SearchAssessedIdentityParams getSearchParameters() { + return searchParams; + } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { @@ -209,7 +217,6 @@ public class AssessedIdentityListController extends FormBasicController implemen tableEl.setExtendedFilterButton(translate("filter.groups"), groupFilters); } } - } public class AToolsOptions extends AssessmentToolOptions { @@ -220,7 +227,7 @@ public class AssessedIdentityListController extends FormBasicController implemen } } - private void updateModel(UserRequest ureq, String searchString, List<FlexiTableFilter> filters, List<FlexiTableFilter> extendedFilters) { + protected void updateModel(String searchString, List<FlexiTableFilter> filters, List<FlexiTableFilter> extendedFilters) { SearchAssessedIdentityParams params = new SearchAssessedIdentityParams(testEntry, null, testEntry, assessmentCallback); List<AssessmentEntryStatus> assessmentStatus = null; @@ -261,8 +268,9 @@ public class AssessedIdentityListController extends FormBasicController implemen List<AssessedIdentityElementRow> rows = new ArrayList<>(assessedIdentities.size()); for(Identity assessedIdentity:assessedIdentities) { AssessmentEntry entry = entryMap.get(assessedIdentity.getKey()); - Date initialLaunchDate = initialLaunchDates.get(assessedIdentity.getKey()); - rows.add(new AssessedIdentityElementRow(assessedIdentity, entry, initialLaunchDate, null, null, userPropertyHandlers, getLocale())); + AssessedIdentityElementRow row = new AssessedIdentityElementRow(assessedIdentity, entry, null, null, userPropertyHandlers, getLocale()); + row.setInitialCourseLaunchDate(initialLaunchDates.get(assessedIdentity.getKey())); + rows.add(row); } usersTableModel.setObjects(rows); @@ -271,34 +279,13 @@ public class AssessedIdentityListController extends FormBasicController implemen } tableEl.reloadData(); searchParams = params; - - List<String> toolCmpNames = new ArrayList<>(); - AssessmentToolOptions asOptions = new AssessmentToolOptions(); - asOptions.setAdmin(assessmentCallback.isAdmin()); - asOptions.setIdentities(assessedIdentities); - List<Controller> tools = element.createAssessmentTools(ureq, getWindowControl(), stackPanel, - testEntry, asOptions); - int count = 0; - if(tools.size() > 0) { - for(Controller tool:tools) { - listenTo(tool); - String toolCmpName = "ctrl_" + (count++); - flc.put(toolCmpName, tool.getInitialComponent()); - toolCmpNames.add(toolCmpName); - if(tool instanceof BreadcrumbPanelAware) { - ((BreadcrumbPanelAware)tool).setBreadcrumbPanel(stackPanel); - } - } - } - - if(toolsCtrl != null) { - for(Controller toolCtrl:toolsCtrl) { - removeAsListenerAndDispose(toolCtrl); - } - } - toolsCtrl = tools; - flc.contextPut("toolCmpNames", toolCmpNames); + updateTools(assessedIdentities); } + + protected void updateTools(@SuppressWarnings("unused") List<Identity> assessedIdentities) { + //to override + } + @Override protected void doDispose() { // @@ -315,7 +302,7 @@ public class AssessedIdentityListController extends FormBasicController implemen } tableEl.setSelectedFilterKey(filter); - updateModel(ureq, null, tableEl.getSelectedFilters(), null); + updateModel(null, tableEl.getSelectedFilters(), null); if(entries != null && entries.size() > 0) { String resourceType = entries.get(0).getOLATResourceable().getResourceableTypeName(); @@ -350,16 +337,16 @@ public class AssessedIdentityListController extends FormBasicController implemen public void event(UserRequest ureq, Controller source, Event event) { if(currentIdentityCtrl == source) { if(event == Event.CHANGED_EVENT) { - updateModel(ureq, null, null, null); + updateModel(null, null, null); } else if(event == Event.DONE_EVENT) { - updateModel(ureq, null, null, null); + updateModel(null, null, null); stackPanel.popController(currentIdentityCtrl); } else if(event == Event.CANCELLED_EVENT) { stackPanel.popController(currentIdentityCtrl); } } else if(toolsCtrl != null && toolsCtrl.contains(source)) { if(event == Event.CHANGED_EVENT) { - updateModel(ureq, null, null, null); + updateModel(null, null, null); } } super.event(ureq, source, event); @@ -377,7 +364,7 @@ public class AssessedIdentityListController extends FormBasicController implemen } } else if(event instanceof FlexiTableSearchEvent) { FlexiTableSearchEvent ftse = (FlexiTableSearchEvent)event; - updateModel(ureq, ftse.getSearch(), ftse.getFilters(), ftse.getExtendedFilters()); + updateModel(ftse.getSearch(), ftse.getFilters(), ftse.getExtendedFilters()); } } diff --git a/src/main/java/org/olat/modules/assessment/ui/AssessmentToolController.java b/src/main/java/org/olat/modules/assessment/ui/AssessmentToolController.java index bd6ae32bea2..3b2ce524a4e 100644 --- a/src/main/java/org/olat/modules/assessment/ui/AssessmentToolController.java +++ b/src/main/java/org/olat/modules/assessment/ui/AssessmentToolController.java @@ -58,7 +58,7 @@ public class AssessmentToolController extends MainLayoutBasicController implemen private Link usersLink; private final TooledStackedPanel stackPanel; - private AssessedIdentityListController currentCtl; + private Controller currentCtl; private AssessmentOverviewController overviewCtrl; public AssessmentToolController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, @@ -115,13 +115,16 @@ public class AssessmentToolController extends MainLayoutBasicController implemen UserSelectionEvent use = (UserSelectionEvent)event; OLATResourceable resource = OresHelper.createOLATResourceableInstance("Identity", use.getIdentityKey()); List<ContextEntry> entries = BusinessControlFactory.getInstance().createCEListFromString(resource); - doSelectUsersView(ureq, "Users", null).activate(ureq, entries, null); + Controller userViewCtrl = doSelectUsersView(ureq, "Users", null); + if(userViewCtrl instanceof Activateable2) { + ((Activateable2)userViewCtrl).activate(ureq, entries, null); + } } } super.event(ureq, source, event); } - private Activateable2 doSelectUsersView(UserRequest ureq, String resName, AssessedIdentityListState state) { + private Controller doSelectUsersView(UserRequest ureq, String resName, AssessedIdentityListState state) { if(currentCtl != null) { stackPanel.popController(currentCtl); } @@ -129,12 +132,13 @@ public class AssessmentToolController extends MainLayoutBasicController implemen OLATResourceable ores = OresHelper.createOLATResourceableInstance(resName, 0l); WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl()); addToHistory(ureq, bwControl); - AssessedIdentityListController treeCtrl = new AssessedIdentityListController(ureq, bwControl, stackPanel, - testEntry, element, assessmentCallback); + Controller treeCtrl = element.createIdentityList(ureq, bwControl, stackPanel, testEntry, assessmentCallback); listenTo(treeCtrl); stackPanel.pushController(translate("users"), treeCtrl); currentCtl = treeCtrl; - treeCtrl.activate(ureq, null, state); + if(treeCtrl instanceof Activateable2) { + ((Activateable2)treeCtrl).activate(ureq, null, state); + } return currentCtl; } } diff --git a/src/main/java/org/olat/modules/assessment/ui/_content/identity_element.html b/src/main/java/org/olat/modules/assessment/ui/_content/identity_element.html index 552eea214ec..74005495897 100644 --- a/src/main/java/org/olat/modules/assessment/ui/_content/identity_element.html +++ b/src/main/java/org/olat/modules/assessment/ui/_content/identity_element.html @@ -1,10 +1,2 @@ <h2><i class="o_icon $cssClass"> </i> $r.escapeHtml($title)</h2> -#if($r.isNotEmpty($toolCmpNames)) - <div class="o_button_group o_button_group_right"> - #foreach($toolCmpName in $toolCmpNames) - $r.render($toolCmpName) - #end - </div> -#end - $r.render("table") \ No newline at end of file diff --git a/src/main/java/org/olat/modules/portfolio/ui/model/AssessableBinderResource.java b/src/main/java/org/olat/modules/portfolio/ui/model/AssessableBinderResource.java index 6dd0d9255cf..4f5f321ce78 100644 --- a/src/main/java/org/olat/modules/portfolio/ui/model/AssessableBinderResource.java +++ b/src/main/java/org/olat/modules/portfolio/ui/model/AssessableBinderResource.java @@ -19,15 +19,13 @@ */ package org.olat.modules.portfolio.ui.model; -import java.util.Collections; -import java.util.List; - import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; -import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.modules.assessment.ui.AssessableResource; +import org.olat.modules.assessment.ui.AssessedIdentityListController; +import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback; import org.olat.repository.RepositoryEntry; /** @@ -44,8 +42,9 @@ public class AssessableBinderResource extends AssessableResource { } @Override - public List<Controller> createAssessmentTools(UserRequest ureq, WindowControl wControl, - TooledStackedPanel stackPanel, RepositoryEntry entry, AssessmentToolOptions options) { - return Collections.emptyList(); + public Controller createIdentityList(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, + RepositoryEntry entry, AssessmentToolSecurityCallback assessmentCallback) { + return new AssessedIdentityListController(ureq, wControl, stackPanel, + entry, this, assessmentCallback); } } -- GitLab