Skip to content
Snippets Groups Projects
CoursefolderWebDAVMergeSource.java 16.6 KiB
Newer Older
/**
 * <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;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.commons.services.webdav.WebDAVModule;
import org.olat.core.commons.services.webdav.manager.WebDAVMergeSource;
import org.olat.core.commons.services.webdav.servlets.RequestUtil;
import org.olat.core.id.IdentityEnvironment;
import org.olat.core.util.StringHelper;
import org.olat.core.util.vfs.NamedContainerImpl;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VirtualContainer;
import org.olat.modules.curriculum.CurriculumService;
import org.olat.modules.curriculum.model.CurriculumElementWebDAVInfos;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryEntryStatusEnum;
import org.olat.repository.RepositoryManager;
import org.olat.repository.model.RepositoryEntryLifecycle;

/**
 * This WebDAV provider delivery all folders in courses where the user
 * is owner or is editor via a right group.
 * 
 * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
 */
class CoursefolderWebDAVMergeSource extends WebDAVMergeSource {
	
	private final IdentityEnvironment identityEnv;
	
	private final WebDAVModule webDAVModule;
	private final RepositoryManager repositoryManager;
	private final CurriculumService curriculumService;
	
	public CoursefolderWebDAVMergeSource(IdentityEnvironment identityEnv) {
		super(identityEnv.getIdentity());
		this.identityEnv = identityEnv;
		webDAVModule = CoreSpringFactory.getImpl(WebDAVModule.class);
		repositoryManager = CoreSpringFactory.getImpl(RepositoryManager.class);
		curriculumService = CoreSpringFactory.getImpl(CurriculumService.class);
	/*
	 * _others
	 *   _managed
	 *     _closed
	 *   _manually
	 *     _closed
	 * 
	 * (non-Javadoc)
	 * @see org.olat.core.commons.services.webdav.manager.WebDAVMergeSource#loadMergedContainers()
	 */
	protected List<VFSContainer> loadMergedContainers() {
		List<VFSContainer> containers = new ArrayList<>();
		Map<String, VFSContainer> terms = new HashMap<>();
		boolean useSemestersTerms = webDAVModule.isTermsFoldersEnabled();
		boolean useCurriculumElementsTerms = webDAVModule.isCurriculumElementFoldersEnabled();
		boolean prependReference = webDAVModule.isPrependCourseReferenceToTitle();
		boolean useManaged = webDAVModule.isManagedFoldersEnabled();
		NamingAndGrouping namingAndGrouping = new NamingAndGrouping(prependReference, useManaged, useSemestersTerms, useCurriculumElementsTerms);
		if(useCurriculumElementsTerms) {
			namingAndGrouping.setCurriculumElementInfos(getCurriculumElementWebDAVInfosMap());
		}
		
		NoTermContainer noTermContainer = new NoTermContainer(namingAndGrouping);
		List<RepositoryEntry> editorEntries = repositoryManager.queryByOwner(getIdentity(), true, "CourseModule");
		appendCourses(editorEntries, containers, terms, noTermContainer, namingAndGrouping, true);
		if(webDAVModule.isEnableLearnersParticipatingCourses()) {
			List<RepositoryEntry> entries = repositoryManager.getLearningResourcesAsParticipantAndCoach(getIdentity(), "CourseModule");
			appendCourses(entries, containers, terms, noTermContainer, namingAndGrouping, false);
		}
		
		//add bookmarked courses
		if(webDAVModule.isEnableLearnersBookmarksCourse()) {
			List<RepositoryEntry> bookmarkedEntries = repositoryManager.getLearningResourcesAsBookmark(getIdentity(), identityEnv.getRoles(), "CourseModule", 0, -1);
			appendCourses(bookmarkedEntries, containers, terms, noTermContainer, namingAndGrouping, false);
		if (useSemestersTerms || useCurriculumElementsTerms) {
			// add no-terms folder if any have been found
			if (noTermContainer.hasNoTerms()) {
				addContainerToList(noTermContainer.computeNoTermContainer(), containers);
		} else if(noTermContainer.hasFinished()) {
			addContainerToList(noTermContainer.getFinishedContainer(), containers);
	private Map<Long,List<CurriculumElementWebDAVInfos>> getCurriculumElementWebDAVInfosMap() {
		List<CurriculumElementWebDAVInfos> infos = curriculumService.getCurriculumElementInfosForWebDAV(getIdentity());
		Map<Long,List<CurriculumElementWebDAVInfos>> infoMap = new HashMap<>();
		for(CurriculumElementWebDAVInfos info:infos) {
			List<CurriculumElementWebDAVInfos> repoInfos = infoMap
					.computeIfAbsent(info.getRepositoryEntryKey(), i -> new ArrayList<>());
			if(!repoInfos.contains(info)) {
				repoInfos.add(info);
			}
		}
		return infoMap;
	}
	
	private void appendCourses(List<RepositoryEntry> courseEntries, List<VFSContainer> containers,
			Map<String, VFSContainer> terms, NoTermContainer noTermContainer,
			NamingAndGrouping namingAndGrouping, boolean editor) {	
		// Add all found repo entries to merge source
		for (RepositoryEntry re:courseEntries) {
			if(namingAndGrouping.isDuplicate(re)) {
			if(namingAndGrouping.isUseFinished() && re.getEntryStatus() == RepositoryEntryStatusEnum.closed) {
				String courseTitle = getCourseTitle(re, namingAndGrouping.isPrependReference());
				String name = namingAndGrouping.getFinishedUniqueName(courseTitle);
				NamedContainerImpl cfContainer = new CoursefolderWebDAVNamedContainer(name, re, editor ? null : identityEnv);
				noTermContainer.addToFinished(cfContainer);
			} else if (namingAndGrouping.isUseSemesterTerms() || namingAndGrouping.isUseCurriculumElementsTerms()) {
				appendCoursesWithTerms(re, containers, noTermContainer, terms, namingAndGrouping, editor);
				String courseTitle = getCourseTitle(re, namingAndGrouping.isPrependReference());
				String name = namingAndGrouping.getContainersUniqueName(courseTitle);
				NamedContainerImpl cfContainer = new CoursefolderWebDAVNamedContainer(name, re, editor ? null : identityEnv);
				addContainerToList(cfContainer, containers);
			if(++count % 5 == 0) {
				DBFactory.getInstance().commitAndCloseSession();
			}
	private void appendCoursesWithTerms(RepositoryEntry re, List<VFSContainer> containers, NoTermContainer noTermContainer,
			Map<String, VFSContainer> terms, NamingAndGrouping namingAndGrouping, boolean editor) {
		RepositoryEntryLifecycle lc = re.getLifecycle();
		
		boolean termed = false;
		if (namingAndGrouping.isUseSemesterTerms() && lc != null && !lc.isPrivateCycle()) {
			// when a semester term info is found, add it to corresponding term folder
			String termSoftKey = lc.getSoftKey();
			VFSContainer termContainer = terms.computeIfAbsent(termSoftKey, term -> {
				String normalizedKey = RequestUtil.normalizeFilename(term);
				VirtualContainer container = new VirtualContainer(normalizedKey);
				addContainerToList(container, containers);
				return container;
			});

			String courseTitle = getCourseTitle(re, namingAndGrouping.isPrependReference());
			String name = namingAndGrouping.getTermUniqueName(termSoftKey, courseTitle);
			NamedContainerImpl cfContainer = new CoursefolderWebDAVNamedContainer(name, re, editor ? null : identityEnv);
			termContainer.getItems().add(cfContainer);
			termed = true;
		}
		
		if(namingAndGrouping.isUseManaged() && re.getEntryStatus() == RepositoryEntryStatusEnum.closed) {
			String courseTitle = getCourseTitle(re, namingAndGrouping.isPrependReference());
			String name = namingAndGrouping.getNoTermUniqueName(courseTitle);
			NamedContainerImpl cfContainer = new CoursefolderWebDAVNamedContainer(name, re, editor ? null : identityEnv);
			noTermContainer.add(re, cfContainer);
			termed = true;
		} else if(namingAndGrouping.isUseCurriculumElementsTerms() && namingAndGrouping.hasCurriculumElements(re)) {
			List<CurriculumElementWebDAVInfos> elements = namingAndGrouping.getCurriculumElementInfos().get(re.getKey());
			for(CurriculumElementWebDAVInfos element:elements) {
				String termSoftKey = getTermSoftKey(element);
				VFSContainer termContainer = terms.computeIfAbsent(termSoftKey, term -> {
					String normalizedKey = RequestUtil.normalizeFilename(term);
					VirtualContainer container = new VirtualContainer(normalizedKey);
					addContainerToList(container, containers);
					return container;
				});	

				String courseTitle = getCourseTitle(re, false);
				String name = namingAndGrouping.getTermUniqueName(termSoftKey, courseTitle);
				NamedContainerImpl cfContainer = new CoursefolderWebDAVNamedContainer(name, re, editor ? null : identityEnv);
				termContainer.getItems().add(cfContainer);
				termed = true;
			}
		}
		
		if(!termed) {
			// no semester term found, add to no-term folder
			String courseTitle = getCourseTitle(re, namingAndGrouping.isPrependReference());
			String name = namingAndGrouping.getNoTermUniqueName(courseTitle);
			NamedContainerImpl cfContainer = new CoursefolderWebDAVNamedContainer(name, re, editor ? null : identityEnv);
			noTermContainer.add(re, cfContainer);
		}
	}
	
	private String getCourseTitle(RepositoryEntry re, boolean prependReference) {
		String displayName = re.getDisplayname();
		if(prependReference && StringHelper.containsNonWhitespace(re.getExternalRef())) {
			displayName = re.getExternalRef() + " " + displayName;
		}
		return RequestUtil.normalizeFilename(displayName);
	}
	
	private String getTermSoftKey(CurriculumElementWebDAVInfos element) {
		StringBuilder sb = new StringBuilder(64);
		if(StringHelper.containsNonWhitespace(element.getParentCurriculumElementDisplayName())) {
			if(StringHelper.containsNonWhitespace(element.getParentCurriculumElementIdentifier())) {
				sb.append(element.getParentCurriculumElementIdentifier()).append(" ");
			}
			sb.append(element.getParentCurriculumElementDisplayName());
		} else if(StringHelper.containsNonWhitespace(element.getCurriculumElementDisplayName())) {
			if(StringHelper.containsNonWhitespace(element.getCurriculumElementIdentifier())) {
				sb.append(element.getCurriculumElementIdentifier()).append(" ");
			}
			sb.append(element.getCurriculumElementDisplayName());
		}
		return sb.toString();
	}
	
	private static class NoTermContainer {
	
		private final Set<RepositoryEntry> entriesSet = new HashSet<>();
		private final VirtualContainer noTermContainer = new VirtualContainer("_other");
		private final VirtualContainer finishedContainer = new VirtualContainer("_finished");
		

		private VirtualContainer managedContainer;
		private VirtualContainer managedAndClosedContainer;
		private VirtualContainer manuallyContainer;
		private VirtualContainer manuallyAndClosedContainer;
		
		private final NamingAndGrouping namingAndGrouping;
		
		public NoTermContainer(NamingAndGrouping namingAndGrouping) {
			this.namingAndGrouping = namingAndGrouping;
		}
		
		public boolean hasNoTerms() {
			return !noTermContainer.getItems().isEmpty();
		}
		
		public VFSContainer computeNoTermContainer() {
			return noTermContainer;
		}
		
		public boolean hasFinished() {
			return !finishedContainer.getItems().isEmpty();
		}
		
		public VFSContainer getFinishedContainer() {
			return finishedContainer;
		}
		
		public void addToFinished(VFSContainer container) {
			finishedContainer.getItems().add(container);
		}
		
		public void addToManagedAndClosed(VFSContainer container) {
			if(managedAndClosedContainer == null) {
				managedAndClosedContainer = new VirtualContainer("_closed");
				addToManaged(managedAndClosedContainer);
			}
			managedAndClosedContainer.getItems().add(container);
		}
		
		public void addToManaged(VFSContainer container) {
			if(managedContainer == null) {
				managedContainer = new VirtualContainer("_managed");
				noTermContainer.getItems().add(managedContainer);
			}
			managedContainer.getItems().add(container);	
		}
		
		public void addToManuualyAndClosed(VFSContainer container) {
			if(manuallyAndClosedContainer == null) {
				manuallyAndClosedContainer = new VirtualContainer("_closed");
				addToManually(manuallyAndClosedContainer);
			}
			manuallyAndClosedContainer.getItems().add(container);
		}
		
		public void addToManually(VFSContainer container) {
			if(manuallyContainer == null) {
				manuallyContainer = new VirtualContainer("_manually");
				noTermContainer.getItems().add(manuallyContainer);
			}
			manuallyContainer.getItems().add(container);
		}
		
		public void add(RepositoryEntry entry, VFSContainer container) {
			if(entriesSet.contains(entry)) {
				return;
			} else {
				entriesSet.add(entry);
			}
			if(namingAndGrouping.isUseManaged()) {
				if(StringHelper.containsNonWhitespace(entry.getManagedFlagsString())) {
					if(entry.getEntryStatus() == RepositoryEntryStatusEnum.closed) {
						addToManagedAndClosed(container);
					} else {
						addToManaged(container);
					}
				} else if(entry.getEntryStatus() == RepositoryEntryStatusEnum.closed) {
					addToManuualyAndClosed(container);
				} else {
					addToManually(container);
				}
			} else {
				noTermContainer.getItems().add(container);
			}
		}
	}
	
	private static class NamingAndGrouping {
		
		private final boolean useManaged;
		private final boolean prependReference;
		private final boolean useSemesterTerms;
		private final boolean useCurriculumElementsTerms;
		
		private final Set<RepositoryEntry> duplicates = new HashSet<>();
		private final Set<String> containers = new HashSet<>();
		private final Set<String> noTermContainer = new HashSet<>();
		private final Set<String> finishedContainer = new HashSet<>();
		private final Map<String,Set<String>> termContainers = new HashMap<>();
		
		private Map<Long,List<CurriculumElementWebDAVInfos>> curriculumElementInfos;
		
		public NamingAndGrouping(boolean prependReference, boolean useManaged,
				boolean useSemesterTerms, boolean useCurriculumElementsTerms) {
			this.prependReference = prependReference;
			this.useManaged = useManaged;
			this.useSemesterTerms = useSemesterTerms;
			this.useCurriculumElementsTerms = useCurriculumElementsTerms;
		}
		
		public boolean isUseManaged() {
			return useManaged;
		}
		
		public boolean isUseSemesterTerms() {
			return useSemesterTerms;
		}

		public boolean isUseCurriculumElementsTerms() {
			return useCurriculumElementsTerms;
		}
		
		public boolean isPrependReference() {
			return prependReference;
		}
		
		public boolean isUseFinished() {
			return !useSemesterTerms && !useSemesterTerms && !useManaged;
		}

		public Map<Long, List<CurriculumElementWebDAVInfos>> getCurriculumElementInfos() {
			return curriculumElementInfos;
		}

		public void setCurriculumElementInfos(Map<Long, List<CurriculumElementWebDAVInfos>> curriculumElementInfos) {
			this.curriculumElementInfos = curriculumElementInfos;
		}
		
		public boolean hasCurriculumElements(RepositoryEntry re) {
			return curriculumElementInfos != null && curriculumElementInfos.containsKey(re.getKey());
		}

		public boolean isDuplicate(RepositoryEntry re) {
			boolean duplicate = duplicates.contains(re);
			if(!duplicate) {
				duplicates.add(re);
			}
			return duplicate;
		}
		
		private String getTermUniqueName(String term, String courseTitle) {
			String name = courseTitle;
			if(termContainers.containsKey(term)) {
				Set<String> termContainer = termContainers.get(term);
				name = getUniqueName(courseTitle, termContainer);
			} else {
				Set<String> termContainer = new HashSet<>();
				termContainer.add(courseTitle);
				termContainers.put(term, termContainer);
			}
			return name;
		}
		
		private String getNoTermUniqueName(String courseTitle) {
			return getUniqueName(courseTitle, noTermContainer);
		}
		
		private String getFinishedUniqueName(String courseTitle) {
			return getUniqueName(courseTitle, finishedContainer);
		}
		
		private String getContainersUniqueName(String courseTitle) {
			return getUniqueName(courseTitle, containers);
		}
		
		private String getUniqueName(String name, Set<String> set) {
			String uniqueName = name;
			if(set.contains(name)) {
				// attach a serial to the group name to avoid duplicate mount points...
				int serial = 1;
				while (set.contains(name + " " + serial) && serial < 255) {
					serial++;
				}
				uniqueName = name + " " + serial;
			}
			set.add(uniqueName);
			return uniqueName;
		}
		
	}