/** * <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.home; import java.io.File; import java.util.ArrayList; import java.util.Collections; 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.collaboration.CollaborationManager; import org.olat.collaboration.CollaborationTools; import org.olat.commons.calendar.CalendarManager; import org.olat.commons.calendar.CalendarModule; import org.olat.commons.calendar.PersonalCalendarManager; import org.olat.commons.calendar.manager.ImportCalendarManager; import org.olat.commons.calendar.model.CalendarFileInfos; import org.olat.commons.calendar.model.CalendarKey; import org.olat.commons.calendar.model.CalendarUserConfiguration; import org.olat.commons.calendar.ui.components.KalendarRenderWrapper; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DB; import org.olat.core.gui.UserRequest; import org.olat.core.gui.control.WindowControl; import org.olat.core.id.Identity; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.nodes.INode; import org.olat.core.util.tree.TreeVisitor; import org.olat.core.util.tree.Visitor; import org.olat.course.CorruptedCourseException; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.groupsandrights.CourseRights; import org.olat.course.nodes.CalCourseNode; import org.olat.course.nodes.CourseNode; import org.olat.course.run.calendar.CourseLinkProviderController; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupService; import org.olat.group.model.SearchBusinessGroupParams; import org.olat.repository.RepositoryEntry; import org.olat.resource.OLATResource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * * Initial date: 18.08.2015<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ @Service public class HomeCalendarManager implements PersonalCalendarManager { private static final OLog log = Tracing.createLoggerFor(HomeCalendarManager.class); @Autowired private DB dbInstance; @Autowired private CalendarModule calendarModule; @Autowired private CalendarManager calendarManager; @Autowired private BusinessGroupService businessGroupService; @Autowired private ImportCalendarManager importCalendarManager; @Override public List<CalendarFileInfos> getListOfCalendarsFiles(Identity identity) { List<CalendarFileInfos> aggregatedFiles = new ArrayList<>(); Map<CalendarKey,CalendarUserConfiguration> configMap = calendarManager.getCalendarUserConfigurationsMap(identity); //personal calendar CalendarKey personalCalendarKey = new CalendarKey(identity.getName(), CalendarManager.TYPE_USER); CalendarUserConfiguration personalCalendarConfig = configMap.get(personalCalendarKey); if(calendarModule.isEnablePersonalCalendar() && (personalCalendarConfig == null || personalCalendarConfig.isInAggregatedFeed())) { File iCalFile = calendarManager.getCalendarICalFile(CalendarManager.TYPE_USER, identity.getName()); if(iCalFile != null) { aggregatedFiles.add(new CalendarFileInfos(identity.getName(), CalendarManager.TYPE_USER, iCalFile)); } //reload every hour List<CalendarFileInfos> importedCalendars = importCalendarManager.getImportedCalendarInfosForIdentity(identity, true); aggregatedFiles.addAll(importedCalendars); } //group calendars if(calendarModule.isEnableGroupCalendar()) { SearchBusinessGroupParams groupParams = new SearchBusinessGroupParams(identity, true, true); groupParams.addTools(CollaborationTools.TOOL_CALENDAR); List<BusinessGroup> groups = businessGroupService.findBusinessGroups(groupParams, null, 0, -1); Set<BusinessGroup> resourceSet = new HashSet<>(); for(BusinessGroup group:groups) { if(resourceSet.contains(group)) { continue; } else { resourceSet.add(group); } String calendarId = group.getKey().toString(); CalendarKey key = new CalendarKey(calendarId, CalendarManager.TYPE_GROUP); CalendarUserConfiguration calendarConfig = configMap.get(key); if(calendarConfig == null || calendarConfig.isInAggregatedFeed()) { File iCalFile = calendarManager.getCalendarICalFile(CalendarManager.TYPE_GROUP, calendarId); if(iCalFile != null) { aggregatedFiles.add(new CalendarFileInfos(calendarId, CalendarManager.TYPE_GROUP, iCalFile)); } } } } if(calendarModule.isEnableCourseElementCalendar() || calendarModule.isEnableCourseToolCalendar()) { List<Object[]> resources = getCourses(identity); Set<RepositoryEntry> resourceSet = new HashSet<>(); for(Object[] resource:resources) { RepositoryEntry courseEntry = (RepositoryEntry)resource[0]; if(resourceSet.contains(courseEntry)) { continue; } else { resourceSet.add(courseEntry); } String calendarId = courseEntry.getOlatResource().getResourceableId().toString(); CalendarKey key = new CalendarKey(calendarId, CalendarManager.TYPE_COURSE); CalendarUserConfiguration calendarConfig = configMap.get(key); if(calendarConfig == null || calendarConfig.isInAggregatedFeed()) { File iCalFile = calendarManager.getCalendarICalFile(CalendarManager.TYPE_COURSE, calendarId); if(iCalFile != null) { aggregatedFiles.add(new CalendarFileInfos(calendarId, CalendarManager.TYPE_COURSE, iCalFile)); } } } } return aggregatedFiles; } @Override public List<KalendarRenderWrapper> getListOfCalendarWrappers(UserRequest ureq, WindowControl wControl) { if(!calendarModule.isEnabled()) { return new ArrayList<KalendarRenderWrapper>(); } Identity identity = ureq.getIdentity(); List<KalendarRenderWrapper> calendars = new ArrayList<KalendarRenderWrapper>(); Map<CalendarKey,CalendarUserConfiguration> configMap = calendarManager .getCalendarUserConfigurationsMap(ureq.getIdentity()); appendPersonalCalendar(identity, calendars, configMap); appendGroupCalendars(identity, calendars, configMap); appendCourseCalendars(ureq, wControl, calendars, configMap); //reload every hour List<KalendarRenderWrapper> importedCalendars = importCalendarManager.getImportedCalendarsForIdentity(identity, true); for(KalendarRenderWrapper importedCalendar:importedCalendars) { importedCalendar.setPrivateEventsVisible(true); } calendars.addAll(importedCalendars); return calendars; } private void appendPersonalCalendar(Identity identity, List<KalendarRenderWrapper> calendars, Map<CalendarKey,CalendarUserConfiguration> configMap) { // get the personal calendar if(calendarModule.isEnablePersonalCalendar()) { try { KalendarRenderWrapper calendarWrapper = calendarManager.getPersonalCalendar(identity); calendarWrapper.setAccess(KalendarRenderWrapper.ACCESS_READ_WRITE); calendarWrapper.setPrivateEventsVisible(true); CalendarUserConfiguration config = configMap.get(calendarWrapper.getCalendarKey()); if (config != null) { calendarWrapper.setConfiguration(config); } calendars.add(calendarWrapper); } catch (Exception e) { log.error("Cannot read personal calendar of: " + identity, e); } } } private void appendGroupCalendars(Identity identity, List<KalendarRenderWrapper> calendars, Map<CalendarKey,CalendarUserConfiguration> configMap) { // get group calendars if(calendarModule.isEnableGroupCalendar()) { SearchBusinessGroupParams groupParams = new SearchBusinessGroupParams(identity, true, false); groupParams.addTools(CollaborationTools.TOOL_CALENDAR); List<BusinessGroup> ownerGroups = businessGroupService.findBusinessGroups(groupParams, null, 0, -1); addCalendars(ownerGroups, true, false, calendars, configMap); SearchBusinessGroupParams groupParams2 = new SearchBusinessGroupParams(identity, false, true); groupParams2.addTools(CollaborationTools.TOOL_CALENDAR); List<BusinessGroup> attendedGroups = businessGroupService.findBusinessGroups(groupParams2, null, 0, -1); attendedGroups.removeAll(ownerGroups); addCalendars(attendedGroups, false, true, calendars, configMap); } } private void appendCourseCalendars(UserRequest ureq, WindowControl wControl, List<KalendarRenderWrapper> calendars, Map<CalendarKey,CalendarUserConfiguration> configMap) { if(calendarModule.isEnableCourseElementCalendar() || calendarModule.isEnableCourseToolCalendar()) { // add course calendars List<Object[]> resources = getCourses(ureq.getIdentity()); Set<OLATResource> editoredResources = getEditorGrants(ureq.getIdentity()); Set<Long> duplicates = new HashSet<>(); for (Object[] resource:resources) { RepositoryEntry courseEntry = (RepositoryEntry)resource[0]; if(duplicates.contains(courseEntry.getKey())) { continue; } duplicates.add(courseEntry.getKey()); String role = (String)resource[1]; Long courseResourceableID = courseEntry.getOlatResource().getResourceableId(); try { ICourse course = CourseFactory.loadCourse(courseEntry); if(isCourseCalendarEnabled(course)) { //calendar course aren't enabled per default but course node of type calendar are always possible //REVIEW if (!course.getCourseEnvironment().getCourseConfig().isCalendarEnabled()) continue; // add course calendar KalendarRenderWrapper courseCalendarWrapper = calendarManager.getCourseCalendar(course); boolean isPrivileged = GroupRoles.owner.name().equals(role) || editoredResources.contains(courseEntry.getOlatResource()); if (isPrivileged) { courseCalendarWrapper.setAccess(KalendarRenderWrapper.ACCESS_READ_WRITE); } else { courseCalendarWrapper.setAccess(KalendarRenderWrapper.ACCESS_READ_ONLY); } if(role != null && (GroupRoles.owner.name().equals(role) || GroupRoles.coach.name().equals(role) || GroupRoles.participant.name().equals(role))) { courseCalendarWrapper.setPrivateEventsVisible(true); } CalendarUserConfiguration config = configMap.get(courseCalendarWrapper.getCalendarKey()); if (config != null) { courseCalendarWrapper.setConfiguration(config); } courseCalendarWrapper.setLinkProvider(new CourseLinkProviderController(course, Collections.singletonList(course), ureq, wControl)); calendars.add(courseCalendarWrapper); } } catch (CorruptedCourseException e) { OLATResource olatResource = courseEntry.getOlatResource(); log.error("Corrupted course: " + olatResource.getResourceableTypeName() + " :: " + courseResourceableID, null); } catch (Exception e) { OLATResource olatResource = courseEntry.getOlatResource(); log.error("Cannor read calendar of course: " + olatResource.getResourceableTypeName() + " :: " + courseResourceableID, null); } } } } private boolean isCourseCalendarEnabled(ICourse course) { if(course.getCourseConfig().isCalendarEnabled()) { return true; } CourseNode rootNode = course.getRunStructure().getRootNode(); CalCourseNodeVisitor v = new CalCourseNodeVisitor(); new TreeVisitor(v, rootNode, true).visitAll(); return v.isFound(); } /** * * @param identity * @return List of array, first the repository entry, second the role */ private List<Object[]> getCourses(IdentityRef identity) { StringBuilder sb = new StringBuilder(); sb.append("select v, membership.role from repositoryentry v ") .append(" inner join fetch v.olatResource as resource ") .append(" inner join v.groups as retogroup") .append(" inner join retogroup.group as baseGroup") .append(" inner join baseGroup.members as membership") .append(" where v.olatResource.resName='CourseModule' and membership.identity.key=:identityKey and") .append(" (") .append(" (v.access=").append(RepositoryEntry.ACC_OWNERS).append(" and v.membersOnly=true and membership.role in ('").append(GroupRoles.owner.name()).append("','").append(GroupRoles.coach.name()).append("','").append(GroupRoles.participant.name()).append("'))") .append(" or") .append(" (v.access>=").append(RepositoryEntry.ACC_OWNERS).append(" and membership.role='").append(GroupRoles.owner.name()).append("')") .append(" or") .append(" (v.access>=").append(RepositoryEntry.ACC_USERS).append(" and membership.role in ('").append(GroupRoles.coach.name()).append("','").append(GroupRoles.participant.name()).append("'))") .append(" )"); return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Object[].class) .setParameter("identityKey", identity.getKey()) .getResultList(); } private Set<OLATResource> getEditorGrants(IdentityRef identity) { StringBuilder sb = new StringBuilder(); sb.append("select grant.resource from bgrant as grant") .append(" inner join grant.group as baseGroup") .append(" inner join baseGroup.members as membership") .append(" where membership.identity.key=:identityKey and grant.permission='").append(CourseRights.RIGHT_COURSEEDITOR).append("' and membership.role=grant.role"); List<OLATResource> resources = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), OLATResource.class) .setParameter("identityKey", identity.getKey()) .getResultList(); return new HashSet<>(resources); } /** * Append the calendars of a list of groups. The groups must have their calendar tool * enabled, this routine doesn't check it. * @param ureq * @param groups * @param isOwner * @param calendars */ private void addCalendars(List<BusinessGroup> groups, boolean isOwner, boolean isParticipant, List<KalendarRenderWrapper> calendars, Map<CalendarKey,CalendarUserConfiguration> configMap) { Map<Long,Long> groupKeyToAccess = CoreSpringFactory.getImpl(CollaborationManager.class).lookupCalendarAccess(groups); for (BusinessGroup bGroup:groups) { try { KalendarRenderWrapper groupCalendarWrapper = calendarManager.getGroupCalendar(bGroup); groupCalendarWrapper.setPrivateEventsVisible(true); // set calendar access int iCalAccess = CollaborationTools.CALENDAR_ACCESS_OWNERS; Long lCalAccess = groupKeyToAccess.get(bGroup.getKey()); if (lCalAccess != null) { iCalAccess = lCalAccess.intValue(); } if (iCalAccess == CollaborationTools.CALENDAR_ACCESS_OWNERS && !isOwner) { groupCalendarWrapper.setAccess(KalendarRenderWrapper.ACCESS_READ_ONLY); } else { groupCalendarWrapper.setAccess(KalendarRenderWrapper.ACCESS_READ_WRITE); } CalendarUserConfiguration config = configMap.get(groupCalendarWrapper.getCalendarKey()); if (config != null) { groupCalendarWrapper.setConfiguration(config); } if(isOwner || isParticipant) { groupCalendarWrapper.setPrivateEventsVisible(true); } calendars.add(groupCalendarWrapper); } catch (Exception e) { log.error("Cannot read calendar of group: " + bGroup, e); } } } private static class CalCourseNodeVisitor implements Visitor { private boolean found = false; public boolean isFound() { return found; } @Override public void visit(INode node) { if(node instanceof CalCourseNode) { found = true; } } } }