/** * <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.manager; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import javax.servlet.http.HttpServletRequest; import org.olat.basesecurity.GroupRoles; import org.olat.basesecurity.IdentityRef; import org.olat.core.commons.persistence.DB; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.Encoder; import org.olat.core.util.IPUtils; import org.olat.core.util.StringHelper; import org.olat.course.assessment.AssessmentMode; import org.olat.course.assessment.AssessmentMode.Status; import org.olat.course.assessment.AssessmentMode.Target; import org.olat.course.assessment.AssessmentModeManager; import org.olat.course.assessment.AssessmentModeToArea; import org.olat.course.assessment.AssessmentModeToGroup; import org.olat.course.assessment.model.AssessmentModeImpl; import org.olat.course.assessment.model.AssessmentModeToAreaImpl; import org.olat.course.assessment.model.AssessmentModeToGroupImpl; import org.olat.course.assessment.model.SearchAssessmentModeParams; import org.olat.course.nodes.CourseNode; import org.olat.group.BusinessGroup; import org.olat.group.area.BGArea; import org.olat.group.area.BGAreaManager; import org.olat.group.manager.BusinessGroupRelationDAO; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryRef; import org.olat.repository.RepositoryEntryRelationType; import org.olat.repository.manager.RepositoryEntryRelationDAO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * * Initial date: 12.12.2014<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ @Service("assessmentModeManager") public class AssessmentModeManagerImpl implements AssessmentModeManager { private static final OLog log = Tracing.createLoggerFor(AssessmentModeManagerImpl.class); @Autowired private DB dbInstance; @Autowired private BGAreaManager areaMgr; @Autowired private AssessmentModeDAO assessmentModeDao; @Autowired private BusinessGroupRelationDAO businessGroupRelationDao; @Autowired private RepositoryEntryRelationDAO repositoryEntryRelationDao; @Autowired private AssessmentModeCoordinationServiceImpl assessmentModeCoordinationService; @Override public AssessmentMode createAssessmentMode(RepositoryEntry entry) { AssessmentModeImpl mode = new AssessmentModeImpl(); mode.setCreationDate(new Date()); mode.setLastModified(new Date()); mode.setRepositoryEntry(entry); mode.setStatus(Status.none); mode.setManualBeginEnd(false); return mode; } protected Date evaluateLeadTime(Date begin, int leadtime) { Calendar cal = Calendar.getInstance(); cal.setTime(begin); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); if(leadtime > 0) { cal.add(Calendar.MINUTE, -leadtime); } return cal.getTime(); } protected Date evaluateFollowupTime(Date end, int followupTime) { Calendar cal = Calendar.getInstance(); cal.setTime(end); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); if(followupTime > 0) { cal.add(Calendar.MINUTE, followupTime); } return cal.getTime(); } @Override public AssessmentMode persist(AssessmentMode assessmentMode) { assessmentMode.setLastModified(new Date()); //update begin with lead time Date begin = assessmentMode.getBegin(); Date beginWithLeadTime = evaluateLeadTime(begin, assessmentMode.getLeadTime()); ((AssessmentModeImpl)assessmentMode).setBeginWithLeadTime(beginWithLeadTime); Date end = assessmentMode.getEnd(); Date endWithFollowupTime = this.evaluateFollowupTime(end, assessmentMode.getFollowupTime()); ((AssessmentModeImpl)assessmentMode).setEndWithFollowupTime(endWithFollowupTime); dbInstance.getCurrentEntityManager().persist(assessmentMode); dbInstance.commit(); return assessmentMode; } @Override public AssessmentMode merge(AssessmentMode assessmentMode, boolean forceStatus) { assessmentMode.setLastModified(new Date()); //update begin with lead time Date begin = assessmentMode.getBegin(); Date beginWithLeadTime = evaluateLeadTime(begin, assessmentMode.getLeadTime()); ((AssessmentModeImpl)assessmentMode).setBeginWithLeadTime(beginWithLeadTime); Date end = assessmentMode.getEnd(); Date endWithFollowupTime = evaluateFollowupTime(end, assessmentMode.getFollowupTime()); ((AssessmentModeImpl)assessmentMode).setEndWithFollowupTime(endWithFollowupTime); AssessmentMode reloadedMode; if(assessmentMode.getKey() == null) { dbInstance.getCurrentEntityManager().persist(assessmentMode); reloadedMode = assessmentMode; } else { reloadedMode = dbInstance.getCurrentEntityManager() .merge(assessmentMode); } dbInstance.commit(); if(reloadedMode.isManualBeginEnd()) { reloadedMode = assessmentModeCoordinationService.syncManuallySetStatus(reloadedMode, forceStatus); } else { reloadedMode = assessmentModeCoordinationService.syncAutomicallySetStatus(reloadedMode); } return reloadedMode; } @Override public void delete(AssessmentMode assessmentMode) { assessmentModeDao.delete(assessmentMode); } @Override public AssessmentMode getAssessmentModeById(Long key) { return assessmentModeDao.getAssessmentModeById(key); } @Override public List<AssessmentMode> findAssessmentMode(SearchAssessmentModeParams params) { return assessmentModeDao.findAssessmentMode(params); } @Override public List<AssessmentMode> getAssessmentModeFor(RepositoryEntryRef entry) { return assessmentModeDao.getAssessmentModeFor(entry); } @Override public List<AssessmentMode> getAssessmentModeFor(IdentityRef identity) { List<AssessmentMode> currentModes = getAssessmentModes(new Date()); List<AssessmentMode> myModes = null; if(currentModes.size() > 0) { //check permissions, groups, areas, course myModes = assessmentModeDao.loadAssessmentModeFor(identity, currentModes); } return myModes == null ? Collections.<AssessmentMode>emptyList() : myModes; } @Override public Set<Long> getAssessedIdentityKeys(AssessmentMode assessmentMode) { Target targetAudience = assessmentMode.getTargetAudience(); RepositoryEntry re = assessmentMode.getRepositoryEntry(); Set<Long> assessedKeys = new HashSet<>(); if(targetAudience == Target.course || targetAudience == Target.courseAndGroups) { List<Long> courseMemberKeys = assessmentMode.isApplySettingsForCoach() ? repositoryEntryRelationDao.getMemberKeys(re, RepositoryEntryRelationType.defaultGroup, GroupRoles.coach.name(), GroupRoles.participant.name()) : repositoryEntryRelationDao.getMemberKeys(re, RepositoryEntryRelationType.defaultGroup, GroupRoles.participant.name()); assessedKeys.addAll(courseMemberKeys); } if(targetAudience == Target.groups || targetAudience == Target.courseAndGroups) { List<BusinessGroup> groups = new ArrayList<>(); Set<AssessmentModeToArea> modeToAreas = assessmentMode.getAreas(); if(modeToAreas.size() > 0) { List<BGArea> areas = new ArrayList<>(modeToAreas.size()); for(AssessmentModeToArea modeToArea: modeToAreas) { areas.add(modeToArea.getArea()); } List<BusinessGroup> groupsInAreas = areaMgr.findBusinessGroupsOfAreas(areas); groups.addAll(groupsInAreas); } Set<AssessmentModeToGroup> modeToGroups = assessmentMode.getGroups(); if(modeToGroups.size() > 0) { for(AssessmentModeToGroup modeToGroup: modeToGroups) { groups.add(modeToGroup.getBusinessGroup()); } } List<Long> groupMemberKeys = assessmentMode.isApplySettingsForCoach() ? businessGroupRelationDao.getMemberKeys(groups, GroupRoles.coach.name(), GroupRoles.participant.name()) : businessGroupRelationDao.getMemberKeys(groups, GroupRoles.participant.name()); assessedKeys.addAll(groupMemberKeys); } return assessedKeys; } @Override public List<AssessmentMode> getAssessmentModes(Date now) { return assessmentModeDao.getAssessmentModes(now); } @Override public boolean isInAssessmentMode(RepositoryEntryRef entry, Date date) { return assessmentModeDao.isInAssessmentMode(entry, date); } @Override public AssessmentModeToGroup createAssessmentModeToGroup(AssessmentMode mode, BusinessGroup group) { AssessmentModeToGroupImpl modeToGroup = new AssessmentModeToGroupImpl(); modeToGroup.setAssessmentMode(mode); modeToGroup.setBusinessGroup(group); dbInstance.getCurrentEntityManager().persist(modeToGroup); return modeToGroup; } @Override public AssessmentModeToArea createAssessmentModeToArea(AssessmentMode mode, BGArea area) { AssessmentModeToAreaImpl modeToArea = new AssessmentModeToAreaImpl(); modeToArea.setAssessmentMode(mode); modeToArea.setArea(area); dbInstance.getCurrentEntityManager().persist(modeToArea); return modeToArea; } @Override public boolean isNodeInUse(RepositoryEntryRef entry, CourseNode node) { return assessmentModeDao.isNodeInUse(entry, node); } @Override public boolean isIpAllowed(String ipList, String address) { boolean allOk = false; if(!StringHelper.containsNonWhitespace(ipList)) { allOk |= true; } else { for(StringTokenizer tokenizer = new StringTokenizer(ipList, "\n\r", false); tokenizer.hasMoreTokens(); ) { String ipRange = tokenizer.nextToken(); if(StringHelper.containsNonWhitespace(ipRange)) { int indexMask = ipRange.indexOf("/"); int indexPseudoRange = ipRange.indexOf("-"); if(indexMask > 0) { allOk |= IPUtils.isValidRange(ipRange, address); } else if(indexPseudoRange > 0) { String begin = ipRange.substring(0, indexPseudoRange).trim(); String end = ipRange.substring(indexPseudoRange + 1).trim(); allOk |= IPUtils.isValidRange(begin, end, address); } else { allOk |= ipRange.equals(address); } } } } return allOk; } @Override public boolean isSafelyAllowed(HttpServletRequest request, String safeExamBrowserKeys) { boolean safe = false; boolean debug = log.isDebug(); if(StringHelper.containsNonWhitespace(safeExamBrowserKeys)) { String safeExamHash = request.getHeader("x-safeexambrowser-requesthash"); String url = request.getRequestURL().toString(); for(StringTokenizer tokenizer = new StringTokenizer(safeExamBrowserKeys); tokenizer.hasMoreTokens() && !safe; ) { String safeExamBrowserKey = tokenizer.nextToken(); String hash = Encoder.sha256Exam(url + safeExamBrowserKey); if(safeExamHash != null && safeExamHash.equals(hash)) { safe = true; } if(debug) { log.debug((safeExamHash.equals(hash) ? "Success" : "Failed") + " : " + safeExamHash +" (Header) " + hash + " (Calculated)"); } } } else { safe = true; } return safe; } }