/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <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> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. */ package org.olat.modules.wiki; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.Iterator; import java.util.List; import java.util.Properties; import org.apache.commons.codec.binary.Base64; import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl; import org.olat.core.commons.services.notifications.SubscriptionContext; import org.olat.core.gui.UserRequest; import org.olat.core.gui.control.WindowControl; import org.olat.core.id.OLATResourceable; 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.logging.activity.LearningResourceLoggingAction; import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; import org.olat.core.util.FileUtils; import org.olat.core.util.cache.CacheWrapper; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.resource.OresHelper; 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.VFSItemSuffixFilter; import org.olat.core.util.vfs.filters.VFSLeafFilter; import org.olat.course.CourseModule; import org.olat.course.nodes.WikiCourseNode; import org.olat.course.run.environment.CourseEnvironment; import org.olat.fileresource.FileResourceManager; import org.olat.fileresource.types.FileResource; import org.olat.fileresource.types.WikiResource; import org.olat.group.BusinessGroup; import org.olat.modules.wiki.versioning.DifferenceService; import org.olat.modules.wiki.versioning.diff.CookbookDifferenceService; import org.olat.resource.OLATResource; import org.olat.resource.OLATResourceManager; /** * Description:<br> * This class handles several wiki's by storing them in a cache and also creates a new wikis. * It handles also the file operation to persist the data in the wiki pages which are stored on the file * system. * <P> * Initial Date: May 5, 2006 <br> * * @author guido */ public class WikiManager { private static final OLog log = Tracing.createLoggerFor(WikiManager.class); public static final String VIEW_COUNT = "view.count"; public static final String MODIFY_AUTHOR = "modify.author"; public static final String M_TIME = "mTime"; public static final String INITIAL_AUTHOR = "initial.author"; public static final String FORUM_KEY = "forum.key"; public static final String VERSION = "version"; public static final String C_TIME = "cTime"; public static final String PAGENAME = "pagename"; private static WikiManager instance; public static final String WIKI_RESOURCE_FOLDER_NAME = "wiki"; public static final String VERSION_FOLDER_NAME = "versions"; public static final String WIKI_FILE_SUFFIX = "wp"; public static final String WIKI_PROPERTIES_SUFFIX = "properties"; public static final String UPDATE_COMMENT = "update.comment"; //o_clusterNOK cache : 08.04.08/cg Not tested in cluster-mode CacheWrapper<String,Wiki> wikiCache; OLATResourceManager resourceManager; FileResourceManager fileResourceManager; CoordinatorManager coordinator; /** * spring only */ private WikiManager() { instance = this; } /** * return singleton */ public static WikiManager getInstance() { return instance; } // ---- begin controller factory ----- /** @param ureq * @param wControl * @param ores either an OlatResourcable of an repository entry or of an BusinessGroup * @param securityCallback a callback to evaluate the permissions * @param initialPageName opens the wiki with an certain page, default is the index page if null is passed * @param courseContext - a course context or null if used outside a course * @param courseNodeContext - a courseNode context or null if used outside a course */ public WikiMainController createWikiMainController(UserRequest ureq, WindowControl wControl, OLATResourceable ores, WikiSecurityCallback securityCallback, String initialPageName) { return new WikiMainController(ureq, wControl, ores, securityCallback, initialPageName); } // ---- end controller factory ----- /** * @return the new created resource */ public WikiResource createWiki() { WikiResource resource = new WikiResource(); createFolders(resource); OLATResourceManager rm = getResourceManager(); OLATResource ores = rm.createOLATResourceInstance(resource); rm.saveOLATResource(ores); return resource; } public void setCoordinator(CoordinatorManager coord){ coordinator = coord; } public boolean importWiki(File file, String filename, File targetDirectory) { try { Path path = FileResource.getResource(file, filename); if(path == null) { return false; } Path destDir = targetDirectory.toPath(); Files.walkFileTree(path, new ImportVisitor(destDir)); return true; } catch (IOException e) { log.error("", e); return false; } } /** * Copy the wiki pages, media but ignores the versions. * */ public boolean copyWiki(File sourceDirectory, File targetDirectory) { try { Path path = sourceDirectory.toPath(); Path destDir = targetDirectory.toPath(); Files.walkFileTree(path, new CopyVisitor(path, destDir)); return true; } catch (IOException e) { log.error("", e); return false; } } /** * Reset the same properties as in the method resetCopiedPage * of WikiPage. * * @param file * @param destFile */ private final static void resetAndCopyProperties(Path file, Path destFile) { Properties props = new Properties(); try(InputStream inStream = Files.newInputStream(file); OutputStream outStream = Files.newOutputStream(destFile)) { props.load(inStream); props.setProperty(VERSION, "0"); props.setProperty(FORUM_KEY, "0"); props.setProperty(MODIFY_AUTHOR, "0"); props.setProperty(UPDATE_COMMENT, "0"); props.setProperty(VIEW_COUNT, "0"); props.setProperty(M_TIME, "0"); props.store(outStream, ""); } catch(Exception e) { log.error("", e); } } /** * Copy the content of a resource of type wiki where the files * are saved in different directories: wiki and media. The versions * are ignored. * * Initial date: 02.05.2014<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public static class CopyVisitor extends SimpleFileVisitor<Path> { private final Path sourceDir; private final Path destDir; public CopyVisitor(Path sourceDir, Path destDir) throws IOException { this.destDir = destDir; this.sourceDir = sourceDir; Path wikiDir = destDir.resolve(WIKI_RESOURCE_FOLDER_NAME); Files.createDirectories(wikiDir); Path mediaDir = destDir.resolve(WikiContainer.MEDIA_FOLDER_NAME); Files.createDirectories(mediaDir); Path versionDir = destDir.resolve(VERSION_FOLDER_NAME); Files.createDirectories(versionDir); } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Path relFile = sourceDir.relativize(file); String filename = file.getFileName().toString(); if(filename.endsWith(WikiManager.WIKI_PROPERTIES_SUFFIX)) { final Path destFile = Paths.get(destDir.toString(), relFile.toString()); resetAndCopyProperties(file, destFile); } else if (filename.endsWith(WIKI_FILE_SUFFIX)) { final Path destFile = Paths.get(destDir.toString(), relFile.toString()); Files.copy(file, destFile, StandardCopyOption.REPLACE_EXISTING); } else if (!filename.contains(WIKI_FILE_SUFFIX + "-") && !filename.contains(WIKI_PROPERTIES_SUFFIX + "-")) { final Path destFile = Paths.get(destDir.toString(), relFile.toString()); Files.copy(file, destFile, StandardCopyOption.REPLACE_EXISTING); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { dir = sourceDir.relativize(dir); final Path dirToCreate = Paths.get(destDir.toString(), dir.toString()); if(Files.notExists(dirToCreate)){ Files.createDirectory(dirToCreate); } return FileVisitResult.CONTINUE; } } /** * Dispatch the content in the wiki and media folders from an import * where all the files are flatted at the root of the archive. * * Initial date: 02.05.2014<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public static class ImportVisitor extends SimpleFileVisitor<Path> { private final Path destDir; private final Path wikiDir; private final Path mediaDir; public ImportVisitor(Path destDir) throws IOException { this.destDir = destDir; wikiDir = destDir.resolve(WIKI_RESOURCE_FOLDER_NAME); Files.createDirectories(wikiDir); mediaDir = destDir.resolve(WikiContainer.MEDIA_FOLDER_NAME); Files.createDirectories(mediaDir); Path versionDir = destDir.resolve(VERSION_FOLDER_NAME); Files.createDirectories(versionDir); } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { String filename = file.getFileName().toString(); if(filename.endsWith(WikiManager.WIKI_PROPERTIES_SUFFIX)) { final Path destFile = Paths.get(wikiDir.toString(), file.toString()); resetAndCopyProperties(file, destFile); } else if (filename.endsWith(WIKI_FILE_SUFFIX)) { final Path destFile = Paths.get(wikiDir.toString(), file.toString()); Files.copy(file, destFile, StandardCopyOption.REPLACE_EXISTING); } else if (!filename.contains(WIKI_FILE_SUFFIX + "-") && !filename.contains(WIKI_PROPERTIES_SUFFIX + "-")) { final Path destFile = Paths.get(mediaDir.toString(), file.toString()); Files.copy(file, destFile, StandardCopyOption.REPLACE_EXISTING); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { final Path dirToCreate = Paths.get(destDir.toString(), dir.toString()); if(Files.notExists(dirToCreate)){ Files.createDirectory(dirToCreate); } return FileVisitResult.CONTINUE; } } /** * API change stop */ void createFolders(OLATResourceable ores) { long start = 0; if (log.isDebug()) { start = System.currentTimeMillis(); } VFSContainer rootContainer = getWikiRootContainer(ores); VFSContainer unzippedDir = (VFSContainer) rootContainer.resolve(FileResourceManager.ZIPDIR); if (unzippedDir == null) { // check for _unzipped_ dir from imported wiki's if (rootContainer.createChildContainer(WIKI_RESOURCE_FOLDER_NAME) == null) throwError(ores); if (rootContainer.createChildContainer(WikiContainer.MEDIA_FOLDER_NAME) == null) throwError(ores); if (rootContainer.createChildContainer(VERSION_FOLDER_NAME) == null) throwError(ores); } else { // _unzipped_ dir found: move elements to wiki folder and delete // unzipped dir and zip files List<VFSItem> files = unzippedDir.getItems(); VFSContainer wikiCtn = rootContainer.createChildContainer(WIKI_RESOURCE_FOLDER_NAME); VFSContainer mediaCtn = rootContainer.createChildContainer(WikiContainer.MEDIA_FOLDER_NAME); if (rootContainer.createChildContainer(VERSION_FOLDER_NAME) == null) throwError(ores); if (wikiCtn == null) throwError(ores); // copy files to wiki and media folder for (Iterator<VFSItem> iter = files.iterator(); iter.hasNext();) { VFSLeaf leaf = ((VFSLeaf) iter.next()); if (leaf.getName().endsWith(WikiManager.WIKI_FILE_SUFFIX) || leaf.getName().endsWith(WikiManager.WIKI_PROPERTIES_SUFFIX)) { wikiCtn.copyFrom(leaf); } else { if (leaf.getName().contains(WikiManager.WIKI_FILE_SUFFIX+"-") || leaf.getName().contains(WikiManager.WIKI_PROPERTIES_SUFFIX+"-")) { leaf.delete(); // delete version history } else mediaCtn.copyFrom(leaf); } } unzippedDir.delete(); List<VFSItem> zipFiles = rootContainer.getItems(new VFSItemSuffixFilter(new String[] { "zip" })); // delete all zips for (Iterator<VFSItem> iter = zipFiles.iterator(); iter.hasNext();) { VFSLeaf element = (VFSLeaf) iter.next(); element.delete(); } //reset forum key and author references keys back to default as users and forums may not exist List<VFSItem> propertyLeafs = wikiCtn.getItems(new VFSItemSuffixFilter(new String[] { WikiManager.WIKI_PROPERTIES_SUFFIX })); for (Iterator<VFSItem> iter = propertyLeafs.iterator(); iter.hasNext();) { VFSLeaf element = (VFSLeaf) iter.next(); WikiPage page = Wiki.assignPropertiesToPage(element); page.setForumKey(0); page.setInitalAuthor(0); page.setModifyAuthor(0); page.setModificationTime(0); page.setViewCount(0); page.setVersion("0"); page.setCreationTime(System.currentTimeMillis()); saveWikiPageProperties(ores, page); } } if (log.isDebug()) { long end = System.currentTimeMillis(); log.debug("creating folders and move files and updating properties to default values took: (milliseconds)"+(end-start), null); } } private void throwError(OLATResourceable ores) { throw new OLATRuntimeException(this.getClass(), "Unable to create wiki folder structure for resource: " + ores.getResourceableId(), null); } /** * @param ores * @return a wiki loaded from cache or the fileSystem */ public Wiki getOrLoadWiki(final OLATResourceable ores) { final String wikiKey = OresHelper.createStringRepresenting(ores); //cluster_OK by guido if (wikiCache == null) { wikiCache = coordinator.getCoordinator().getCacher().getCache(WikiManager.class.getSimpleName(), "wiki"); } return wikiCache.computeIfAbsent(wikiKey, (key) -> { long start = 0; // wiki not in cache load form filesystem if (log.isDebug()) { log.debug("wiki not in cache. Loading wiki from filesystem. Ores: " + ores.getResourceableId()); start = System.currentTimeMillis(); } VFSContainer folder = getWikiContainer(ores, WIKI_RESOURCE_FOLDER_NAME); // wiki folder structure does not yet exists, but resource does. Create // wiki in group context if (folder == null) { // createWikiforExistingResource(ores); createFolders(ores); folder = getWikiContainer(ores, WIKI_RESOURCE_FOLDER_NAME); } // folders should be present, create the wiki Wiki wiki = new Wiki(getWikiRootContainer(ores)); // filter for xyz.properties files List<VFSItem> wikiLeaves = folder.getItems(new VFSItemSuffixFilter(new String[] { WikiManager.WIKI_PROPERTIES_SUFFIX })); for (Iterator<VFSItem> iter = wikiLeaves.iterator(); iter.hasNext();) { VFSLeaf propertiesFile = (VFSLeaf) iter.next(); WikiPage page = Wiki.assignPropertiesToPage(propertiesFile); if (page == null) { // broken pages get automatically cleaned from filesystem String contentFileToBeDeleted = (propertiesFile.getName().substring(0, propertiesFile.getName().length() - WikiManager.WIKI_PROPERTIES_SUFFIX.length()) + WikiManager.WIKI_FILE_SUFFIX); folder.resolve(contentFileToBeDeleted).delete(); propertiesFile.delete(); continue; } // index and menu page are loaded by default if (page.getPageName().equals(WikiPage.WIKI_INDEX_PAGE) || page.getPageName().equals(WikiPage.WIKI_MENU_PAGE)) { VFSLeaf leaf = (VFSLeaf) folder.resolve(page.getPageId() + "." + WikiManager.WIKI_FILE_SUFFIX); page.setContent(FileUtils.load(leaf.getInputStream(), "utf-8")); } // due to a bug we have to rename some pages that start with an non // ASCII lowercase letter String idOutOfFileName = propertiesFile.getName().substring(0, propertiesFile.getName().indexOf(".")); if (!page.getPageId().equals(idOutOfFileName)) { // rename corrupt prop file propertiesFile.rename(page.getPageId() + "." + WikiManager.WIKI_PROPERTIES_SUFFIX); // load content and delete corrupt content file VFSLeaf contentFile = (VFSLeaf) folder.resolve(idOutOfFileName + "." + WikiManager.WIKI_FILE_SUFFIX); contentFile.rename(page.getPageId() + "." + WikiManager.WIKI_FILE_SUFFIX); } wiki.addPage(page); } // if index and menu page not present create the first page and save it if (wiki.getNumberOfPages() == 0) { WikiPage indexPage = new WikiPage(WikiPage.WIKI_INDEX_PAGE); WikiPage menuPage = new WikiPage(WikiPage.WIKI_MENU_PAGE); indexPage.setCreationTime(System.currentTimeMillis()); wiki.addPage(indexPage); menuPage.setCreationTime(System.currentTimeMillis()); menuPage.setContent("* [[Index]]\n* [[Index|Your link]]"); wiki.addPage(menuPage); saveWikiPage(ores, indexPage, false, wiki); saveWikiPage(ores, menuPage, false, wiki); } // add pages internally used for displaying dynamic data, they are not persisted WikiPage recentChangesPage = new WikiPage(WikiPage.WIKI_RECENT_CHANGES_PAGE); WikiPage a2zPage = new WikiPage(WikiPage.WIKI_A2Z_PAGE); wiki.addPage(recentChangesPage); wiki.addPage(a2zPage); if (log.isDebug()) { long stop = System.currentTimeMillis(); log.debug("loading of wiki from filessystem took (ms) " + (stop - start)); } return wiki; }); } public DifferenceService getDiffService() { return new CookbookDifferenceService(); } /** * persists a wiki page on the filesystem. It moves the recent page and the * metadata to the versions folder with the version on the tail and saves new * page with metadata to the wiki folder. Does not need to be synchronized as * editing is locked on page level by the * * @see WikiMainController * @param ores * @param page */ public void saveWikiPage(OLATResourceable ores, WikiPage page, boolean incrementVersion, Wiki wiki) { //cluster_OK by guido VFSContainer versionsContainer = getWikiContainer(ores, VERSION_FOLDER_NAME); VFSContainer wikiContentContainer = getWikiContainer(ores, WIKI_RESOURCE_FOLDER_NAME); // rename existing content file to version x and copy it to the version // container VFSItem item = wikiContentContainer.resolve(page.getPageId() + "." + WIKI_FILE_SUFFIX); if (item != null && incrementVersion) { if (page.getVersion() > 0) { versionsContainer.copyFrom(item); VFSItem copiedItem = versionsContainer.resolve(page.getPageId() + "." + WIKI_FILE_SUFFIX); String fileName = page.getPageId() + "." + WIKI_FILE_SUFFIX + "-" + page.getVersion(); copiedItem.rename(fileName); } item.delete(); } // rename existing meta file to version x and copy it to the version // container item = wikiContentContainer.resolve(page.getPageId() + "." + WIKI_PROPERTIES_SUFFIX); if (item != null && incrementVersion) { // TODO renaming and coping does not work. Bug?? felix fragen if (page.getVersion() > 0) { versionsContainer.copyFrom(item); VFSItem copiedItem = versionsContainer.resolve(page.getPageId() + "." + WIKI_PROPERTIES_SUFFIX); String fileName = page.getPageId() + "." + WIKI_PROPERTIES_SUFFIX + "-" + page.getVersion(); copiedItem.rename(fileName); } item.delete(); } // store recent content file VFSLeaf leaf = wikiContentContainer.createChildLeaf(page.getPageId() + "." + WIKI_FILE_SUFFIX); if(leaf == null) throw new AssertException("Tried to save wiki page with id ("+page.getPageId()+") and Olatresource: "+ores.getResourceableId()+" but page already existed!"); FileUtils.save(leaf.getOutputStream(false), page.getContent(), "utf-8"); // store recent properties file leaf = wikiContentContainer.createChildLeaf(page.getPageId() + "." + WIKI_PROPERTIES_SUFFIX); if (leaf == null) throw new AssertException("could not create file for wiki page "+page.getPageId()+", ores: "+ores.getResourceableTypeName()+":"+ores.getResourceableId()+", wikicontainer:"+wikiContentContainer); if (incrementVersion) page.incrementVersion(); // update modification time if (!page.getContent().equals("")) page.setModificationTime(System.currentTimeMillis()); Properties p = getPageProperties(page); try { OutputStream os = leaf.getOutputStream(false); p.store(os, "wiki page meta properties"); os.close(); // if (incrementVersion) page.incrementVersion(); } catch (IOException e) { throw new OLATRuntimeException(WikiManager.class, "failed to save wiki page properties for page with id: " + page.getPageId() + " and olatresource: " + ores.getResourceableId(), e); } page.setViewCount(0); //reset view count of the page //update cache to inform all nodes about the change if (wikiCache!=null) { wikiCache.update(OresHelper.createStringRepresenting(ores), wiki); } if(ThreadLocalUserActivityLogger.getLoggedIdentity() != null) { // do logging only for real user ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_UPDATE, getClass()); } } /** * delete a page completely by removing from the file system * * @param ores * @param page */ protected void deleteWikiPage(OLATResourceable ores, WikiPage page) { String name = page.getPageName(); // do not delete default pages if (name.equals(WikiPage.WIKI_INDEX_PAGE) || name.equals(WikiPage.WIKI_MENU_PAGE)) return; VFSContainer wikiContentContainer = getWikiContainer(ores, WIKI_RESOURCE_FOLDER_NAME); VFSContainer versionsContainer = getWikiContainer(ores, VERSION_FOLDER_NAME); //delete content and property file VFSItem item = wikiContentContainer.resolve(page.getPageId() + "." + WIKI_FILE_SUFFIX); if (item != null) item.delete(); item = wikiContentContainer.resolve(page.getPageId() + "." + WIKI_PROPERTIES_SUFFIX); if (item != null) item.delete(); //delete all version files of the page List<VFSItem> leafs = versionsContainer.getItems(new VFSLeafFilter()); if (leafs.size() > 0) { for (Iterator<VFSItem> iter = leafs.iterator(); iter.hasNext();) { VFSLeaf leaf = (VFSLeaf) iter.next(); String filename = leaf.getName(); if (filename.startsWith(page.getPageId())) { leaf.delete(); } } } log.audit("Deleted wiki page with name: " + page.getPageName() + " from resourcable id: "+ ores.getResourceableId()); if (wikiCache!=null) { wikiCache.update(OresHelper.createStringRepresenting(ores), getOrLoadWiki(ores)); } } /** * delete a whole wiki from the cache and the filesystem * @param ores */ protected void deleteWiki(OLATResourceable ores) { if (wikiCache!=null) { wikiCache.remove(OresHelper.createStringRepresenting(ores)); } getResourceManager().deleteOLATResourceable(ores); } /** * @param ores * @param page */ public void updateWikiPageProperties(OLATResourceable ores, WikiPage page) { saveWikiPageProperties(ores, page); if (wikiCache!=null) { wikiCache.update(OresHelper.createStringRepresenting(ores), getOrLoadWiki(ores)); } } private void saveWikiPageProperties(OLATResourceable ores, WikiPage page) { VFSContainer wikiContentContainer = getWikiContainer(ores, WIKI_RESOURCE_FOLDER_NAME); VFSLeaf leaf = (VFSLeaf) wikiContentContainer.resolve(page.getPageId() + "." + WIKI_PROPERTIES_SUFFIX); if (leaf == null) leaf = wikiContentContainer.createChildLeaf(page.getPageId() + "." + WIKI_PROPERTIES_SUFFIX); Properties p = getPageProperties(page); try { p.store(leaf.getOutputStream(false), "wiki page meta properties"); } catch (IOException e) { throw new OLATRuntimeException(WikiManager.class, "failed to save wiki page properties for page with id: " + page.getPageId() +" and olatresource: " + ores.getResourceableId(), e); } } /** * @param page * @return the fields of the page object as properties */ private Properties getPageProperties(WikiPage page) { Properties p = new Properties(); p.setProperty(PAGENAME, page.getPageName()); p.setProperty(VERSION, String.valueOf(page.getVersion())); p.setProperty(FORUM_KEY, String.valueOf(page.getForumKey())); p.setProperty(INITIAL_AUTHOR, String.valueOf(page.getInitalAuthor())); p.setProperty(MODIFY_AUTHOR, String.valueOf(page.getModifyAuthor())); p.setProperty(C_TIME, String.valueOf(page.getCreationTime())); p.setProperty(VIEW_COUNT, String.valueOf(page.getViewCount())); p.setProperty(M_TIME, String.valueOf(page.getModificationTime())); p.setProperty(UPDATE_COMMENT, page.getUpdateComment()); return p; } /** * @param pageName * @return */ public static String generatePageId(String pageName) { try { String encoded = new String(Base64.encodeBase64(pageName.getBytes("utf-8")), "us-ascii"); encoded = encoded.replace('/', '_'); //base64 can contain "/" so we have to replace them return encoded; } catch (UnsupportedEncodingException e) { throw new OLATRuntimeException(WikiManager.class, "Encoding UTF-8 not supported by your platform!", e); } } /** * @param ores * @param folderName * @return the Vfs container or null if not found */ public VFSContainer getWikiContainer(OLATResourceable ores, String folderName) { VFSContainer wikiRootContainer = getWikiRootContainer(ores); return (VFSContainer) wikiRootContainer.resolve(folderName); } /** * Returns the root-container for certain OLAT-resourceable. * @param ores * @return */ public OlatRootFolderImpl getWikiRootContainer(OLATResourceable ores) { // Check if Resource is a BusinessGroup, because BusinessGroup-wiki's are stored at a different place if(log.isDebug()){ log.debug("calculating wiki root container with ores id: "+ores.getResourceableId()+" and resourcable type name: "+ores.getResourceableTypeName(), null); } if (isGroupContextWiki(ores)) { // Group Wiki return new OlatRootFolderImpl(getGroupWikiRelPath(ores), null); } else { // Repository Wiki return getFileResourceManager().getFileResourceRootImpl(ores); } } /** * Get Wiki-File-Path for certain BusinessGroup. * @param businessGroup * @return */ private String getGroupWikiRelPath(OLATResourceable ores) { return "/cts/wikis/" + ores.getResourceableTypeName() + "/" + ores.getResourceableId(); } /** * Return Media folder for uploading files. * @param ores * @return */ public OlatRootFolderImpl getMediaFolder(OLATResourceable ores) { // Check if Resource is a BusinessGroup, because BusinessGroup-wiki's are stored at a different place if (isGroupContextWiki(ores)) { // Group Wiki return new OlatRootFolderImpl(getGroupWikiRelPath(ores) + "/" + WikiContainer.MEDIA_FOLDER_NAME, null); } else { // Repository Wiki return new OlatRootFolderImpl("/repository/" + ores.getResourceableId() + "/" + WikiContainer.MEDIA_FOLDER_NAME, null); } } /** * @return false if repository wiki or true if group only wiki */ /** * @return false if repository wiki or true if group only wiki */ protected boolean isGroupContextWiki(OLATResourceable ores) { return ores.getResourceableTypeName().equals(OresHelper.calculateTypeName(BusinessGroup.class)); } /** * wiki subscription context for wikis in the course. * @param cenv * @param wikiCourseNode * @return */ public static SubscriptionContext createTechnicalSubscriptionContextForCourse(CourseEnvironment cenv, WikiCourseNode wikiCourseNode) { return new SubscriptionContext(CourseModule.getCourseTypeName(), cenv.getCourseResourceableId(), wikiCourseNode.getIdent()); } public void setResourceManager(OLATResourceManager resourceManager) { this.resourceManager = resourceManager; } public OLATResourceManager getResourceManager() { return resourceManager; } public void setFileResourceManager(FileResourceManager fileResourceManager) { this.fileResourceManager = fileResourceManager; } public FileResourceManager getFileResourceManager() { return fileResourceManager; } }