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 org.olat.core.CoreSpringFactory;
import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
import org.olat.core.commons.services.webdav.servlets.RequestUtil;
import org.olat.core.gui.components.tree.GenericTreeModel;
import org.olat.core.gui.components.tree.TreeNode;
import org.olat.core.id.IdentityEnvironment;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.StringHelper;
import org.olat.core.util.vfs.MergeSource;
import org.olat.core.util.vfs.NamedContainerImpl;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSItem;
import org.olat.core.util.vfs.callbacks.ReadOnlyCallback;
import org.olat.core.util.vfs.filters.VFSItemFilter;
import org.olat.course.config.CourseConfig;
import org.olat.course.nodes.BCCourseNode;
import org.olat.course.nodes.CourseNode;
import org.olat.course.nodes.PFCourseNode;

dfurrer
committed
import org.olat.course.nodes.bc.BCCourseNodeEditController;
import org.olat.course.nodes.pf.manager.PFManager;
import org.olat.course.run.userview.NodeEvaluation;
import org.olat.course.run.userview.TreeEvaluation;
import org.olat.course.run.userview.UserCourseEnvironment;
import org.olat.course.run.userview.UserCourseEnvironmentImpl;

srosse
committed
import org.olat.course.run.userview.VisibleTreeFilter;
import org.olat.modules.sharedfolder.SharedFolderManager;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryManager;
import org.olat.repository.RepositoryService;
import org.olat.repository.model.RepositoryEntrySecurity;
import org.olat.resource.OLATResource;
/**

dfurrer
committed
*
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*/
public class MergedCourseContainer extends MergeSource {
private static final OLog log = Tracing.createLoggerFor(MergedCourseContainer.class);
private final Long courseId;
private boolean courseReadOnly = false;
private boolean overrideReadOnly = false;
private final IdentityEnvironment identityEnv;
public MergedCourseContainer(Long courseId, String name) {
this(courseId, name, null, false);
}
public MergedCourseContainer(Long courseId, String name, IdentityEnvironment identityEnv) {
this(courseId, name, identityEnv, false);
}
public MergedCourseContainer(Long courseId, String name, IdentityEnvironment identityEnv, boolean overrideReadOnly) {
super(null, name);
this.courseId = courseId;
this.identityEnv = identityEnv;
this.overrideReadOnly = overrideReadOnly;
}
@Override
protected void init() {
ICourse course = CourseFactory.loadCourse(courseId);
if(course instanceof PersistingCourseImpl) {
init((PersistingCourseImpl)course);
}
}
protected void init(PersistingCourseImpl persistingCourse) {
super.init();
RepositoryEntry courseRe = persistingCourse.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
courseReadOnly = !overrideReadOnly && (courseRe.getRepositoryEntryStatus().isClosed() || courseRe.getRepositoryEntryStatus().isUnpublished());
if(courseReadOnly) {
setLocalSecurityCallback(new ReadOnlyCallback());
}
if(identityEnv == null || identityEnv.getRoles().isOLATAdmin()) {
VFSContainer courseContainer = persistingCourse.getIsolatedCourseFolder();
if(courseReadOnly) {
courseContainer.setLocalSecurityCallback(new ReadOnlyCallback());
}
addContainersChildren(courseContainer, true);
RepositoryEntry re = persistingCourse.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
RepositoryEntrySecurity reSecurity = RepositoryManager.getInstance()
.isAllowed(identityEnv.getIdentity(), identityEnv.getRoles(), re);
if(reSecurity.isEntryAdmin()) {
VFSContainer courseContainer = persistingCourse.getIsolatedCourseFolder();
if(courseReadOnly) {
courseContainer.setLocalSecurityCallback(new ReadOnlyCallback());
}
addContainersChildren(courseContainer, true);
}
initSharedFolder(persistingCourse);
// add all course building blocks of type BC to a virtual folder
MergeSource nodesContainer = new MergeSource(null, "_courseelementdata");
if(identityEnv == null) {
CourseNode rootNode = persistingCourse.getRunStructure().getRootNode();
addFolderBuildingBlocks(persistingCourse, nodesContainer, rootNode);
} else {
TreeEvaluation treeEval = new TreeEvaluation();
GenericTreeModel treeModel = new GenericTreeModel();
UserCourseEnvironment userCourseEnv = new UserCourseEnvironmentImpl(identityEnv, persistingCourse.getCourseEnvironment());
CourseNode rootCn = userCourseEnv.getCourseEnvironment().getRunStructure().getRootNode();
NodeEvaluation rootNodeEval = rootCn.eval(userCourseEnv.getConditionInterpreter(), treeEval, new VisibleTreeFilter());
TreeNode treeRoot = rootNodeEval.getTreeNode();
treeModel.setRootNode(treeRoot);
addFolderBuildingBlocks(persistingCourse, nodesContainer, treeRoot);
}
if (nodesContainer.getItems().size() > 0) {
addContainer(nodesContainer);
}
}
/**
* Grab any shared folder that is configured, but only when in unchecked
* security mode (no identity environment) or when the user has course
* admin rights
*
* @param persistingCourse
*/
private void initSharedFolder(PersistingCourseImpl persistingCourse) {
CourseConfig courseConfig = persistingCourse.getCourseConfig();
String sfSoftkey = courseConfig.getSharedFolderSoftkey();
if (StringHelper.containsNonWhitespace(sfSoftkey) && !CourseConfig.VALUE_EMPTY_SHAREDFOLDER_SOFTKEY.equals(sfSoftkey)) {
RepositoryEntry re = persistingCourse.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
if(identityEnv == null || identityEnv.getRoles().isOLATAdmin() || RepositoryManager.getInstance().isOwnerOfRepositoryEntry(identityEnv.getIdentity(), re)) {
OLATResource sharedResource = CoreSpringFactory.getImpl(RepositoryService.class).loadRepositoryEntryResourceBySoftKey(sfSoftkey);
if (sharedResource != null) {
OlatRootFolderImpl sharedFolder = SharedFolderManager.getInstance().getSharedFolder(sharedResource);
if (sharedFolder != null) {
if(courseConfig.isSharedFolderReadOnlyMount() || courseReadOnly) {
sharedFolder.setLocalSecurityCallback(new ReadOnlyCallback());
}
//add local course folder's children as read/write source and any sharedfolder as subfolder
addContainer(new NamedContainerImpl("_sharedfolder", sharedFolder));
}
}
}
}
}
private void addFolderBuildingBlocks(PersistingCourseImpl course, MergeSource nodesContainer, TreeNode courseNode) {
if(courseNode == null) return;
for (int i = 0; i < courseNode.getChildCount(); i++) {
TreeNode child = (TreeNode)courseNode.getChildAt(i);
NodeEvaluation nodeEval;
if(child.getUserObject() instanceof NodeEvaluation) {
nodeEval = (NodeEvaluation)child.getUserObject();
} else {
continue;
}
if(nodeEval != null && nodeEval.getCourseNode() != null) {
CourseNode courseNodeChild = nodeEval.getCourseNode();
String folderName = RequestUtil.normalizeFilename(courseNodeChild.getShortTitle());
if (courseNodeChild instanceof BCCourseNode) {
final BCCourseNode bcNode = (BCCourseNode) courseNodeChild;
// add folder not to merge source. Use name and node id to have unique name
VFSContainer rootFolder = getBCContainer(course, bcNode);

dfurrer
committed
boolean canDownload = nodeEval.isCapabilityAccessible("download");
if(canDownload && rootFolder != null) {
if(courseReadOnly) {
rootFolder.setLocalSecurityCallback(new ReadOnlyCallback());
} else if(nodeEval.isCapabilityAccessible("upload")) {
//inherit the security callback from the course as for author
} else {
rootFolder.setLocalSecurityCallback(new ReadOnlyCallback());
}
folderName = getBCFolderName(nodesContainer, bcNode, folderName);
// Create a container for this node content and wrap it with a merge source which is attached to tree

dfurrer
committed
VFSContainer nodeContentContainer = new NamedContainerImpl(folderName, rootFolder);
MergeSource courseNodeContainer = new MergeSource(nodesContainer, folderName);
courseNodeContainer.addContainersChildren(nodeContentContainer, true);
nodesContainer.addContainer(courseNodeContainer);
// Do recursion for all children
addFolderBuildingBlocks(course, courseNodeContainer, child);
} else {
// For non-folder course nodes, add merge source (no files to show) ...
MergeSource courseNodeContainer = new MergeSource(null, folderName);
// , then do recursion for all children ...
addFolderBuildingBlocks(course, courseNodeContainer, child);
// ... but only add this container if it contains any children with at least one BC course node
if (courseNodeContainer.getItems().size() > 0) {
nodesContainer.addContainer(courseNodeContainer);
}
}
} else if (courseNodeChild instanceof PFCourseNode) {
final PFCourseNode pfNode = (PFCourseNode) courseNodeChild;
// add folder not to merge source. Use name and node id to have unique name
PFManager pfManager = CoreSpringFactory.getImpl(PFManager.class);
folderName = getBCFolderName(nodesContainer, pfNode, folderName);
MergeSource courseNodeContainer = new MergeSource(nodesContainer, folderName);
UserCourseEnvironment userCourseEnv = new UserCourseEnvironmentImpl(identityEnv, course.getCourseEnvironment());
VFSContainer rootFolder = pfManager.provideCoachOrParticipantContainer(pfNode, userCourseEnv, identityEnv.getIdentity());
VFSContainer nodeContentContainer = new NamedContainerImpl(folderName, rootFolder);
courseNodeContainer.addContainersChildren(nodeContentContainer, true);
addFolderBuildingBlocks(course, courseNodeContainer, child);
nodesContainer.addContainer(courseNodeContainer);
} else {
// For non-folder course nodes, add merge source (no files to show) ...
MergeSource courseNodeContainer = new MergeSource(null, folderName);
// , then do recursion for all children ...
addFolderBuildingBlocks(course, courseNodeContainer, child);
// ... but only add this container if it contains any children with at least one BC course node
if (courseNodeContainer.getItems().size() > 0) {
nodesContainer.addContainer(courseNodeContainer);
}
}
}
}
}
/**
* Internal method to recursively add all course building blocks of type
* BC to a given VFS container. This should only be used for an author view,
* it does not test for security.
* @param course
* @param nodesContainer
* @param courseNode
* @return container for the current course node
*/
private void addFolderBuildingBlocks(PersistingCourseImpl course, MergeSource nodesContainer, CourseNode courseNode) {
for (int i = 0; i < courseNode.getChildCount(); i++) {
CourseNode child = (CourseNode) courseNode.getChildAt(i);
String folderName = RequestUtil.normalizeFilename(child.getShortTitle());
if (child instanceof BCCourseNode) {
final BCCourseNode bcNode = (BCCourseNode) child;
// add folder not to merge source. Use name and node id to have unique name
VFSContainer rootFolder = getBCContainer(course, bcNode);
if(courseReadOnly) {
rootFolder.setLocalSecurityCallback(new ReadOnlyCallback());

dfurrer
committed
}
folderName = getBCFolderName(nodesContainer, bcNode, folderName);

dfurrer
committed
if(rootFolder != null) {
// Create a container for this node content and wrap it with a merge source which is attached to tree
VFSContainer nodeContentContainer = new NamedContainerImpl(folderName, rootFolder);
MergeSource courseNodeContainer = new MergeSource(nodesContainer, folderName);
courseNodeContainer.addContainersChildren(nodeContentContainer, true);
nodesContainer.addContainer(courseNodeContainer);
// Do recursion for all children
addFolderBuildingBlocks(course, courseNodeContainer, child);

dfurrer
committed
}
} else if (child instanceof PFCourseNode) {
//FIXME check if something is to be done here
} else {
// For non-folder course nodes, add merge source (no files to show) ...
MergeSource courseNodeContainer = new MergeSource(null, folderName);
// , then do recursion for all children ...
addFolderBuildingBlocks(course, courseNodeContainer, child);
// ... but only add this container if it contains any children with at least one BC course node
if (courseNodeContainer.getItems().size() > 0) {
nodesContainer.addContainer(courseNodeContainer);
}
}
}
}
/**
* Add node ident if multiple files have same name
*
* @param nodesContainer
* @param bcNode
* @param folderName
* @return
*/
private String getBCFolderName(MergeSource nodesContainer, CourseNode bcNode, String folderName) {
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
// add node ident if multiple files have same name
if (nodesContainer.getItems(new VFSItemFilter() {
@Override
public boolean accept(VFSItem vfsItem) {
return (bcNode.getShortTitle().equals(RequestUtil.normalizeFilename(bcNode.getShortTitle())));
}
}).size() > 0) {
folderName = folderName + " (" + bcNode.getIdent() + ")";
}
return folderName;
}
private VFSContainer getBCContainer(ICourse course, BCCourseNode bcNode) {
bcNode.updateModuleConfigDefaults(false);
// add folder not to merge source. Use name and node id to have unique name
VFSContainer rootFolder = null;
String subpath = bcNode.getModuleConfiguration().getStringValue(BCCourseNodeEditController.CONFIG_SUBPATH);
if(StringHelper.containsNonWhitespace(subpath)){
if(bcNode.isSharedFolder()){
// grab any shared folder that is configured
OlatRootFolderImpl sharedFolder = null;
String sfSoftkey = course.getCourseConfig().getSharedFolderSoftkey();
if (StringHelper.containsNonWhitespace(sfSoftkey) && !CourseConfig.VALUE_EMPTY_SHAREDFOLDER_SOFTKEY.equals(sfSoftkey)) {
RepositoryManager rm = RepositoryManager.getInstance();
RepositoryEntry re = rm.lookupRepositoryEntryBySoftkey(sfSoftkey, false);
if (re != null) {
sharedFolder = SharedFolderManager.getInstance().getSharedFolder(re.getOlatResource());
VFSContainer courseBase = sharedFolder;
subpath = subpath.replaceFirst("/_sharedfolder", "");
rootFolder = (VFSContainer) courseBase.resolve(subpath);
if(rootFolder != null && (course.getCourseConfig().isSharedFolderReadOnlyMount() || courseReadOnly)) {
rootFolder.setLocalSecurityCallback(new ReadOnlyCallback());
}
}
}
}else{
VFSContainer courseBase = course.getCourseBaseContainer();
rootFolder = (VFSContainer) courseBase.resolve("/coursefolder" + subpath);
}
}
if(bcNode.getModuleConfiguration().getBooleanSafe(BCCourseNodeEditController.CONFIG_AUTO_FOLDER)){
String path = BCCourseNode.getFoldernodePathRelToFolderBase(course.getCourseEnvironment(), bcNode);
rootFolder = new OlatRootFolderImpl(path, null);
}
return rootFolder;
}
private Object readResolve() {
try {
init();
return this;
} catch (Exception e) {
log.error("Cannot init the merged container of a course after deserialization", e);
return null;
}
}
}