From fd6f4370bd4ac4cd4c2be0ac826f2ab0d412a969 Mon Sep 17 00:00:00 2001 From: fkiefer <none@none> Date: Fri, 13 Jan 2017 14:01:59 +0100 Subject: [PATCH] OO-2365 GUI Changes, Webdav behaviour, wording --- .../olat/course/MergedCourseContainer.java | 16 +- .../groupsandrights/CourseGroupManager.java | 2 +- .../nodes/pf/manager/CountingCallback.java | 123 +++++++++++ .../nodes/pf/manager/FileSystemExport.java | 13 +- .../course/nodes/pf/manager/PFManager.java | 198 ++++++++++-------- .../nodes/pf/manager/PFNotifications.java | 2 +- .../pf/manager/PFNotificationsHandler.java | 2 +- .../nodes/pf/manager/ReadWriteCallback.java | 4 +- .../course/nodes/pf/ui/PFCoachController.java | 4 +- .../nodes/pf/ui/PFEditFormController.java | 24 ++- .../nodes/pf/ui/PFParticipantController.java | 3 +- .../nodes/pf/ui/PFPeekviewController.java | 3 +- .../pf/ui/_i18n/LocalStrings_de.properties | 3 + .../pf/ui/_i18n/LocalStrings_en.properties | 3 + .../pf/ui/_i18n/LocalStrings_fr.properties | 5 +- .../nodes/pf/manager/PFManagerTest.java | 6 +- 16 files changed, 300 insertions(+), 111 deletions(-) create mode 100644 src/main/java/org/olat/course/nodes/pf/manager/CountingCallback.java diff --git a/src/main/java/org/olat/course/MergedCourseContainer.java b/src/main/java/org/olat/course/MergedCourseContainer.java index 5167de38893..a52893c4cec 100644 --- a/src/main/java/org/olat/course/MergedCourseContainer.java +++ b/src/main/java/org/olat/course/MergedCourseContainer.java @@ -226,7 +226,8 @@ public class MergedCourseContainer extends MergeSource { 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 rootFolder = pfManager.provideCoachOrParticipantContainer(pfNode, userCourseEnv, + identityEnv.getIdentity(), courseReadOnly); VFSContainer nodeContentContainer = new NamedContainerImpl(folderName, rootFolder); courseNodeContainer.addContainersChildren(nodeContentContainer, true); @@ -284,7 +285,18 @@ public class MergedCourseContainer extends MergeSource { addFolderBuildingBlocks(course, courseNodeContainer, child); } } else if (child instanceof PFCourseNode) { - //FIXME check if something is to be done here + final PFCourseNode pfNode = (PFCourseNode) child; + // 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); + VFSContainer rootFolder = pfManager.provideAdminContainer(pfNode, course.getCourseEnvironment()); + VFSContainer nodeContentContainer = new NamedContainerImpl(folderName, rootFolder); + courseNodeContainer.addContainersChildren(nodeContentContainer, true); + + addFolderBuildingBlocks(course, courseNodeContainer, child); + // Do recursion for all children + nodesContainer.addContainer(courseNodeContainer); } else { // For non-folder course nodes, add merge source (no files to show) ... MergeSource courseNodeContainer = new MergeSource(null, folderName); diff --git a/src/main/java/org/olat/course/groupsandrights/CourseGroupManager.java b/src/main/java/org/olat/course/groupsandrights/CourseGroupManager.java index a79465bb2f6..ef5ab2196ca 100644 --- a/src/main/java/org/olat/course/groupsandrights/CourseGroupManager.java +++ b/src/main/java/org/olat/course/groupsandrights/CourseGroupManager.java @@ -108,7 +108,7 @@ public interface CourseGroupManager { public boolean isIdentityInLearningArea(Identity identity, Long areaKey); /** - * Checks if user is coach in any of the courses learning groups + * Checks if user is coach in the course (of the repository entry or of a business group) * * @param identity * @return true if user is coach diff --git a/src/main/java/org/olat/course/nodes/pf/manager/CountingCallback.java b/src/main/java/org/olat/course/nodes/pf/manager/CountingCallback.java new file mode 100644 index 00000000000..4800bcc0637 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/pf/manager/CountingCallback.java @@ -0,0 +1,123 @@ +/** + * <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.pf.manager; + +import java.util.List; + +import org.olat.core.commons.modules.bc.components.FolderComponent; +import org.olat.core.commons.services.notifications.SubscriptionContext; +import org.olat.core.util.vfs.Quota; +import org.olat.core.util.vfs.VFSContainer; +import org.olat.core.util.vfs.VFSItem; +import org.olat.core.util.vfs.callbacks.VFSSecurityCallback; +import org.olat.core.util.vfs.filters.VFSItemExcludePrefixFilter; +import org.olat.core.util.vfs.filters.VFSItemFilter; +/** +* +* @author Fabian Kiefer, fabian.kiefer@frentix.com, http://www.frentix.com +* +*/ +public class CountingCallback implements VFSSecurityCallback { + + private static final VFSItemFilter attachmentExcludeFilter = new VFSItemExcludePrefixFilter(FolderComponent.ATTACHMENT_EXCLUDE_PREFIXES); + + private SubscriptionContext subsContext; + private VFSContainer dropbox; + private int limit; + private boolean alterFile; + + public CountingCallback(SubscriptionContext subsContext, VFSContainer dropbox, int limit, boolean alterFile) { + super(); + this.subsContext = subsContext; + this.dropbox = dropbox; + this.limit = limit; + this.alterFile = alterFile; + } + + private int countFiles(VFSContainer vfsContainer) { + int counter = 0; + if (vfsContainer.exists()) { + List<VFSItem> children = vfsContainer.getItems(attachmentExcludeFilter); + for (VFSItem vfsItem : children) { + if (vfsItem instanceof VFSContainer){ + counter += countFiles((VFSContainer)vfsItem); + } else { + counter++; + } + } + } + return counter; + } + + private boolean limitReached () { + return countFiles(dropbox) <= limit; + } + + @Override + public boolean canRead() { + return true; + } + + @Override + public boolean canWrite() { + return limitReached(); + } + + @Override + public boolean canCreateFolder() { + return limitReached(); + } + + @Override + public boolean canDelete() { + return alterFile; + } + + @Override + public boolean canList() { + return true; + } + + @Override + public boolean canCopy() { + return limitReached(); + } + + @Override + public boolean canDeleteRevisionsPermanently() { + return false; + } + + @Override + public Quota getQuota() { + return null; + } + + @Override + public void setQuota(Quota quota) { + + } + + @Override + public SubscriptionContext getSubscriptionContext() { + return subsContext; + } + +} diff --git a/src/main/java/org/olat/course/nodes/pf/manager/FileSystemExport.java b/src/main/java/org/olat/course/nodes/pf/manager/FileSystemExport.java index 950f3139e51..f440cabb989 100644 --- a/src/main/java/org/olat/course/nodes/pf/manager/FileSystemExport.java +++ b/src/main/java/org/olat/course/nodes/pf/manager/FileSystemExport.java @@ -29,6 +29,7 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -37,10 +38,13 @@ import javax.servlet.http.HttpServletResponse; import org.olat.core.CoreSpringFactory; import org.olat.core.gui.media.MediaResource; +import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; +import org.olat.core.util.Util; import org.olat.course.nodes.PFCourseNode; +import org.olat.course.nodes.pf.ui.PFRunController; import org.olat.course.run.environment.CourseEnvironment; import org.olat.user.UserManager; /** @@ -56,12 +60,15 @@ public class FileSystemExport implements MediaResource { private List<Identity> identities; private PFCourseNode pfNode; private CourseEnvironment courseEnv; + private Translator translator; - public FileSystemExport(List<Identity> identities, PFCourseNode pfNode, CourseEnvironment courseEnv) { + public FileSystemExport(List<Identity> identities, PFCourseNode pfNode, CourseEnvironment courseEnv, Locale locale) { super(); this.identities = identities; this.pfNode = pfNode; this.courseEnv = courseEnv; + this.translator = Util.createPackageTranslator(PFRunController.class, locale); + } @Override @@ -94,10 +101,10 @@ public class FileSystemExport implements MediaResource { try (ZipOutputStream zout = new ZipOutputStream(hres.getOutputStream())) { zout.setLevel(9); - String pfolder = "participantfolder/"; + String pfolder = translator.translate("participant.folder") + "/"; Path relPath = Paths.get(courseEnv.getCourseBaseContainer().getBasefile().getAbsolutePath(), - pfolder, pfNode.getIdent()); + PFManager.FILENAME_PARTICIPANTFOLDER, pfNode.getIdent()); fsToZip(zout, relPath, pfolder); diff --git a/src/main/java/org/olat/course/nodes/pf/manager/PFManager.java b/src/main/java/org/olat/course/nodes/pf/manager/PFManager.java index 29f2a712925..14b6b489a46 100644 --- a/src/main/java/org/olat/course/nodes/pf/manager/PFManager.java +++ b/src/main/java/org/olat/course/nodes/pf/manager/PFManager.java @@ -30,15 +30,14 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.olat.basesecurity.GroupRoles; +import org.olat.core.commons.modules.bc.components.FolderComponent; import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl; import org.olat.core.commons.services.notifications.SubscriptionContext; import org.olat.core.gui.UserRequest; @@ -47,9 +46,9 @@ import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; -import org.olat.core.util.FileUtils; import org.olat.core.util.Util; import org.olat.core.util.i18n.I18nManager; +import org.olat.core.util.i18n.I18nModule; import org.olat.core.util.vfs.NamedContainerImpl; import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSItem; @@ -57,8 +56,9 @@ import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.VFSManager; import org.olat.core.util.vfs.VirtualContainer; import org.olat.core.util.vfs.callbacks.VFSSecurityCallback; +import org.olat.core.util.vfs.filters.VFSItemExcludePrefixFilter; +import org.olat.core.util.vfs.filters.VFSItemFilter; import org.olat.course.CourseModule; -import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.nodes.PFCourseNode; import org.olat.course.nodes.pf.ui.DropBoxRow; import org.olat.course.nodes.pf.ui.PFRunController; @@ -66,6 +66,10 @@ import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupService; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryEntryRelationType; +import org.olat.repository.RepositoryService; +import org.olat.repository.manager.RepositoryEntryRelationDAO; import org.olat.user.UserManager; import org.olat.user.UserPropertiesRow; import org.olat.user.propertyhandlers.UserPropertyHandler; @@ -83,7 +87,8 @@ import org.springframework.stereotype.Service; public class PFManager { private static final OLog log = Tracing.createLoggerFor(PFManager.class); - + private static final VFSItemFilter attachmentExcludeFilter = new VFSItemExcludePrefixFilter(FolderComponent.ATTACHMENT_EXCLUDE_PREFIXES); + public static final String FILENAME_PARTICIPANTFOLDER = "participantfolder"; public static final String FILENAME_RETURNBOX = "returnbox"; public static final String FILENAME_DROPBOX = "dropbox"; @@ -91,6 +96,10 @@ public class PFManager { @Autowired private BusinessGroupService groupService; @Autowired + private RepositoryService repositoryService; + @Autowired + private RepositoryEntryRelationDAO repositoryEntryRelationDao; + @Autowired private UserManager userManager; /** @@ -132,16 +141,12 @@ public class PFManager { private int countFiles(VFSContainer vfsContainer) { int counter = 0; if (vfsContainer.exists()) { - List<VFSItem> children = vfsContainer.getItems(); + List<VFSItem> children = vfsContainer.getItems(attachmentExcludeFilter); for (VFSItem vfsItem : children) { if (vfsItem instanceof VFSContainer){ counter += countFiles((VFSContainer)vfsItem); } else { - String filename = vfsItem.getName(); - String suffix = FileUtils.getFileSuffix(filename); - if (!filename.startsWith(".") && !"zip".equals(suffix)){ - counter++; - } + counter++; } } } @@ -251,7 +256,7 @@ public class PFManager { * @param courseEnv */ public MediaResource exportMediaResource (UserRequest ureq, List<Identity> identities, PFCourseNode pfNode, CourseEnvironment courseEnv) { - MediaResource resource = new FileSystemExport (identities, pfNode, courseEnv); + MediaResource resource = new FileSystemExport (identities, pfNode, courseEnv, ureq.getLocale()); ureq.getDispatchResult().setResultingMediaResource(resource); return resource; } @@ -263,7 +268,7 @@ public class PFManager { * @param dropbox * @return the VFSSecurityCallback */ - private VFSSecurityCallback calculateCallback (CourseEnvironment courseEnv, PFCourseNode pfNode, VFSContainer dropbox) { + private VFSSecurityCallback calculateCallback (CourseEnvironment courseEnv, PFCourseNode pfNode, VFSContainer dropbox, boolean webdav) { VFSSecurityCallback callback; SubscriptionContext folderSubContext = CourseModule.createSubscriptionContext(courseEnv, pfNode); int count = countFiles(dropbox); @@ -272,6 +277,8 @@ public class PFManager { boolean alterFile = pfNode.hasAlterFileConfigured(); if (timeFrame || limitCount && !alterFile){ callback = new ReadOnlyCallback(folderSubContext); + } else if (webdav) { + callback= new CountingCallback(folderSubContext, dropbox, pfNode.getLimitCount(), alterFile); } else if (limitCount && alterFile) { callback = new ReadDeleteCallback(folderSubContext); } else if (!limitCount && !alterFile) { @@ -291,12 +298,13 @@ public class PFManager { * @param identity * @return the VFSContainer */ - public VFSContainer provideCoachOrParticipantContainer (PFCourseNode pfNode, UserCourseEnvironment userCourseEnv, Identity identity) { + public VFSContainer provideCoachOrParticipantContainer (PFCourseNode pfNode, UserCourseEnvironment userCourseEnv, + Identity identity, boolean courseReadOnly) { VFSContainer vfsContainer = null; if (userCourseEnv.isCoach() || userCourseEnv.isAdmin()) { - vfsContainer = provideCoachContainer(pfNode, userCourseEnv.getCourseEnvironment(), identity); + vfsContainer = provideCoachContainer(pfNode, userCourseEnv.getCourseEnvironment(), identity, userCourseEnv.isAdmin()); } else if (userCourseEnv.isParticipant()) { - vfsContainer = provideParticipantContainer(pfNode, userCourseEnv, identity); + vfsContainer = provideParticipantContainer(pfNode, userCourseEnv.getCourseEnvironment(), identity, courseReadOnly); } return vfsContainer; } @@ -309,11 +317,10 @@ public class PFManager { * @param identity * @return the VFS container */ - private VFSContainer provideParticipantContainer (PFCourseNode pfNode, UserCourseEnvironment userCourseEnv, Identity identity) { + private VFSContainer provideParticipantContainer (PFCourseNode pfNode, CourseEnvironment courseEnv, + Identity identity, boolean courseReadOnly) { Locale locale = I18nManager.getInstance().getLocaleOrDefault(identity.getUser().getPreferences().getLanguage()); Translator translator = Util.createPackageTranslator(PFRunController.class, locale); - CourseEnvironment courseEnv = userCourseEnv.getCourseEnvironment(); - boolean readOnly = userCourseEnv.isCourseReadOnly(); SubscriptionContext subsContext = CourseModule.createSubscriptionContext(courseEnv, pfNode); String path = courseEnv.getCourseBaseContainer().getRelPath() + "/" + FILENAME_PARTICIPANTFOLDER; VFSContainer courseElementBaseContainer = new OlatRootFolderImpl(path, null); @@ -323,11 +330,11 @@ public class PFManager { if (pfNode.hasParticipantBoxConfigured()){ VFSContainer dropContainer = new NamedContainerImpl(translator.translate("drop.box"), VFSManager.resolveOrCreateContainerFromPath(userBaseContainer, FILENAME_DROPBOX)); - if (readOnly) { + if (courseReadOnly) { dropContainer.setLocalSecurityCallback(new ReadOnlyCallback(subsContext)); } else { VFSContainer dropbox = resolveOrCreateDropFolder(courseEnv, pfNode, identity); - VFSSecurityCallback callback = calculateCallback(courseEnv, pfNode, dropbox); + VFSSecurityCallback callback = calculateCallback(courseEnv, pfNode, dropbox, true); dropContainer.setLocalSecurityCallback(callback); } namedCourseFolder.addItem(dropContainer); @@ -349,26 +356,28 @@ public class PFManager { * @param identity * @return the VFSContainer */ - private VFSContainer provideCoachContainer (PFCourseNode pfNode, CourseEnvironment courseEnv, Identity identity) { + private VFSContainer provideCoachContainer (PFCourseNode pfNode, CourseEnvironment courseEnv, Identity identity, boolean admin) { Locale locale = I18nManager.getInstance().getLocaleOrDefault(identity.getUser().getPreferences().getLanguage()); Translator translator = Util.createPackageTranslator(PFRunController.class, locale); SubscriptionContext nodefolderSubContext = CourseModule.createSubscriptionContext(courseEnv, pfNode); - List<Identity> participants = getParticipants(identity, courseEnv); + List<Identity> participants = getParticipants(identity, courseEnv, admin); String path = courseEnv.getCourseBaseContainer().getRelPath() + "/" + FILENAME_PARTICIPANTFOLDER; VFSContainer courseElementBaseContainer = new OlatRootFolderImpl(path, null); - VirtualContainer namedCourseFolder = new VirtualContainer(identity.getName()); + VirtualContainer namedCourseFolder = new VirtualContainer(translator.translate("participant.folder")); for (Identity participant : participants) { Path relPath = Paths.get(pfNode.getIdent(), getIdFolderName(participant)); VFSContainer userBaseContainer = VFSManager.resolveOrCreateContainerFromPath(courseElementBaseContainer, relPath.toString()); - VirtualContainer participantFolder = new VirtualContainer(participant.getName()); + String participantfoldername = userManager.getUserDisplayName(participant); + VirtualContainer participantFolder = new VirtualContainer(participantfoldername); namedCourseFolder.addItem(participantFolder); if (pfNode.hasParticipantBoxConfigured()){ VFSContainer dropContainer = new NamedContainerImpl(translator.translate("drop.box"), VFSManager.resolveOrCreateContainerFromPath(userBaseContainer, FILENAME_DROPBOX)); + //if coach is also participant, can user his/her webdav folder with participant rights if (identity.equals(participant)){ VFSContainer dropbox = resolveOrCreateDropFolder(courseEnv, pfNode, identity); - VFSSecurityCallback callback = calculateCallback(courseEnv, pfNode, dropbox); + VFSSecurityCallback callback = calculateCallback(courseEnv, pfNode, dropbox, true); dropContainer.setLocalSecurityCallback(callback); } else { dropContainer.setLocalSecurityCallback(new ReadOnlyCallback(nodefolderSubContext)); @@ -387,6 +396,47 @@ public class PFManager { return namedCourseFolder; } + /** + * Provide admin view for webdav, contains all participants of the course. + * + * @param pfNode the pf node + * @param courseEnv the course env + * @return the VFS container + */ + public VFSContainer provideAdminContainer (PFCourseNode pfNode, CourseEnvironment courseEnv) { + Translator translator = Util.createPackageTranslator(PFRunController.class, I18nModule.getDefaultLocale()); + SubscriptionContext nodefolderSubContext = CourseModule.createSubscriptionContext(courseEnv, pfNode); + RepositoryEntry re = courseEnv.getCourseGroupManager().getCourseEntry(); + List<Identity> participants = repositoryEntryRelationDao.getMembers(re, + RepositoryEntryRelationType.both, GroupRoles.participant.name()); + String path = courseEnv.getCourseBaseContainer().getRelPath() + "/" + FILENAME_PARTICIPANTFOLDER; + VFSContainer courseElementBaseContainer = new OlatRootFolderImpl(path, null); + VirtualContainer namedCourseFolder = new VirtualContainer(translator.translate("participant.folder")); + for (Identity participant : participants) { + Path relPath = Paths.get(pfNode.getIdent(), getIdFolderName(participant)); + VFSContainer userBaseContainer = VFSManager.resolveOrCreateContainerFromPath(courseElementBaseContainer, relPath.toString()); + String participantfoldername = userManager.getUserDisplayName(participant); + VirtualContainer participantFolder = new VirtualContainer(participantfoldername); + namedCourseFolder.addItem(participantFolder); + + if (pfNode.hasParticipantBoxConfigured()) { + VFSContainer dropContainer = new NamedContainerImpl(translator.translate("drop.box"), + VFSManager.resolveOrCreateContainerFromPath(userBaseContainer, FILENAME_DROPBOX)); + dropContainer.setLocalSecurityCallback(new ReadWriteDeleteCallback(nodefolderSubContext)); + participantFolder.addItem(dropContainer); + } + + if (pfNode.hasCoachBoxConfigured()){ + VFSContainer returnContainer = new NamedContainerImpl(translator.translate("return.box"), + VFSManager.resolveOrCreateContainerFromPath(userBaseContainer, FILENAME_RETURNBOX)); + returnContainer.setLocalSecurityCallback(new ReadWriteDeleteCallback(nodefolderSubContext)); + participantFolder.addItem(returnContainer); + } + } + return namedCourseFolder; + } + + /** * Provide participant folder in GUI. @@ -398,11 +448,9 @@ public class PFManager { * @param isCoach * @return the VFS container */ - public VFSContainer provideParticipantFolder (PFCourseNode pfNode, PFView pfView, + public VFSContainer provideParticipantFolder (PFCourseNode pfNode, PFView pfView, Translator translator, CourseEnvironment courseEnv, Identity identity, boolean isCoach, boolean readOnly) { - Locale locale = I18nManager.getInstance().getLocaleOrDefault(identity.getUser().getPreferences().getLanguage()); SubscriptionContext nodefolderSubContext = CourseModule.createSubscriptionContext(courseEnv, pfNode); - Translator translator = Util.createPackageTranslator(PFRunController.class, locale); String path = courseEnv.getCourseBaseContainer().getRelPath() + "/" + FILENAME_PARTICIPANTFOLDER; VFSContainer courseElementBaseContainer = new OlatRootFolderImpl(path, null); @@ -442,7 +490,7 @@ public class PFManager { returnContainer.setLocalSecurityCallback(new ReadWriteDeleteCallback(nodefolderSubContext)); } else { VFSContainer dropbox = resolveOrCreateDropFolder(courseEnv, pfNode, identity); - VFSSecurityCallback callback = calculateCallback(courseEnv, pfNode, dropbox); + VFSSecurityCallback callback = calculateCallback(courseEnv, pfNode, dropbox, false); dropContainer.setLocalSecurityCallback(callback); returnContainer.setLocalSecurityCallback(new ReadOnlyCallback(nodefolderSubContext)); } @@ -474,29 +522,29 @@ public class PFManager { * @param pfNode * @param locale * @param courseEnv + * @param admin * @return the participants */ - public List<Identity> getParticipants (Identity id, CourseEnvironment courseEnv) { - CourseGroupManager groupManager = courseEnv.getCourseGroupManager(); - Set<Identity> identitiesSet = new HashSet<>(); - // iterate course members - if (groupManager.isIdentityCourseAdministrator(id) || groupManager.isIdentityCourseAdministrator(id)) { - List<Identity> identities = groupManager.getParticipants(); - for (Identity identity : identities) { - identitiesSet.add(identity); + public List<Identity> getParticipants(Identity id, CourseEnvironment courseEnv, boolean admin) { + Set<Identity> identitySet = new HashSet<>(); + RepositoryEntry re = courseEnv.getCourseGroupManager().getCourseEntry(); + if(admin) { + return repositoryEntryRelationDao.getMembers(re, RepositoryEntryRelationType.both, GroupRoles.participant.name()); + } else { + if(repositoryService.hasRole(id, re, GroupRoles.coach.name())) { + List<Identity> identities = repositoryService.getMembers(re, GroupRoles.participant.name()); + identitySet.addAll(identities); } - } - //iterate group members - List<BusinessGroup> bgroups = groupManager.getOwnedBusinessGroups(id); - if (bgroups != null) { - for (BusinessGroup bgroup : bgroups) { - List<Identity> identities = groupService.getMembers(bgroup, GroupRoles.participant.toString()); - for (Identity identity : identities) { - identitiesSet.add(identity); + + List<BusinessGroup> bgroups = courseEnv.getCourseGroupManager().getOwnedBusinessGroups(id); + if (bgroups != null) { + for (BusinessGroup bgroup : bgroups) { + List<Identity> identities = groupService.getMembers(bgroup, GroupRoles.participant.name()); + identitySet.addAll(identities); } } } - List<Identity> participants = identitiesSet.stream().collect(Collectors.toList()); + List<Identity> participants = identitySet.stream().collect(Collectors.toList()); return participants; } @@ -508,48 +556,32 @@ public class PFManager { * @param userPropertyHandlers * @param locale * @param courseEnv + * @param admin * @return the participants */ public List<DropBoxRow> getParticipants (Identity id, PFCourseNode pfNode, List<UserPropertyHandler> userPropertyHandlers, - Locale locale, CourseEnvironment courseEnv) { - Map<Identity, DropBoxRow> participantsMap = new HashMap<>(); - CourseGroupManager groupManager = courseEnv.getCourseGroupManager(); - // iterate course members - if (groupManager.isIdentityCourseAdministrator(id) || groupManager.isIdentityCourseAdministrator(id)) { - List<Identity> identities = groupManager.getParticipants(); - for (Identity identity : identities) { - VFSContainer dropbox = resolveOrCreateDropFolder(courseEnv, pfNode, identity); - int filecount = countFiles(dropbox); - VFSContainer returnbox = resolveOrCreateReturnFolder(courseEnv, pfNode, identity); - int filecountR = countFiles(returnbox); - Date lastModified = getLastUpdated(courseEnv, pfNode, identity, FILENAME_DROPBOX); - Date lastModifiedR = getLastUpdated(courseEnv, pfNode, identity, FILENAME_RETURNBOX); - UserPropertiesRow urow = new UserPropertiesRow(identity, userPropertyHandlers, locale); - participantsMap.put(identity, new DropBoxRow(urow, "status", - filecount, filecountR, pfNode.getLimitCount(), lastModified, lastModifiedR)); + Locale locale, CourseEnvironment courseEnv, boolean admin) { + List<Identity> identityList = getParticipants(id, courseEnv, admin); + + Set<Identity> duplicates = new HashSet<>(); + List<DropBoxRow> participants = new ArrayList<>(identityList.size()); + for (Identity identity : identityList) { + if(duplicates.contains(identity)) { + continue; } + duplicates.add(identity); + + VFSContainer dropbox = resolveOrCreateDropFolder(courseEnv, pfNode, identity); + int filecount = countFiles(dropbox); + VFSContainer returnbox = resolveOrCreateReturnFolder(courseEnv, pfNode, identity); + int filecountR = countFiles(returnbox); + Date lastModified = getLastUpdated(courseEnv, pfNode, identity, FILENAME_DROPBOX); + Date lastModifiedR = getLastUpdated(courseEnv, pfNode, identity, FILENAME_RETURNBOX); + UserPropertiesRow urow = new UserPropertiesRow(identity, userPropertyHandlers, locale); + participants.add(new DropBoxRow(urow, "status", + filecount, filecountR, pfNode.getLimitCount(), lastModified, lastModifiedR)); } - //iterate group members - List<BusinessGroup> bgroups = groupManager.getOwnedBusinessGroups(id); - if (bgroups != null) { - for (BusinessGroup bgroup : bgroups) { - List<Identity> identities = groupService.getMembers(bgroup, GroupRoles.participant.toString()); - for (Identity identity : identities) { - VFSContainer dropbox = resolveOrCreateDropFolder(courseEnv, pfNode, identity); - int filecount = countFiles(dropbox); - VFSContainer returnbox = resolveOrCreateReturnFolder(courseEnv, pfNode, identity); - int filecountR = countFiles(returnbox); - Date lastModified = getLastUpdated(courseEnv, pfNode, identity, FILENAME_DROPBOX); - Date lastModifiedR = getLastUpdated(courseEnv, pfNode, identity, FILENAME_RETURNBOX); - UserPropertiesRow urow = new UserPropertiesRow(identity, userPropertyHandlers, locale); - participantsMap.put(identity, new DropBoxRow(urow, "status", - filecount, filecountR, pfNode.getLimitCount(), lastModified, lastModifiedR)); - - } - } - } - List<DropBoxRow> participants = new ArrayList<>(participantsMap.values()); return participants; } diff --git a/src/main/java/org/olat/course/nodes/pf/manager/PFNotifications.java b/src/main/java/org/olat/course/nodes/pf/manager/PFNotifications.java index 99d30c3238f..761b131c0f1 100644 --- a/src/main/java/org/olat/course/nodes/pf/manager/PFNotifications.java +++ b/src/main/java/org/olat/course/nodes/pf/manager/PFNotifications.java @@ -94,7 +94,7 @@ public class PFNotifications { this.displayname = entry.getDisplayname(); if (groupManager.isIdentityCourseCoach(identity) || groupManager.isIdentityCourseAdministrator(identity)) { - List<Identity> participants = pfManager.getParticipants(identity, courseEnv); + List<Identity> participants = pfManager.getParticipants(identity, courseEnv, groupManager.isIdentityCourseAdministrator(identity)); for (Identity participant : participants) { gatherItems(participant, p, courseEnv, node); diff --git a/src/main/java/org/olat/course/nodes/pf/manager/PFNotificationsHandler.java b/src/main/java/org/olat/course/nodes/pf/manager/PFNotificationsHandler.java index a2db4064d27..a13852ea4ad 100644 --- a/src/main/java/org/olat/course/nodes/pf/manager/PFNotificationsHandler.java +++ b/src/main/java/org/olat/course/nodes/pf/manager/PFNotificationsHandler.java @@ -57,7 +57,7 @@ import org.springframework.stereotype.Service; public class PFNotificationsHandler implements NotificationsHandler { private static final OLog log = Tracing.createLoggerFor(PFNotificationsHandler.class); - protected static final String CSS_CLASS_ICON = "o_gta_icon"; + protected static final String CSS_CLASS_ICON = "o_pf_icon"; @Autowired private NotificationsManager notificationsManager; diff --git a/src/main/java/org/olat/course/nodes/pf/manager/ReadWriteCallback.java b/src/main/java/org/olat/course/nodes/pf/manager/ReadWriteCallback.java index c06c9a82606..e5eed49f686 100644 --- a/src/main/java/org/olat/course/nodes/pf/manager/ReadWriteCallback.java +++ b/src/main/java/org/olat/course/nodes/pf/manager/ReadWriteCallback.java @@ -48,7 +48,7 @@ public class ReadWriteCallback implements VFSSecurityCallback { @Override public boolean canCreateFolder() { - return false; + return true; } @Override @@ -63,7 +63,7 @@ public class ReadWriteCallback implements VFSSecurityCallback { @Override public boolean canCopy() { - return false; + return true; } @Override diff --git a/src/main/java/org/olat/course/nodes/pf/ui/PFCoachController.java b/src/main/java/org/olat/course/nodes/pf/ui/PFCoachController.java index a70979b8d12..cc6d3842f94 100644 --- a/src/main/java/org/olat/course/nodes/pf/ui/PFCoachController.java +++ b/src/main/java/org/olat/course/nodes/pf/ui/PFCoachController.java @@ -272,8 +272,8 @@ public class PFCoachController extends FormBasicController implements Controller dropboxTable.setSelectAllEnable(true); dropboxTable.setExportEnabled(true); dropboxTable.setAndLoadPersistedPreferences(ureq, this.getClass().getName() + "_" + pfView.name()); - - tableModel.setObjects(pfManager.getParticipants(identity, pfNode, userPropertyHandlers, getLocale(), courseEnv)); + + tableModel.setObjects(pfManager.getParticipants(identity, pfNode, userPropertyHandlers, getLocale(), courseEnv, userCourseEnv.isAdmin())); FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); diff --git a/src/main/java/org/olat/course/nodes/pf/ui/PFEditFormController.java b/src/main/java/org/olat/course/nodes/pf/ui/PFEditFormController.java index c953678293d..6b4df7c3bc3 100644 --- a/src/main/java/org/olat/course/nodes/pf/ui/PFEditFormController.java +++ b/src/main/java/org/olat/course/nodes/pf/ui/PFEditFormController.java @@ -67,19 +67,11 @@ public class PFEditFormController extends FormBasicController { alterFiles = uifactory.addCheckboxesHorizontal("alter.file", "blank.label", formLayout, new String[]{"xx"}, alterfile); alterFiles.addActionListener(FormEvent.ONCLICK); - String[] limitcount = new String[]{ translate("limit.count") }; - limitFileCount = uifactory.addCheckboxesHorizontal("limit.count", "blank.label", formLayout, new String[]{"xx"}, limitcount); - limitFileCount.addActionListener(FormEvent.ONCLICK); - limitFileCount.showLabel(Boolean.FALSE); - fileCount = uifactory.addIntegerElement("file.count", 3, formLayout); - fileCount.showLabel(Boolean.FALSE); - String[] timeframe = new String[]{ translate("time.frame") }; timeFrame = uifactory.addCheckboxesHorizontal("time.frame", "blank.label", formLayout, new String[]{"xx"}, timeframe); timeFrame.addActionListener(FormEvent.ONCLICK); timeFrame.showLabel(Boolean.FALSE); - - + dateStart = new JSDateChooser("dateStart", getLocale()); dateStart.setLabel("date.start", null); dateStart.setDateChooserTimeEnabled(true); @@ -94,6 +86,14 @@ public class PFEditFormController extends FormBasicController { dateEnd.setMandatory(true); formLayout.add(dateEnd); + String[] limitcount = new String[]{ translate("limit.count") }; + limitFileCount = uifactory.addCheckboxesHorizontal("limit.count", "blank.label", formLayout, new String[]{"xx"}, limitcount); + limitFileCount.addActionListener(FormEvent.ONCLICK); + limitFileCount.showLabel(Boolean.FALSE); + fileCount = uifactory.addIntegerElement("file.count", 3, formLayout); + fileCount.showLabel(Boolean.FALSE); + fileCount.setHelpTextKey("limit.count.coach.info", null); + spacerEl = uifactory.addSpacerElement("spacer1", formLayout, false); teacherDropBox = uifactory.addCheckboxesHorizontal("coach.drop", formLayout, new String[]{"xx"}, new String[]{null}); @@ -137,7 +137,9 @@ public class PFEditFormController extends FormBasicController { activateSettings(); } else if (source == limitFileCount) { activateFileCount(); - showInfo("limit.count.coach.info"); + if (limitFileCount.isSelected(0)) { + showInfo("limit.count.coach.info"); + } } else if (source == timeFrame) { activateTimeFrame(); } @@ -199,6 +201,8 @@ public class PFEditFormController extends FormBasicController { protected void formOK(UserRequest ureq) { if (!checkTimeFrameValid()) { dateEnd.setErrorKey("timeframe.error", null); + } else if (1 > fileCount.getIntValue()) { + fileCount.setErrorKey("filecount.error", null); } else if (!(studentDropBox.isSelected(0) || teacherDropBox.isSelected(0))) { showError("folderselection.error"); } else { diff --git a/src/main/java/org/olat/course/nodes/pf/ui/PFParticipantController.java b/src/main/java/org/olat/course/nodes/pf/ui/PFParticipantController.java index 37235020045..1606e1562c8 100644 --- a/src/main/java/org/olat/course/nodes/pf/ui/PFParticipantController.java +++ b/src/main/java/org/olat/course/nodes/pf/ui/PFParticipantController.java @@ -79,7 +79,8 @@ public class PFParticipantController extends BasicController { //CourseFreeze readOnly = readOnly ? true : userCourseEnv.isCourseReadOnly(); - VFSContainer frc = pfManager.provideParticipantFolder(pfNode, pfView, courseEnv, identity, isCoach, readOnly); + VFSContainer frc = pfManager.provideParticipantFolder(pfNode, pfView, getTranslator(),courseEnv, + identity, isCoach, readOnly); folderRunController = new FolderRunController(frc, false, false, false, false, ureq, wControl, null, null, null); folderRunController.disableSubscriptionController(); listenTo(folderRunController); diff --git a/src/main/java/org/olat/course/nodes/pf/ui/PFPeekviewController.java b/src/main/java/org/olat/course/nodes/pf/ui/PFPeekviewController.java index 3f19fb01faa..a546bf56a00 100644 --- a/src/main/java/org/olat/course/nodes/pf/ui/PFPeekviewController.java +++ b/src/main/java/org/olat/course/nodes/pf/ui/PFPeekviewController.java @@ -22,6 +22,7 @@ package org.olat.course.nodes.pf.ui; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.List; import org.olat.core.CoreSpringFactory; @@ -86,7 +87,7 @@ public class PFPeekviewController extends BasicController implements Controller // Add link to jump to course node if (leaf instanceof LocalFileImpl) { DownloadComponent dlComp = new DownloadComponent("nodeLinkDL_"+(i+1), leaf, forceDownload, - leaf.getName(), translate("preview.downloadfile"), + leaf.getName() + " " + new Date(leaf.getLastModified()), translate("peekview.downloadfile"), CSSHelper.createFiletypeIconCssClassFor(leaf.getName())); dlComp.setElementCssClass("o_gotoNode"); peekviewVC.put("nodeLinkDL_"+(i+1),dlComp); diff --git a/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_de.properties index eba24c12dd5..187b3dff5b6 100644 --- a/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_de.properties @@ -1,3 +1,4 @@ +participant.folder=Teilnehmer Ordner participant.drop=Teilnehmer Abgabeordner aktivieren coach.drop=Betreuer R\u00FCckgabeordner aktivieren drop.box=Teilnehmer Abgabeordner @@ -40,9 +41,11 @@ notifications.entry.by=von notifications.header=Teilnehmer des Kurses "{0}" haben neue Dokumente eingestellt. timeframe.error=Bitte \u00FCberpr\u00FCfen Sie ob das limitierende Zeitfenster korrekt ist. folderselection.error=Bitte aktivieren Sie mindestens einen der beiden Ordner. +filecount.error=Bitte erlauben Sie mindestens einen Hochladevorgang. condition.accessibility.title=Zugang upload.success=Es wurden Dokumente eingestellt peekview.allItemsLink=Alle Dokumente +peekview.downloadfile=Dokument herunterladen preview.drop=Teilnehmer Abgabeordner aktiviert preview.return=Betreuer R\u00FCckgabeordner aktiviert preview.limit=Dokumentenlimite aktiviert diff --git a/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_en.properties index eb3fabeafd1..0557e658c03 100644 --- a/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_en.properties @@ -1,3 +1,4 @@ +participant.folder=Participant folder participant.drop=Enable Participant Drop Box coach.drop=Enable Coach Return Box drop.box=Participant Drop Box @@ -40,9 +41,11 @@ notifications.entry.by=by notifications.header=Participants of of the course "{0}" uploaded new files. timeframe.error=Please check if your time interval is valid! folderselection.error=Please enable at least one folder. +filecount.error=Please allow at least one upload. condition.accessibility.title=Access Control upload.success=Files have been uploaded peekview.allItemsLink=All documents +peekview.downloadfile=Download file preview.drop=Participant Dropbox enabled preview.return=Coach Returnbox enabled preview.limit=Filelimit active diff --git a/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_fr.properties index 6ee0809afb6..52298a9c5ca 100644 --- a/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/course/nodes/pf/ui/_i18n/LocalStrings_fr.properties @@ -1,4 +1,5 @@ -#Thu Jan 12 18:48:48 CET 2017 +#Wed Jan 11 22:04:13 CET 2017 +participant.folder=Dossier de participant alter.file=Autoriser l'effacement et la r\u00E9\u00E9criture des documents blank.label= coach.drop=Activ\u00E9 le dossier du coach @@ -26,6 +27,7 @@ pane.tab.accessibility=Acc\u00E8s pane.tab.configuration=Configuration des dossiers participant.drop=Activer bo\u00EEte de d\u00E9p\u00F4t peekview.allItemsLink=Tous les documents +peekview.downloadfile=T\u00E9tl\u00E9tcharger un fichier preview.drop=Bo\u00EEte de d\u00E9p\u00F4t activ\u00E9e preview.header=Configuration du dossier pour l'utilisateur simul\u00E9 preview.info=Un exemple se trouve ci-dessous\: @@ -47,6 +49,7 @@ table.no.selection=Vous n'avez pas s\u00E9lectionn\u00E9 d'utilisateur. textfield.upload=T\u00E9l\u00E9charger un fichier time.frame=Limiter la p\u00E9riode de t\u00E9l\u00E9chargement timeframe.error=V\u00E9rifier que l'intervalle de temps sont correct. +filecount.error=Veuillez autoriser au moins un t\u00E9l\u00E9chargement. upload.link=T\u00E9l\u00E9charger en masse upload.success=Des fichiers ont \u00E9t\u00E9 t\u00E9l\u00E9charg\u00E9s valid.date=Entrez s'il vous pla\u00EEt une date valide. diff --git a/src/test/java/org/olat/course/nodes/pf/manager/PFManagerTest.java b/src/test/java/org/olat/course/nodes/pf/manager/PFManagerTest.java index e95170aacd9..a9fa64fd5fc 100644 --- a/src/test/java/org/olat/course/nodes/pf/manager/PFManagerTest.java +++ b/src/test/java/org/olat/course/nodes/pf/manager/PFManagerTest.java @@ -83,7 +83,7 @@ public class PFManagerTest extends OlatTestCase { Identity check3 = JunitTestHelper.createAndPersistIdentityAsRndUser("check-3"); repositoryEntryRelationDao.addRole(check3, entry, GroupRoles.participant.name()); - VFSContainer vfsContainer = pfManager.provideCoachOrParticipantContainer(pfNode, userCourseEnv, check3); + VFSContainer vfsContainer = pfManager.provideCoachOrParticipantContainer(pfNode, userCourseEnv, check3, false); Assert.assertNotNull(vfsContainer); Assert.assertTrue(vfsContainer.exists()); } @@ -107,7 +107,7 @@ public class PFManagerTest extends OlatTestCase { Identity check4 = JunitTestHelper.createAndPersistIdentityAsRndUser("check-4"); repositoryEntryRelationDao.addRole(check4, entry, GroupRoles.coach.name()); - VFSContainer vfsContainer = pfManager.provideCoachOrParticipantContainer(pfNode, userCourseEnv, check4); + VFSContainer vfsContainer = pfManager.provideCoachOrParticipantContainer(pfNode, userCourseEnv, check4, false); Assert.assertNotNull(vfsContainer); } @@ -203,7 +203,7 @@ public class PFManagerTest extends OlatTestCase { dbInstance.commitAndCloseSession(); - List<Identity> ids = pfManager.getParticipants(initialAuthor, courseEnv); + List<Identity> ids = pfManager.getParticipants(initialAuthor, courseEnv, true); //check Assert.assertEquals(ids.size(), 5); Assert.assertFalse(ids.contains(initialAuthor)); -- GitLab