diff --git a/src/main/java/org/olat/core/commons/services/commentAndRating/manager/CommentAndRatingServiceImpl.java b/src/main/java/org/olat/core/commons/services/commentAndRating/manager/CommentAndRatingServiceImpl.java index 22f1e189f3aee6e68073f0d3c560b13230e58015..bd81c47caa7b14c1e8692273678a02391d0e74bd 100644 --- a/src/main/java/org/olat/core/commons/services/commentAndRating/manager/CommentAndRatingServiceImpl.java +++ b/src/main/java/org/olat/core/commons/services/commentAndRating/manager/CommentAndRatingServiceImpl.java @@ -31,9 +31,6 @@ import org.olat.core.commons.services.notifications.NotificationsManager; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.logging.AssertException; -import org.olat.core.logging.OLog; -import org.olat.core.logging.Tracing; -import org.olat.user.UserDataDeletable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -49,9 +46,7 @@ import org.springframework.stereotype.Service; * @author gnaegi */ @Service -public class CommentAndRatingServiceImpl implements CommentAndRatingService, UserDataDeletable { - - private static final OLog log = Tracing.createLoggerFor(CommentAndRatingServiceImpl.class); +public class CommentAndRatingServiceImpl implements CommentAndRatingService { @Autowired private UserRatingsDAO userRatingsDao; @@ -173,14 +168,6 @@ public class CommentAndRatingServiceImpl implements CommentAndRatingService, Use return delCount; } - @Override - public void deleteUserData(Identity identity, String newDeletedUserName) { - int rows = userRatingsDao.deleteRatings(identity); - log.audit(rows + " rating deleted"); - int comments = userCommentsDao.deleteAllComments(identity); - log.audit(comments + " rating erased"); - } - private void markPublisherNews(UserComment comment) { if (comment == null) return; diff --git a/src/main/java/org/olat/core/commons/services/commentAndRating/manager/CommentAndRatingUserDataManager.java b/src/main/java/org/olat/core/commons/services/commentAndRating/manager/CommentAndRatingUserDataManager.java new file mode 100644 index 0000000000000000000000000000000000000000..340cf012054babdb1da12c5e1781c11adc29a8e5 --- /dev/null +++ b/src/main/java/org/olat/core/commons/services/commentAndRating/manager/CommentAndRatingUserDataManager.java @@ -0,0 +1,225 @@ +/** + * <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.core.commons.services.commentAndRating.manager; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Locale; + +import org.olat.core.commons.persistence.DB; +import org.olat.core.commons.services.commentAndRating.model.UserComment; +import org.olat.core.commons.services.commentAndRating.model.UserRating; +import org.olat.core.id.Identity; +import org.olat.core.id.OLATResourceable; +import org.olat.core.id.context.BusinessControlFactory; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.StringHelper; +import org.olat.core.util.openxml.OpenXMLWorkbook; +import org.olat.core.util.openxml.OpenXMLWorksheet; +import org.olat.core.util.openxml.OpenXMLWorksheet.Row; +import org.olat.core.util.resource.OresHelper; +import org.olat.modules.portfolio.Binder; +import org.olat.modules.portfolio.Page; +import org.olat.modules.portfolio.manager.PageDAO; +import org.olat.modules.qpool.QuestionItem; +import org.olat.modules.qpool.manager.QuestionItemDAO; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryManager; +import org.olat.user.UserDataDeletable; +import org.olat.user.UserDataExportable; +import org.olat.user.manager.ManifestBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * Initial date: 28 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +@Service +public class CommentAndRatingUserDataManager implements UserDataDeletable, UserDataExportable { + + private static final OLog log = Tracing.createLoggerFor(CommentAndRatingUserDataManager.class); + + @Autowired + private DB dbInstance; + @Autowired + private PageDAO pageDao; + @Autowired + private QuestionItemDAO questionItemDao; + @Autowired + private UserRatingsDAO userRatingsDao; + @Autowired + private UserCommentsDAO userCommentsDao; + @Autowired + private RepositoryManager repositoryManager; + + @Override + public void deleteUserData(Identity identity, String newDeletedUserName) { + int rows = userRatingsDao.deleteRatings(identity); + log.audit(rows + " rating deleted"); + int comments = userCommentsDao.deleteAllComments(identity); + log.audit(comments + " rating erased"); + } + + @Override + public String getExporterID() { + return "comments.ratings"; + } + + @Override + public void export(Identity identity, ManifestBuilder manifest, File archiveDirectory, Locale locale) { + exportRatings(identity, manifest, archiveDirectory); + exportComments(identity, manifest, archiveDirectory); + } + + private void exportComments(Identity identity, ManifestBuilder manifest, File archiveDirectory) { + List<UserComment> comments = userCommentsDao.getComments(identity); + if(comments == null || comments.isEmpty()) return; + dbInstance.commitAndCloseSession(); + + File noteArchive = new File(archiveDirectory, "Comments.xlsx"); + try(OutputStream out = new FileOutputStream(noteArchive); + OpenXMLWorkbook workbook = new OpenXMLWorkbook(out, 1)) { + OpenXMLWorksheet sheet = workbook.nextWorksheet(); + sheet.setHeaderRows(1); + + Row header = sheet.newRow(); + header.addCell(0, "Comment"); + header.addCell(1, "Resource"); + header.addCell(2, "URL"); + + for(UserComment comment:comments) { + Row row = sheet.newRow(); + row.addCell(0, comment.getComment()); + Location location = resolveLocation(comment.getResName(), comment.getResId()); + if(StringHelper.containsNonWhitespace(location.getName())) { + row.addCell(1, location.getName()); + } + if(StringHelper.containsNonWhitespace(location.getUrl())) { + row.addCell(2, location.getUrl()); + } + } + } catch (IOException e) { + log.error("Unable to export xlsx", e); + } + manifest.appendFile("Bookings.xlsx"); + } + + private void exportRatings(Identity identity, ManifestBuilder manifest, File archiveDirectory) { + List<UserRating> ratings = userRatingsDao.getAllRatings(identity); + if(ratings == null || ratings.isEmpty()) return; + dbInstance.commitAndCloseSession(); + + File ratingsArchive = new File(archiveDirectory, "Ratings.xlsx"); + try(OutputStream out = new FileOutputStream(ratingsArchive); + OpenXMLWorkbook workbook = new OpenXMLWorkbook(out, 1)) { + OpenXMLWorksheet sheet = workbook.nextWorksheet(); + sheet.setHeaderRows(1); + + Row header = sheet.newRow(); + header.addCell(0, "Rating"); + header.addCell(1, "Resource"); + header.addCell(2, "URL"); + + for(UserRating rating:ratings) { + Row row = sheet.newRow(); + row.addCell(0, rating.getRating().toString()); + Location location = resolveLocation(rating.getResName(), rating.getResId()); + if(StringHelper.containsNonWhitespace(location.getName())) { + row.addCell(1, location.getName()); + } + if(StringHelper.containsNonWhitespace(location.getUrl())) { + row.addCell(2, location.getUrl()); + } + } + } catch (IOException e) { + log.error("Unable to export xlsx", e); + } + manifest.appendFile("Bookings.xlsx"); + } + + private Location resolveLocation (String resName, Long resId) { + String name = null; + String businessPath = null; + if("RepositoryEntry".equals(resName)) { + RepositoryEntry entry = repositoryManager.lookupRepositoryEntry(resId, false); + if(entry != null) { + name = entry.getDisplayname(); + businessPath = "[RepositoryEntry:" + entry.getKey() + "]"; + } + } else if("QuestionItem".equals(resName)) { + QuestionItem item = questionItemDao.loadById(resId); + if(item != null) { + name = item.getTitle(); + businessPath = "[QPool:0][QuestionItem:" + item.getKey() + "]"; + } + } else if("Page".equals(resName)) { + Page page = pageDao.loadByKey(resId); + if(page != null) { + name = page.getTitle(); + if(page.getSection() != null) { + Binder binder = page.getSection().getBinder(); + businessPath = "[PortfolioV2:0][Binder:" + binder.getKey() + "][Entries:0][Entry:" + page.getKey() + "]"; + } else { + businessPath = "[PortfolioV2:0][MyPages:0][Entry:" + page.getKey() + "]"; + } + } + } else if("EPDefaultMap".equals(resName)) { + name = "Mappe (v 1.0)"; + } else if("LibrarySite".equals(resName)) { + name = "Library"; + } else { + OLATResourceable resourceable = OresHelper.createOLATResourceableInstance(resName, resId); + RepositoryEntry entry = repositoryManager.lookupRepositoryEntry(resourceable, false); + if(entry != null) { + name = entry.getDisplayname(); + businessPath = "[RepositoryEntry:" + entry.getKey() + "]"; + } + } + + String url = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath); + return new Location(name, url); + } + + private static class Location { + + private final String name; + private final String url; + + public Location(String name, String url) { + this.url = url; + this.name = name; + } + + public String getName() { + return name; + } + + public String getUrl() { + return url; + } + } +} diff --git a/src/main/java/org/olat/core/commons/services/commentAndRating/manager/UserCommentsDAO.java b/src/main/java/org/olat/core/commons/services/commentAndRating/manager/UserCommentsDAO.java index 80d50d90d91b29b555764c6d4851ded622f3a3a6..1cc11bab16bc910ea22de2c238582534b092102a 100644 --- a/src/main/java/org/olat/core/commons/services/commentAndRating/manager/UserCommentsDAO.java +++ b/src/main/java/org/olat/core/commons/services/commentAndRating/manager/UserCommentsDAO.java @@ -30,7 +30,6 @@ import javax.persistence.TypedQuery; import org.olat.basesecurity.IdentityRef; import org.olat.core.commons.persistence.DB; -import org.olat.core.commons.persistence.DBFactory; import org.olat.core.commons.services.commentAndRating.CommentAndRatingLoggingAction; import org.olat.core.commons.services.commentAndRating.UserCommentsDelegate; import org.olat.core.commons.services.commentAndRating.model.UserComment; @@ -104,10 +103,10 @@ public class UserCommentsDAO { TypedQuery<UserComment> query; if (resSubPath == null) { // special query when sub path is null - query = DBFactory.getInstance().getCurrentEntityManager() + query = dbInstance.getCurrentEntityManager() .createQuery("select comment from usercomment as comment where resName=:resname AND resId=:resId AND resSubPath is NULL", UserComment.class); } else { - query = DBFactory.getInstance().getCurrentEntityManager() + query = dbInstance.getCurrentEntityManager() .createQuery("select comment from usercomment as comment where resName=:resname AND resId=:resId AND resSubPath=:resSubPath", UserComment.class) .setParameter("resSubPath", resSubPath); } @@ -115,6 +114,14 @@ public class UserCommentsDAO { .setParameter("resId", ores.getResourceableId()) .getResultList(); } + + public List<UserComment> getComments(IdentityRef identity) { + String query = "select comment from usercomment as comment where comment.creator.key=:identityKey"; + return dbInstance.getCurrentEntityManager() + .createQuery(query, UserComment.class) + .setParameter("identityKey", identity.getKey()) + .getResultList(); + } public UserComment updateComment(UserComment comment, String newCommentText) { // First reload parent from cache to prevent stale object or cache issues @@ -200,10 +207,10 @@ public class UserCommentsDAO { TypedQuery<Number> query; if (resSubPath == null) { // special query when sub path is null - query = DBFactory.getInstance().getCurrentEntityManager() + query = dbInstance.getCurrentEntityManager() .createQuery("select count(*) from usercomment where resName=:resname AND resId=:resId AND resSubPath is NULL", Number.class); } else { - query = DBFactory.getInstance().getCurrentEntityManager() + query = dbInstance.getCurrentEntityManager() .createQuery("select count(*) from usercomment where resName=:resname AND resId=:resId AND resSubPath=:resSubPath", Number.class) .setParameter("resSubPath", resSubPath); } diff --git a/src/main/java/org/olat/core/commons/services/commentAndRating/manager/UserRatingsDAO.java b/src/main/java/org/olat/core/commons/services/commentAndRating/manager/UserRatingsDAO.java index eb265ee4e36f79705037db35c962486430c25bb8..be36e55e9acf04720df0ac67bdfdb7cad9701c60 100644 --- a/src/main/java/org/olat/core/commons/services/commentAndRating/manager/UserRatingsDAO.java +++ b/src/main/java/org/olat/core/commons/services/commentAndRating/manager/UserRatingsDAO.java @@ -85,6 +85,14 @@ public class UserRatingsDAO { .setHint("org.hibernate.cacheable", Boolean.TRUE) .getResultList(); } + + public List<UserRating> getAllRatings(IdentityRef identity) { + String sb = "select rating from userrating as rating where rating.creator.key=:identityKey"; + return dbInstance.getCurrentEntityManager() + .createQuery(sb, UserRating.class) + .setParameter("identityKey", identity.getKey()) + .getResultList(); + } public float getRatingAverage(OLATResourceable ores, String resSubPath) { StringBuilder sb = new StringBuilder(); diff --git a/src/main/java/org/olat/core/gui/DefaultGlobalSettings.java b/src/main/java/org/olat/core/gui/DefaultGlobalSettings.java index e0c340647916554bc81d4e546dfedf1376e86e70..50f569e62010316ff6306c9cc4ecd8a8cc9b885e 100644 --- a/src/main/java/org/olat/core/gui/DefaultGlobalSettings.java +++ b/src/main/java/org/olat/core/gui/DefaultGlobalSettings.java @@ -1,3 +1,22 @@ +/** + * <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.core.gui; import org.olat.core.gui.control.winmgr.AJAXFlags; diff --git a/src/main/java/org/olat/core/util/FileUtils.java b/src/main/java/org/olat/core/util/FileUtils.java index 62a71d5060545fa0e36729d7040b83f42a88d2d0..e417066549ea1c414cbd85e8f1e481dec1beb797 100644 --- a/src/main/java/org/olat/core/util/FileUtils.java +++ b/src/main/java/org/olat/core/util/FileUtils.java @@ -50,11 +50,14 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.io.IOUtils; import org.olat.core.logging.AssertException; import org.olat.core.logging.OLATRuntimeException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; +import org.olat.core.util.vfs.VFSContainer; +import org.olat.core.util.vfs.VFSItem; +import org.olat.core.util.vfs.VFSLeaf; +import org.olat.core.util.vfs.filters.SystemItemFilter; import org.springframework.core.io.Resource; @@ -136,6 +139,35 @@ public class FileUtils { public static boolean copyFileToDir(File sourceFile, File targetDir, String wt) { return copyFileToDir(sourceFile, targetDir, false, null, wt); } + + /** + * + * @param source The source + * @param targetDirectory The directory to copy the source in + * @param wt A message + * @return True if ok + */ + public static boolean copyItemToDir(VFSItem source, File targetDirectory, String wt) { + targetDirectory.mkdirs(); + + File target = new File(targetDirectory, source.getName()); + if(source instanceof VFSLeaf) { + try(InputStream inStream = ((VFSLeaf)source).getInputStream(); + OutputStream outStream = new FileOutputStream(target)) { + bcopy(inStream, outStream, wt); + } catch(IOException ex) { + log.error("", ex); + return false; + } + } else if(source instanceof VFSContainer) { + target.mkdir(); + List<VFSItem> items = ((VFSContainer)source).getItems(new SystemItemFilter()); + for(VFSItem item:items) { + copyItemToDir(item, target, wt); + } + } + return true; + } /** * @param sourceFile @@ -403,23 +435,20 @@ public class FileUtils { return true; } // end copy - public static boolean copyToFile(InputStream in, File targetFile, String wt) { + public static boolean copyToFile(InputStream in, File targetFile, String wt) throws IOException { if (targetFile.isDirectory()) return false; // create target directories targetFile.getParentFile().mkdirs(); // don't check for success... would return false on - - BufferedInputStream bis = new BufferedInputStream(in); - try (OutputStream dst = new FileOutputStream(targetFile); + + try(BufferedInputStream bis = new BufferedInputStream(in); + OutputStream dst = new FileOutputStream(targetFile); BufferedOutputStream bos = getBos (dst)) { cpio (bis, bos, wt); bos.flush(); return true; } catch (IOException e) { - throw new RuntimeException("I/O error in cpio "+wt); - } finally { - IOUtils.closeQuietly(bis); - IOUtils.closeQuietly(in); + throw e; } } diff --git a/src/main/java/org/olat/course/nodes/gta/GTAManager.java b/src/main/java/org/olat/course/nodes/gta/GTAManager.java index 0499a8f79fd9f8c63b5e1e6ad4a3e6f8cbda6484..0140879b7c4ff4ed11ff0e03dd7aafa29f15941c 100644 --- a/src/main/java/org/olat/course/nodes/gta/GTAManager.java +++ b/src/main/java/org/olat/course/nodes/gta/GTAManager.java @@ -271,6 +271,8 @@ public interface GTAManager { public List<Task> getTasks(TaskList taskList, GTACourseNode gtaNode); + public List<Task> getTasks(IdentityRef identity); + public List<TaskLight> getTasksLight(RepositoryEntryRef entry, GTACourseNode gtaNode); public List<TaskRevisionDate> getTaskRevisions(Task task); diff --git a/src/main/java/org/olat/course/nodes/gta/manager/GTAManagerImpl.java b/src/main/java/org/olat/course/nodes/gta/manager/GTAManagerImpl.java index c0150d07c9a2cbfb46491ee9aea7e60bcb9191e0..f871cb355a65bac4aec6a8bf4076eceb0381e7e1 100644 --- a/src/main/java/org/olat/course/nodes/gta/manager/GTAManagerImpl.java +++ b/src/main/java/org/olat/course/nodes/gta/manager/GTAManagerImpl.java @@ -90,7 +90,6 @@ import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupRef; import org.olat.group.BusinessGroupService; -import org.olat.group.DeletableGroupData; import org.olat.group.area.BGAreaManager; import org.olat.group.manager.BusinessGroupRelationDAO; import org.olat.group.model.BusinessGroupRefImpl; @@ -117,7 +116,7 @@ import com.thoughtworks.xstream.XStream; * */ @Service -public class GTAManagerImpl implements GTAManager, DeletableGroupData { +public class GTAManagerImpl implements GTAManager { private static final OLog log = Tracing.createLoggerFor(GTAManagerImpl.class); @@ -735,17 +734,6 @@ public class GTAManagerImpl implements GTAManager, DeletableGroupData { return tasks.isEmpty() ? null : tasks.get(0); } - - @Override - public boolean deleteGroupDataFor(BusinessGroup group) { - log.audit("Delete tasks of business group: " + group.getKey()); - String deleteTasks = "delete from gtatask as task where task.businessGroup.key=:groupKey"; - dbInstance.getCurrentEntityManager() - .createQuery(deleteTasks) - .setParameter("groupKey", group.getKey()) - .executeUpdate(); - return true; - } @Override public int deleteTaskList(RepositoryEntryRef entry, GTACourseNode cNode) { @@ -813,6 +801,19 @@ public class GTAManagerImpl implements GTAManager, DeletableGroupData { .setParameter("taskListKey", taskList.getKey()) .getResultList(); } + + @Override + public List<Task> getTasks(IdentityRef identity) { + StringBuilder sb = new StringBuilder(); + sb.append("select task from gtatask task") + .append(" inner join task.taskList as tasklist") + .append(" inner join tasklist.entry as entry") + .append(" where task.identity.key=:identityKey"); + return dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Task.class) + .setParameter("identityKey", identity.getKey()) + .getResultList(); + } @Override public List<TaskLight> getTasksLight(RepositoryEntryRef entry, GTACourseNode gtaNode) { diff --git a/src/main/java/org/olat/course/nodes/gta/manager/GTAUserDataManager.java b/src/main/java/org/olat/course/nodes/gta/manager/GTAUserDataManager.java new file mode 100644 index 0000000000000000000000000000000000000000..9c5d1962622009499d30c9bfe540907d41ff3771 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/gta/manager/GTAUserDataManager.java @@ -0,0 +1,140 @@ +/** + * <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.gta.manager; + +import java.io.File; +import java.util.List; +import java.util.Locale; + +import org.olat.core.commons.persistence.DB; +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.StringHelper; +import org.olat.core.util.io.SystemFileFilter; +import org.olat.course.CorruptedCourseException; +import org.olat.course.CourseFactory; +import org.olat.course.ICourse; +import org.olat.course.nodes.CourseNode; +import org.olat.course.nodes.GTACourseNode; +import org.olat.course.nodes.gta.GTAManager; +import org.olat.course.nodes.gta.Task; +import org.olat.course.nodes.gta.TaskList; +import org.olat.group.BusinessGroup; +import org.olat.group.DeletableGroupData; +import org.olat.modules.ModuleConfiguration; +import org.olat.user.UserDataExportable; +import org.olat.user.manager.ManifestBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * Initial date: 28 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +@Service +public class GTAUserDataManager implements DeletableGroupData, UserDataExportable { + + private static final OLog log = Tracing.createLoggerFor(GTAUserDataManager.class); + + @Autowired + private DB dbInstance; + @Autowired + private GTAManager gtaManager; + + @Override + public boolean deleteGroupDataFor(BusinessGroup group) { + log.audit("Delete tasks of business group: " + group.getKey()); + String deleteTasks = "delete from gtatask as task where task.businessGroup.key=:groupKey"; + dbInstance.getCurrentEntityManager() + .createQuery(deleteTasks) + .setParameter("groupKey", group.getKey()) + .executeUpdate(); + return true; + } + + + @Override + public String getExporterID() { + return "gta"; + } + + @Override + public void export(Identity identity, ManifestBuilder manifest, File archiveDirectory, Locale locale) { + File tasksArchiveDir = new File(archiveDirectory, "Tasks"); + tasksArchiveDir.mkdir(); + + List<Task> tasks = gtaManager.getTasks(identity); + for(Task task:tasks) { + try { + TaskList taskList = task.getTaskList(); + ICourse course = CourseFactory.loadCourse(taskList.getEntry()); + if(course == null) continue; + + CourseNode node = course.getRunStructure().getNode(taskList.getCourseNodeIdent()); + if(node instanceof GTACourseNode) { + String archiveName = node.getIdent() + "_" + StringHelper.transformDisplayNameToFileSystemName(node.getShortName()); + File taskArchiveDir = new File(tasksArchiveDir, archiveName); + exportTask(identity, task, (GTACourseNode)node, course, taskArchiveDir); + } + } catch (CorruptedCourseException e) { + log.warn("", e); + } + } + } + + private void exportTask(Identity assessedIdentity, Task task, GTACourseNode node , ICourse course, File taskArchiveDir) { + int flow = 0;//for beautiful ordering + ModuleConfiguration config = node.getModuleConfiguration(); + if(config.getBooleanSafe(GTACourseNode.GTASK_SUBMIT)) { + File submitDirectory = gtaManager.getSubmitDirectory(course.getCourseEnvironment(), node, assessedIdentity); + String submissionDirName = (++flow) + "_submissions"; + copyDirContentToDir(submitDirectory, new File(taskArchiveDir, submissionDirName)); + } + + if(config.getBooleanSafe(GTACourseNode.GTASK_REVIEW_AND_CORRECTION)) { + File correctionsDir = gtaManager.getCorrectionDirectory(course.getCourseEnvironment(), node, assessedIdentity); + String correctionDirName = (++flow) + "_corrections"; + copyDirContentToDir(correctionsDir, new File(taskArchiveDir, correctionDirName)); + } + + if(task != null && config.getBooleanSafe(GTACourseNode.GTASK_REVISION_PERIOD)) { + int numOfIteration = task.getRevisionLoop(); + for(int i=1; i<=numOfIteration; i++) { + File revisionDirectory = gtaManager.getRevisedDocumentsDirectory(course.getCourseEnvironment(), node, i, assessedIdentity); + String revisionDirName = (++flow) + "_revisions_" + i; + copyDirContentToDir(revisionDirectory, new File(taskArchiveDir, revisionDirName)); + } + } + } + + private void copyDirContentToDir(File source, File target) { + File[] sourceFiles = source.listFiles(new SystemFileFilter(true, true)); + if(sourceFiles != null && sourceFiles.length > 0) { + target.mkdirs(); + for(File sourceFile:sourceFiles) { + FileUtils.copyFileToDir(sourceFile, target, ""); + } + } + } +} diff --git a/src/main/java/org/olat/course/statistic/export/UserDataLogExporter.java b/src/main/java/org/olat/course/statistic/export/UserDataLogExporter.java new file mode 100644 index 0000000000000000000000000000000000000000..415bd35593f01ef429e21013281dd38971620801 --- /dev/null +++ b/src/main/java/org/olat/course/statistic/export/UserDataLogExporter.java @@ -0,0 +1,124 @@ +/** + * <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.statistic.export; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Locale; + +import javax.persistence.EntityManager; + +import org.olat.core.commons.persistence.DB; +import org.olat.core.id.Identity; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.logging.activity.LoggingObject; +import org.olat.core.util.openxml.OpenXMLWorkbook; +import org.olat.core.util.openxml.OpenXMLWorksheet; +import org.olat.core.util.openxml.OpenXMLWorksheet.Row; +import org.olat.user.UserDataExportable; +import org.olat.user.manager.ManifestBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * Initial date: 29 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +@Service +public class UserDataLogExporter implements UserDataExportable { + + private static final OLog log = Tracing.createLoggerFor(UserDataLogExporter.class); + + @Autowired + private DB dbInstance; + + @Override + public String getExporterID() { + return "logs"; + } + + @Override + public void export(Identity identity, ManifestBuilder manifest, File archiveDirectory, Locale locale) { + File profileArchive = new File(archiveDirectory, "Log.xlsx"); + try(OutputStream out = new FileOutputStream(profileArchive); + OpenXMLWorkbook workbook = new OpenXMLWorkbook(out, 1)) { + OpenXMLWorksheet sheet = workbook.nextWorksheet(); + + Row header = sheet.newRow(); + header.addCell(0, "Date"); + header.addCell(1, "Action"); + header.addCell(2, "Verb"); + header.addCell(3, "Object"); + header.addCell(4, "Duration"); + header.addCell(5, "Administrative"); + header.addCell(6, "Path"); + header.addCell(7, "Target name"); + header.addCell(8, "Target type"); + header.addCell(9, "Parent name"); + header.addCell(10, "Parent type"); + + EntityManager em = dbInstance.getCurrentEntityManager(); + em.clear(); + + String query = "select v from loggingobject as v where v.userId=:identityKey"; + List<LoggingObject> queryResult = em.createQuery(query, LoggingObject.class) + .setParameter("identityKey", identity.getKey()) + .getResultList(); + int count = 0; + for (LoggingObject loggingObject : queryResult) { + + Row row = sheet.newRow(); + row.addCell(0, loggingObject.getCreationDate(), workbook.getStyles().getDateTimeStyle()); + row.addCell(1, loggingObject.getActionCrudType()); + row.addCell(2, loggingObject.getActionVerb()); + row.addCell(3, loggingObject.getActionObject()); + row.addCell(4, String.valueOf(loggingObject.getSimpleDuration())); + if(loggingObject.getResourceAdminAction() != null) { + row.addCell(5, loggingObject.getResourceAdminAction().toString()); + } + row.addCell(6, loggingObject.getBusinessPath()); + writeData(loggingObject.getTargetResName(), loggingObject.getTargetResType(), identity, row, 7); + writeData(loggingObject.getParentResName(), loggingObject.getParentResType(), identity, row, 9); + + if(count++ % 1000 == 0) { + out.flush(); + em.clear(); + } + } + } catch (IOException e) { + log.error("Unable to export xlsx", e); + } + manifest.appendFile(profileArchive.getName()); + } + + private void writeData(String resName, String resType, Identity identity, Row row, int col) { + row.addCell(col, resType); + if("targetIdentity".equals(resType)) { + resName = resName.equals(identity.getName()) ? resName : ""; + } + row.addCell(col + 1, resName); + } +} diff --git a/src/main/java/org/olat/modules/portfolio/manager/PortfolioUserDataManager.java b/src/main/java/org/olat/modules/portfolio/manager/PortfolioUserDataManager.java index 5e36df4bb2c036c1b3f77d120d89cef0f96e0281..d7f3adcfbab660d1428cc374413e8e7f4beb39e6 100644 --- a/src/main/java/org/olat/modules/portfolio/manager/PortfolioUserDataManager.java +++ b/src/main/java/org/olat/modules/portfolio/manager/PortfolioUserDataManager.java @@ -1,3 +1,22 @@ +/** + * <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.modules.portfolio.manager; import java.io.File; diff --git a/src/main/java/org/olat/modules/webFeed/manager/FeedManagerImpl.java b/src/main/java/org/olat/modules/webFeed/manager/FeedManagerImpl.java index 7a16614fd789aea359b217a2b6bd5bc2a6051c69..1c73badc207bbf6a8eae4c69b5af26f7d4b8d129 100644 --- a/src/main/java/org/olat/modules/webFeed/manager/FeedManagerImpl.java +++ b/src/main/java/org/olat/modules/webFeed/manager/FeedManagerImpl.java @@ -99,9 +99,6 @@ public class FeedManagerImpl extends FeedManager { // 10 minutes private static final int EXTERNAL_FEED_ACTUALIZATION_MILLIS = 10*60*1000; - public static final String KIND_PODCAST = "podcast"; - public static final String KIND_BLOG = "blog"; - private RepositoryManager repositoryManager; private Coordinator coordinator; private OLATResourceManager resourceManager; diff --git a/src/main/java/org/olat/modules/webFeed/manager/FeedUserDataManager.java b/src/main/java/org/olat/modules/webFeed/manager/FeedUserDataManager.java new file mode 100644 index 0000000000000000000000000000000000000000..b627ee785f29525c6cdf88c6a7c74daa8f861799 --- /dev/null +++ b/src/main/java/org/olat/modules/webFeed/manager/FeedUserDataManager.java @@ -0,0 +1,135 @@ +/** + * <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.modules.webFeed.manager; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.olat.core.commons.persistence.DB; +import org.olat.core.gui.DefaultGlobalSettings; +import org.olat.core.gui.components.velocity.VelocityContainer; +import org.olat.core.gui.render.EmptyURLBuilder; +import org.olat.core.gui.render.RenderResult; +import org.olat.core.gui.render.Renderer; +import org.olat.core.gui.render.StringOutput; +import org.olat.core.gui.render.velocity.VelocityRenderDecorator; +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.StringHelper; +import org.olat.core.util.Util; +import org.olat.core.util.vfs.VFSContainer; +import org.olat.core.util.vfs.VFSItem; +import org.olat.core.util.vfs.filters.SystemItemFilter; +import org.olat.modules.portfolio.ui.MediaCenterController; +import org.olat.modules.webFeed.Item; +import org.olat.user.UserDataExportable; +import org.olat.user.manager.ManifestBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * Initial date: 28 mai 2018<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +@Service +public class FeedUserDataManager implements UserDataExportable { + + private static final OLog log = Tracing.createLoggerFor(FeedUserDataManager.class); + + @Autowired + private DB dbInstance; + @Autowired + private ItemDAO itemDao; + @Autowired + private FeedManager feedManager; + + @Override + public String getExporterID() { + return "feeds"; + } + + @Override + public void export(Identity identity, ManifestBuilder manifest, File archiveDirectory, Locale locale) { + List<Item> items = itemDao.loadItemsByAuthor(identity); + dbInstance.commitAndCloseSession(); + + File itemsDirectory = new File(archiveDirectory, "blogspodcasts"); + itemsDirectory.mkdir(); + for(Item item:items) { + export(item, itemsDirectory, locale); + } + } + + private void export(Item item, File itemsDirectory, Locale locale) { + String title = StringHelper.transformDisplayNameToFileSystemName(item.getTitle()); + String name = item.getKey() + "_" + title; + File itemDir = new File(itemsDirectory, name); + itemDir.mkdir(); + + VFSContainer itemContainer = feedManager.getItemContainer(item); + List<File> attachments = new ArrayList<>(); + if(itemContainer != null) { + List<VFSItem> attachmentItems = itemContainer.getItems(new SystemItemFilter()); + for(VFSItem attachmentItem:attachmentItems) { + FileUtils.copyItemToDir(attachmentItem, itemDir, "Copy blog/podcast files"); + attachments.add(new File(itemDir, attachmentItem.getName())); + } + } + + File mediaFile = new File(itemDir, "index.html"); + try(OutputStream out = new FileOutputStream(mediaFile)) { + String content = exportContent(item, attachments, locale); + out.write(content.getBytes("UTF-8")); + out.flush(); + } catch(IOException e) { + log.error("", e); + } + } + + private String exportContent(Item item, List<File> attachments, Locale locale) { + StringOutput sb = new StringOutput(10000); + Translator translator = Util.createPackageTranslator(MediaCenterController.class, locale); + String pagePath = Util.getPackageVelocityRoot(FeedUserDataManager.class) + "/export_item.html"; + VelocityContainer component = new VelocityContainer("html", pagePath, translator, null); + component.contextPut("item", item); + component.contextPut("content", item.getContent()); + component.contextPut("attachments", attachments); + + Renderer renderer = Renderer.getInstance(component, translator, new EmptyURLBuilder(), new RenderResult(), new DefaultGlobalSettings()); + try(VelocityRenderDecorator vrdec = new VelocityRenderDecorator(renderer, component, sb)) { + component.contextPut("r", vrdec); + renderer.render(sb, component, null); + } catch(IOException e) { + log.error("", e); + } + return sb.toString(); + } + +} diff --git a/src/main/java/org/olat/modules/webFeed/manager/ItemDAO.java b/src/main/java/org/olat/modules/webFeed/manager/ItemDAO.java index cac85bbc27767b1a2bbd770441b5049ccd745e20..f65fee6615e48bdcce2c0c0ca8b8da9284f7d200 100644 --- a/src/main/java/org/olat/modules/webFeed/manager/ItemDAO.java +++ b/src/main/java/org/olat/modules/webFeed/manager/ItemDAO.java @@ -26,6 +26,7 @@ import java.util.stream.Collectors; import javax.persistence.TypedQuery; +import org.olat.basesecurity.IdentityRef; import org.olat.core.commons.persistence.DB; import org.olat.core.util.StringHelper; import org.olat.modules.webFeed.Feed; @@ -194,6 +195,13 @@ public class ItemDAO { return query.getResultList(); } + public List<Item> loadItemsByAuthor(IdentityRef author) { + return dbInstance.getCurrentEntityManager() + .createNamedQuery("loadItemsByAuthorWithFeed", Item.class) + .setParameter("authorKey", author.getKey()) + .getResultList(); + } + /** * Loads all items of a feed. * diff --git a/src/main/java/org/olat/modules/webFeed/manager/_content/export_item.html b/src/main/java/org/olat/modules/webFeed/manager/_content/export_item.html new file mode 100644 index 0000000000000000000000000000000000000000..a40b00ae8c888e9aca7bb4fc79fb6bf837a53784 --- /dev/null +++ b/src/main/java/org/olat/modules/webFeed/manager/_content/export_item.html @@ -0,0 +1,40 @@ +<html> + <head> + <title>$r.escapeHtml($item.title)</title> + <meta http-equiv="Content-type" content="text/html; charset=utf-8"> + <script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script> + <script> + jQuery(function() { + jQuery("video").each(function(index, el) { + el.attr("controls", ""); + }); + jQuery("audio").each(function(index, el) { + el.attr("controls", ""); + }); + }); + </script> + </head> + <body> + <h4>$r.escapeHtml($item.title)</h4> + #if($r.isNotEmpty($item.description)) + $r.xssScan($item.description) + #end + #if($r.isNotEmpty($item.enclosure) && $r.isNotEmpty($item.enclosure.fileName)) + #if($item.getEnclosure().getType().startsWith("video/")) + <video width="$item.width" height="$item.height" controls> + <source src="media/$item.enclosure.fileName" type="$item.enclosure.type"> + </video> + #elseif($item.getEnclosure().getType().startsWith("audio/")) + <audio controls> + <source src="media/$item.enclosure.fileName" type="$item.enclosure.type"> + </video> + #else + <a href="media/$item.enclosure.fileName" targe="_blanck">$item.enclosure.fileName</a> + #end + #end + #if($r.isNotEmpty($item.content)) + $r.xssScan($item.content) + #end + + </body> +</html> \ No newline at end of file diff --git a/src/main/java/org/olat/modules/webFeed/model/ItemImpl.java b/src/main/java/org/olat/modules/webFeed/model/ItemImpl.java index 571d22bc7b4e97dddf0325aef8fc9a943617d9ae..f945f225218ef1b030b46eda1eb5ee8eb8d2cb16 100644 --- a/src/main/java/org/olat/modules/webFeed/model/ItemImpl.java +++ b/src/main/java/org/olat/modules/webFeed/model/ItemImpl.java @@ -62,6 +62,8 @@ import org.olat.user.UserManager; query="select data from item data where data.guid=:guid"), @NamedQuery(name="loadItemsByFeed", query="select data from item data where data.feed=:feed"), + @NamedQuery(name="loadItemsByAuthorWithFeed", + query="select data from item data inner join fetch data.feed as feed where data.authorKey=:authorKey"), @NamedQuery(name="loadItemsGuidByFeed", query="select guid from item data where data.feed=:feed"), @NamedQuery(name="removeItem", diff --git a/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java b/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java index 9a82b3e44109b0b6389a49e2275bd786f18fdec7..d7c6cd50540d318fb8e5a07725f16cecfd55a6c5 100644 --- a/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java +++ b/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java @@ -777,13 +777,13 @@ public class ACFrontendManager implements ACService, UserDataExportable { OpenXMLWorksheet sheet = workbook.nextWorksheet(); sheet.setHeaderRows(1); - Row row = sheet.newRow(); - row.addCell(0, "Status"); - row.addCell(1, "Booking number"); - row.addCell(2, "Date"); - row.addCell(3, "Content"); - row.addCell(4, "Method"); - row.addCell(5, "Total"); + Row header = sheet.newRow(); + header.addCell(0, "Status"); + header.addCell(1, "Booking number"); + header.addCell(2, "Date"); + header.addCell(3, "Content"); + header.addCell(4, "Method"); + header.addCell(5, "Total"); List<OrderTableItem> orders = findOrderItems(null, identity, null, null, null, null, 0, -1, null); for(OrderTableItem order:orders) { diff --git a/src/main/java/org/olat/user/ui/data/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/user/ui/data/_i18n/LocalStrings_de.properties index 54789b0ca66ae672eda538f475899e67156879b1..3ec6006af26e30538b7304a61fd840af4d969b56 100644 --- a/src/main/java/org/olat/user/ui/data/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/user/ui/data/_i18n/LocalStrings_de.properties @@ -4,6 +4,7 @@ bookings=Buchungen calendars=Kalendareintr\u00E4ge certificates=Certifikaten chat=Chat +comments.ratings=Kommentaren und Bewertungen disclaimer=Nutzungsbedigung display.portrait=Profilbild download.data=Dateien herunterladen @@ -18,8 +19,11 @@ export.user.data.processing=Der Export ist gerade bearbeitet. export.user.data.ready=Der Benutzer kann den Export mit dem folgenden Link herunterladen. Sie k\u00F6nnen auch ein en neuen Export erfordern. export.user.data.ready.subject=Export von "{0}" ist fertig export.user.data.ready.text=<p>Export von "{0}" ist fertig. Es konnte unter <a href\="{1}">{1}</a> heruntergeladen werden</p> +feeds=Blogs und Podcasts forums=Foren group.memberships=Zugeh\u00F6rigkeit zu Gruppen +gta=Aufgabe +logs=Logs mail=Mail notes=Pers\u00F6nliche Notizen notifications=Abonnements diff --git a/src/main/java/org/olat/user/ui/data/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/user/ui/data/_i18n/LocalStrings_en.properties index 6236b054fd31aa7d67bf1b3fd4e6940e253e72f9..5bb571cb71e8a72f0332bf3afe057d43fd5a0066 100644 --- a/src/main/java/org/olat/user/ui/data/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/user/ui/data/_i18n/LocalStrings_en.properties @@ -4,19 +4,22 @@ bookings=Bookings calendars=Calendars certificates=Certificates chat=Chat messages +comments.ratings=Comments and ratings disclaimer=Disclaimer display.portrait=Published image download.data=Download the data -dx=dr -dy=dr efficiency.statements=Evidences of achievement export.options=Export elements export.start=Start export export.url=Link to the data +feeds=Blogs and podcasts forums=Forums group.memberships=Membership to groups +gta=Tasks +logs=Logs mail=Emails notes=Notes notifications=Subscriptions personal.folders=All documents in private and public folders repository.memberships=Membership to courses +