/** * 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.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.modules.bc.FolderEvent; import org.olat.core.commons.services.notifications.NotificationsManager; import org.olat.core.commons.services.notifications.PublisherData; import org.olat.core.commons.services.notifications.SubscriptionContext; import org.olat.core.commons.services.notifications.ui.ContextualSubscriptionController; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.dropdown.Dropdown; import org.olat.core.gui.components.dropdown.Dropdown.Spacer; import org.olat.core.gui.components.htmlheader.jscss.JSAndCSSComponent; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.panel.Panel; import org.olat.core.gui.components.panel.StackedPanel; import org.olat.core.gui.components.tabbedpane.TabbedPane; import org.olat.core.gui.components.tabbedpane.TabbedPaneChangedEvent; import org.olat.core.gui.components.table.Table; import org.olat.core.gui.components.table.TableController; import org.olat.core.gui.components.table.TableEvent; import org.olat.core.gui.components.table.TableGuiConfiguration; import org.olat.core.gui.components.table.TableMultiSelectEvent; import org.olat.core.gui.components.tree.GenericTreeModel; import org.olat.core.gui.components.tree.GenericTreeNode; import org.olat.core.gui.components.tree.TreeEvent; import org.olat.core.gui.components.tree.TreeNode; import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; import org.olat.core.gui.control.generic.clone.CloneableController; import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowController; import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.gui.control.generic.dtabs.Activateable2; import org.olat.core.gui.control.generic.modal.DialogBoxController; import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.id.OLATResourceable; import org.olat.core.id.context.BusinessControlFactory; import org.olat.core.id.context.ContextEntry; import org.olat.core.id.context.StateEntry; 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.OlatResourceableType; import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; import org.olat.core.util.Encoder; import org.olat.core.util.StringHelper; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.coordinate.LockResult; 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.VFSMediaResource; import org.olat.modules.fo.Forum; import org.olat.modules.fo.ForumCallback; import org.olat.modules.fo.manager.ForumManager; import org.olat.modules.fo.ui.ForumController; import org.olat.modules.portfolio.PortfolioV2Module; import org.olat.modules.portfolio.ui.component.MediaCollectorComponent; import org.olat.modules.wiki.gui.components.wikiToHtml.ErrorEvent; import org.olat.modules.wiki.gui.components.wikiToHtml.FilterUtil; import org.olat.modules.wiki.gui.components.wikiToHtml.RequestImageEvent; import org.olat.modules.wiki.gui.components.wikiToHtml.RequestMediaEvent; import org.olat.modules.wiki.gui.components.wikiToHtml.RequestNewPageEvent; import org.olat.modules.wiki.gui.components.wikiToHtml.RequestPageEvent; import org.olat.modules.wiki.gui.components.wikiToHtml.WikiMarkupComponent; import org.olat.modules.wiki.portfolio.WikiArtefact; import org.olat.modules.wiki.portfolio.WikiMediaHandler; import org.olat.modules.wiki.versioning.ChangeInfo; import org.olat.modules.wiki.versioning.HistoryTableDateModel; import org.olat.portfolio.EPUIFactory; import org.olat.search.SearchServiceUIFactory; import org.olat.search.SearchServiceUIFactory.DisplayOption; import org.olat.util.logging.activity.LoggingResourceable; import org.springframework.beans.factory.annotation.Autowired; /** * Description:<br> * This controller creates the whole GUI for a wiki with a tabbed pane contaning * an article view, per page forum view, edit pane and versioning pane. The * rendering of the wiki syntax to html is done by @see * org.olat.core.gui.components.wikiToHtml.WikiMarkupComponent. * <P> * Initial Date: May 4, 2006 <br> * * @author guido */ public class WikiMainController extends BasicController implements CloneableController, Activateable2 { private static final OLog log = Tracing.createLoggerFor(WikiMainController.class); private TabbedPane tabs; private WikiPage selectedPage; private GenericTreeModel wikiMenuModel; private String pageId; private VFSContainer wikiContainer; private OLATResourceable ores; private VelocityContainer articleContent, navigationContent, discussionContent, editContent, content, versioningContent, mediaMgntContent, imageDisplay; private ForumController forumController; private WikiEditArticleForm wikiEditForm; private WikiMarkupComponent wikiArticleComp, wikiVersionDisplayComp; private ContextualSubscriptionController cSubscriptionCtrl; private TableController versioningTableCtr; private WikiFileUploadController wikiUploadFileCtr; private HistoryTableDateModel versioningTableModel; private DialogBoxController removePageDialogCtr, archiveWikiDialogCtr; private List<ChangeInfo> diffs = new ArrayList<ChangeInfo>(2); private SubscriptionContext subsContext; private LockResult lockEntry; private Link archiveLink, closePreviewButton, createLink, toMainPageLink, a2zLink, changesLink, editMenuButton, revertVersionButton; private TableController mediaTableCtr; private MediaFilesTableModel mediaFilesTableModel; private TableGuiConfiguration tableConfig; private WikiSecurityCallback securityCallback; private Controller searchCtrl; private WikiArticleSearchForm createArticleForm; private CloseableCalloutWindowController calloutCtrl; private CloseableModalController cmc; private StackedPanel mainPanel; private Dropdown wikiMenuDropdown, navigationDropdown, breadcrumpDropdown; private GenericTreeNode navMainPageNode, navAZNode, navChangesNode, wikiMenuNode; public static final String ACTION_COMPARE = "compare"; public static final String ACTION_SHOW = "view.version"; private static final String ACTION_EDIT_MENU = "editMenu"; private static final String ACTION_CLOSE_PREVIEW = "preview.close"; private static final String ACTION_DELETE_MEDIAS = "delete.medias"; private static final String ACTION_DELETE_MEDIA = "delete.media"; protected static final String ACTION_SHOW_MEDIA = "show.media"; protected static final String METADATA_SUFFIX = ".metadata"; private static final String MEDIA_FILE_FILENAME = "filename"; private static final String MEDIA_FILE_CREATIONDATE = "creation.date"; private static final String MEDIA_FILE_CREATED_BY = "created.by"; private static final String MEDIA_FILE_DELETIONDATE = "deleted.at"; private static final String MEDIA_FILE_DELETED_BY = "deleted.by"; // indicates if user is already on image-detail-view-page (OLAT-6233) private boolean isImageDetailView = false; @Autowired private WikiMediaHandler wikiMediaHandler; @Autowired private PortfolioV2Module portfolioModule; public WikiMainController(UserRequest ureq, WindowControl wControl, OLATResourceable ores, WikiSecurityCallback securityCallback, String initialPageName) { super(ureq, wControl); this.wikiContainer = WikiManager.getInstance().getWikiRootContainer(ores); this.ores = ores; this.securityCallback = securityCallback; this.subsContext = securityCallback.getSubscriptionContext(); boolean guestOnly = ureq.getUserSession().getRoles().isGuestOnly(); WikiPage page = null; Wiki wiki = getWiki(); if (wiki == null) { VelocityContainer vc = createVelocityContainer("deleted"); mainPanel = putInitialPanel(vc); return; } if (!ores.getResourceableTypeName().equals("BusinessGroup")) { addLoggingResourceable(LoggingResourceable.wrap(ores, OlatResourceableType.genRepoEntry)); } ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_OPEN, getClass()); // init the first page either startpage or an other page identified by // initial page name if (initialPageName != null && wiki.pageExists(WikiManager.generatePageId(initialPageName))) { page = wiki.getPage(initialPageName, true); } else { page = wiki.getPage(WikiPage.WIKI_INDEX_PAGE); if (initialPageName != null) showError("wiki.error.page.not.found"); } this.pageId = page.getPageId(); tabs = new TabbedPane("userTabP", ureq.getLocale()); tabs.addListener(this); // init the tabbed pane container content = createVelocityContainer("index"); // navigation container navigationContent = createVelocityContainer("navigation"); navigationContent.contextPut("navigationEnabled", Boolean.TRUE); // add a history that displays visited pages breadcrumpDropdown = new Dropdown("breadcrump", "navigation.history", false, getTranslator()); Link indexLink = LinkFactory.createToolLink(WikiPage.WIKI_INDEX_PAGE, "select-page", WikiPage.WIKI_INDEX_PAGE, this); breadcrumpDropdown.addComponent(indexLink); navigationContent.put("breadcrumb", breadcrumpDropdown); if (subsContext != null) { String businnessPath = wControl.getBusinessControl().getAsString(); PublisherData data = new PublisherData(OresHelper.calculateTypeName(WikiPage.class), null, businnessPath); cSubscriptionCtrl = new ContextualSubscriptionController(ureq, getWindowControl(), subsContext, data); listenTo(cSubscriptionCtrl); navigationContent.put("subscription", cSubscriptionCtrl.getInitialComponent()); } navigationDropdown = new Dropdown("navi", "navigation.navigation", false, getTranslator()); navigationContent.put("navi", navigationDropdown); toMainPageLink = LinkFactory.createLink("navigation.mainpage", navigationContent, this); toMainPageLink.setDomReplaceable(false); toMainPageLink.setIconLeftCSS("o_icon o_icon_home"); navigationDropdown.addComponent(toMainPageLink); a2zLink = LinkFactory.createLink("navigation.a-z", navigationContent, this); a2zLink.setDomReplacementWrapperRequired(false); navigationDropdown.addComponent(a2zLink); changesLink = LinkFactory.createLink("navigation.changes", navigationContent, this); changesLink.setDomReplacementWrapperRequired(false); navigationDropdown.addComponent(changesLink); archiveLink = LinkFactory.createLink("archive.wiki", navigationContent, this); archiveLink.setIconLeftCSS("o_icon o_icon_archive_tool"); archiveLink.setDomReplacementWrapperRequired(false); archiveLink.setTitle("archive.wiki.title"); if (securityCallback.mayEditAndCreateArticle()) { createLink = LinkFactory.createLink("navigation.create.article", navigationContent, this); createLink.setIconLeftCSS("o_icon o_icon_create"); createLink.setElementCssClass("o_sel_wiki_create_page"); createLink.setDomReplacementWrapperRequired(false); } content.put("navigation", navigationContent); // search if (!guestOnly) { SearchServiceUIFactory searchServiceUIFactory = (SearchServiceUIFactory) CoreSpringFactory .getBean(SearchServiceUIFactory.class); searchCtrl = searchServiceUIFactory.createInputController(ureq, wControl, DisplayOption.STANDARD, null); listenTo(searchCtrl); navigationContent.put("search_article", searchCtrl.getInitialComponent()); } // attach menu wikiMenuDropdown = new Dropdown("wikiMenu", "navigation.menu", false, getTranslator()); if (securityCallback.mayEditWikiMenu()) { editMenuButton = LinkFactory.createLink("edit.menu", navigationContent, this); editMenuButton.setIconLeftCSS("o_icon o_icon_edit"); } navigationContent.put("wikiMenuDropdown", wikiMenuDropdown); updateWikiMenu(wiki); // attach index article wikiArticleComp = new WikiMarkupComponent("wikiArticle", ores, 300); wikiArticleComp.addListener(this); wikiArticleComp.setImageMapperUri(ureq, wikiContainer); navigationContent.put("wikiArticle", wikiArticleComp); /*************************************************************************** * wiki component **************************************************************************/ articleContent = createVelocityContainer("article"); articleContent.put("wikiArticle", wikiArticleComp); tabs.addTab(translate("tab.article"), articleContent); /*************************************************************************** * discussion container **************************************************************************/ discussionContent = createVelocityContainer("discuss"); tabs.addTab(translate("tab.discuss"), discussionContent); /*************************************************************************** * edit container **************************************************************************/ editContent = createVelocityContainer("edit"); imageDisplay = createVelocityContainer("imagedisplay"); closePreviewButton = LinkFactory.createButtonSmall(ACTION_CLOSE_PREVIEW, editContent, this); editContent.contextPut("isGuest", Boolean.valueOf(ureq.getUserSession().getRoles().isGuestOnly())); wikiEditForm = new WikiEditArticleForm(ureq, wControl, page, securityCallback); listenTo(wikiEditForm); editContent.contextPut("editformid", "ofo_" + wikiEditForm.hashCode()); editContent.put("editForm", wikiEditForm.getInitialComponent()); JSAndCSSComponent js = new JSAndCSSComponent("js", new String[] { "js/openolat/wiki.js" }, null); content.put("js", js); editContent.contextPut("fileList", wiki.getMediaFileList()); editContent.contextPut("linkList", wiki.getListOfAllPageNames()); tabs.addTab(translate("tab.edit"), editContent); /*************************************************************************** * version container **************************************************************************/ versioningContent = createVelocityContainer("versions"); wikiVersionDisplayComp = new WikiMarkupComponent("versionDisplay", ores, 300); wikiVersionDisplayComp.addListener(this); wikiVersionDisplayComp.setImageMapperUri(ureq, wikiContainer); tabs.addTab(translate("tab.versions"), versioningContent); if (securityCallback.mayEditAndCreateArticle()) { revertVersionButton = LinkFactory.createButton("revert.old.version", versioningContent, this); } tableConfig = new TableGuiConfiguration(); tableConfig.setPageingEnabled(true); tableConfig.setResultsPerPage(10); tableConfig.setSelectedRowUnselectable(true); content.put("wikiTabs", tabs); // if not content yet switch to the edit tab if (page.getContent().equals("") && securityCallback.mayEditAndCreateArticle()) { tabs.setSelectedPane(2); tryToSetEditLock(page, ureq, ores); } updatePageContext(ureq, page); setTabsEnabled(true); // apply security settings to tabs by may // disabling edit tab mainPanel = putInitialPanel(content); // set pageId to the latest used this.pageId = page.getPageId(); } private void updateWikiMenu(Wiki wiki) { Collection<String> links = wiki.getListOfAllPageNames(); if (wikiMenuNode != null) { wikiMenuNode.removeAllChildren(); for (String link : links) { String ident = "w" + Encoder.md5hash(link); GenericTreeNode menuItemNode = new GenericTreeNode(ident, link, link); wikiMenuNode.addChild(menuItemNode); } } wikiMenuDropdown.removeAllComponents(); for (String link : links) { Link menuLink = LinkFactory.createToolLink(link, "select-page", link, this); wikiMenuDropdown.addComponent(menuLink); } if (editMenuButton != null) { wikiMenuDropdown.addComponent(new Spacer("wiki-spacer")); wikiMenuDropdown.addComponent(editMenuButton); } } public GenericTreeModel getAndUseExternalTree() { final String resId = ores.getResourceableId().toString(); Wiki wiki = getWiki(); wikiMenuModel = new GenericTreeModel(); String root = "wiki-" + resId; GenericTreeNode rootNode = new GenericTreeNode(root); wikiMenuModel.setRootNode(rootNode); // Index String navMainItem = "nav-main-item-" + resId; navMainPageNode = new GenericTreeNode(navMainItem, translate("navigation.mainpage"), navMainItem); rootNode.addChild(navMainPageNode); // Wiki-Menu String wikiMenuTitle = translate("navigation.menu"); String wikiMenuItem = "menu-item-" + resId; wikiMenuNode = new GenericTreeNode(wikiMenuItem, wikiMenuTitle, wikiMenuItem); rootNode.addChild(wikiMenuNode); String navAZItem = "nav-az-item-" + resId; navAZNode = new GenericTreeNode(navAZItem, translate("navigation.a-z"), navAZItem); rootNode.addChild(navAZNode); String navChangesItem = "nav-changes-item-" + resId; navChangesNode = new GenericTreeNode(navChangesItem, translate("navigation.changes"), navChangesItem); rootNode.addChild(navChangesNode); updateWikiMenu(wiki); navigationDropdown.setVisible(false); wikiMenuDropdown.setVisible(false); navigationContent.contextPut("navigationEnabled", Boolean.FALSE); return wikiMenuModel; } @Override public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { if (entries == null || entries.isEmpty()) return; Wiki wiki = getWiki(); if (wiki == null) { return; } ContextEntry ce = entries.get(0); String typ = ce.getOLATResourceable().getResourceableTypeName(); if ("az".equalsIgnoreCase(typ)) { openAtoZPage(ureq, wiki); } else if ("lastChanges".equalsIgnoreCase(typ)) { openLastChangesPage(ureq, wiki); } else if ("index".equalsIgnoreCase(typ)) { WikiPage page = openIndexPage(ureq, wiki); pageId = page.getPageId(); } else if ("Forum".equalsIgnoreCase(typ)) { Long forumKey = ce.getOLATResourceable().getResourceableId(); for (WikiPage page : wiki.getAllPagesWithContent()) { if (forumKey.longValue() == page.getForumKey()) { if (page != null) { this.pageId = page.getPageId(); } updatePageContext(ureq, page); OLATResourceable tabOres = OresHelper.createOLATResourceableInstance("tab", 1l); ContextEntry tabCe = BusinessControlFactory.getInstance().createContextEntry(tabOres); tabs.activate(ureq, Collections.singletonList(tabCe), null); if (forumController != null && entries.size() > 1) { List<ContextEntry> subEntries = entries.subList(1, entries.size()); forumController.activate(ureq, subEntries, null); } break; } } } else { String path = BusinessControlFactory.getInstance().getPath(ce); if (path.startsWith("page=")) { path = path.substring(5, path.length()); } String activatePageId = WikiManager.generatePageId(FilterUtil.normalizeWikiLink(path)); if (wiki.pageExists(activatePageId)) { WikiPage page = wiki.getPage(activatePageId, true); if (page != null) { this.pageId = page.getPageId(); } updatePageContext(ureq, page); if (entries.size() > 1) { List<ContextEntry> subEntries = entries.subList(1, entries.size()); String subTyp = subEntries.get(0).getOLATResourceable().getResourceableTypeName(); if ("tab".equalsIgnoreCase(subTyp)) { tabs.activate(ureq, subEntries, ce.getTransientState()); } else if ("message".equalsIgnoreCase(subTyp)) { OLATResourceable tabOres = OresHelper.createOLATResourceableInstance("tab", 1l); ContextEntry tabCe = BusinessControlFactory.getInstance().createContextEntry(tabOres); tabs.activate(ureq, Collections.singletonList(tabCe), null); forumController.activate(ureq, subEntries, null); } } } } } @Override public void event(UserRequest ureq, Component source, Event event) { String command = event.getCommand(); setTabsEnabled(true); // to make shure we use the lateste page, reload from cache Wiki wiki = getWiki(); if (wiki == null) { mainPanel.setContent(createVelocityContainer("deleted")); return; } WikiPage page = null; // FIXME:gs images and media should also be wiki pages -> see jamwiki if (!(event instanceof RequestNewPageEvent) && !(event instanceof RequestMediaEvent) && !(event instanceof RequestImageEvent)) { page = wiki.getPage(pageId, true); // set recent page id to the page currently used if (page != null) pageId = page.getPageId(); } if (source == content) { // noting yet } else if (source == tabs) { /************************************************************************* * tabbed pane events ************************************************************************/ TabbedPaneChangedEvent tabEvent = (TabbedPaneChangedEvent) event; Component comp = tabEvent.getNewComponent(); String compName = comp.getComponentName(); selectTab(ureq, command, compName, page, wiki); } else if (source == wikiArticleComp) { /************************************************************************* * wiki component events ************************************************************************/ if (event instanceof RequestPageEvent) { RequestPageEvent pageEvent = (RequestPageEvent) event; page = openPage(ureq, pageEvent.getCommand(), wiki); } else if (event instanceof RequestNewPageEvent) { page = handleRequestNewPageEvent(ureq, (RequestNewPageEvent) event, wiki); } else if (event instanceof ErrorEvent) { showWarning(event.getCommand()); } else if (event instanceof RequestMediaEvent) { deliverMediaFile(ureq, event.getCommand()); } else if (event instanceof RequestImageEvent) { // OLAT-6233 if image-view page is shown 2nd time (click on // image ), return to content-wiki-page // instead of linking to the image-view-page itself if (isImageDetailView) { page = wiki.getPage(pageId, true); updatePageContext(ureq, page); isImageDetailView = false; } else { final WikiPage imagePage = new WikiPage(event.getCommand()); imagePage.setContent("[[Image:" + event.getCommand() + "]]"); articleContent.contextPut("page", imagePage); wikiArticleComp.setWikiContent(imagePage.getContent()); setTabsEnabled(false); isImageDetailView = true; } } } else if (source == navigationContent) { /************************************************************************* * article container events ************************************************************************/ if (command.equals(ACTION_EDIT_MENU)) { page = wiki.getPage(WikiPage.WIKI_MENU_PAGE); editContent.contextPut("linkList", wiki.getListOfAllPageNames()); tryToSetEditLock(page, ureq, ores); updatePageContext(ureq, page); tabs.setSelectedPane(2); } } else if (source == toMainPageLink) { // home link page = openIndexPage(ureq, wiki); } else if (source == a2zLink) { openAtoZPage(ureq, wiki); } else if (source == changesLink) { openLastChangesPage(ureq, wiki); } else if (source == editMenuButton) { page = wiki.getPage(WikiPage.WIKI_MENU_PAGE); editContent.contextPut("linkList", wiki.getListOfAllPageNames()); tryToSetEditLock(page, ureq, ores); updatePageContext(ureq, page); // wikiEditForm.setPage(page); tabs.setSelectedPane(2); } else if (source == archiveLink) { // archive a snapshot of the wiki in the users personal folder archiveWikiDialogCtr = activateOkCancelDialog(ureq, null, translate("archive.question"), archiveWikiDialogCtr); return; } else if (source == createLink) { removeAsListenerAndDispose(calloutCtrl); removeAsListenerAndDispose(createArticleForm); createArticleForm = new WikiArticleSearchForm(ureq, getWindowControl()); listenTo(createArticleForm); calloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(), createArticleForm.getInitialComponent(), createLink, "", true, null); listenTo(calloutCtrl); calloutCtrl.activate(); } else if (source == versioningContent) { // nothing to do } else if (source == editContent) { // nothing to do } else if (source == closePreviewButton) { editContent.remove(wikiVersionDisplayComp); } else if (source == revertVersionButton) { wikiEditForm.setPage(selectedPage); tabs.setSelectedPane(2); tryToSetEditLock(page, ureq, ores); } else if (source instanceof Link && "select-page".equals(command)) { String name = source.getComponentName(); page = openPage(ureq, name, wiki); } // set recent page id to the page currently used if (page != null) { this.pageId = page.getPageId(); } } private void selectTab(UserRequest ureq, String command, String compName, WikiPage page, Wiki wiki) { // first release a potential lock on this page. only when the edit tab // is acitve // a lock will be created. in all other cases it is save to release an // existing lock doReleaseEditLock(); if (command.equals(TabbedPaneChangedEvent.TAB_CHANGED)) { updatePageContext(ureq, page); } if (command.equals(TabbedPaneChangedEvent.TAB_CHANGED) && compName.equals("vc_article")) { /*********************************************************************** * tabbed pane change to article **********************************************************************/ // if(page.getContent().equals("")) // wikiArticleComp.setVisible(false); // FIXME:guido: ... && comp == articleContent)) etc. } else if (command.equals(TabbedPaneChangedEvent.TAB_CHANGED) && compName.equals("vc_edit")) { /*********************************************************************** * tabbed pane change to edit tab **********************************************************************/ wikiEditForm.resetUpdateComment(); editContent.contextPut("linkList", wiki.getListOfAllPageNames()); editContent.contextPut("fileList", wiki.getMediaFileList()); // try to edit acquire lock for this page tryToSetEditLock(page, ureq, ores); } else if (command.equals(TabbedPaneChangedEvent.TAB_CHANGED) && compName.equals("vc_versions")) { /*********************************************************************** * tabbed pane change to versioning tab **********************************************************************/ versioningTableModel = new HistoryTableDateModel(wiki.getHistory(page), getTranslator()); removeAsListenerAndDispose(versioningTableCtr); versioningTableCtr = new TableController(tableConfig, ureq, getWindowControl(), getTranslator()); listenTo(versioningTableCtr); versioningTableModel.addColumnDescriptors(versioningTableCtr); versioningTableCtr.setTableDataModel(versioningTableModel); versioningTableCtr.modelChanged(); versioningTableCtr.setSortColumn(1, false); versioningContent.put("versions", versioningTableCtr.getInitialComponent()); versioningContent.contextPut("diffs", diffs); } else if (command.equals(TabbedPaneChangedEvent.TAB_CHANGED) && compName.equals("vc_discuss")) { /*********************************************************************** * tabbed pane change to discussion tab **********************************************************************/ Forum forum = null; if (page.getForumKey() > 0) { forum = ForumManager.getInstance().loadForum(Long.valueOf(page.getForumKey())); } if (forum == null) { forum = ForumManager.getInstance().addAForum(); page.setForumKey(forum.getKey().longValue()); WikiManager.getInstance().updateWikiPageProperties(ores, page); } ForumCallback forumCallback = securityCallback.getForumCallback(); ContextEntry ce = BusinessControlFactory.getInstance().createContextEntry(forum); WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ce, getWindowControl()); removeAsListenerAndDispose(forumController); forumController = new ForumController(ureq, bwControl, forum, forumCallback, false); listenTo(forumController); discussionContent.put("articleforum", forumController.getInitialComponent()); } OLATResourceable pageRes = OresHelper.createOLATResourceableTypeWithoutCheck("path=" + page.getPageName()); WindowControl wc = addToHistory(ureq, pageRes, null); OLATResourceable tabOres = tabs.getTabResource(); addToHistory(ureq, tabOres, null, wc, true); } private WikiPage openPage(UserRequest ureq, String name, Wiki wiki) { WikiPage page = wiki.getPage(name, true); page.incrementViewCount(); updatePageContext(ureq, page); Link pageLink = LinkFactory.createToolLink(page.getPageName(), "select-page", page.getPageName(), this); breadcrumpDropdown.addComponent(pageLink); setTabsEnabled(true); tabs.setSelectedPane(0); return page; } private WikiPage openIndexPage(UserRequest ureq, Wiki wiki) { WikiPage page = wiki.getPage(WikiPage.WIKI_INDEX_PAGE, true); page.incrementViewCount(); Link pageLink = LinkFactory.createToolLink(page.getPageName(), "select-page", page.getPageName(), this); breadcrumpDropdown.addComponent(pageLink); updatePageContext(ureq, page); setTabsEnabled(true); tabs.setSelectedPane(0); addToHistory(ureq, OresHelper.createOLATResourceableTypeWithoutCheck("index"), null); return page; } private void openLastChangesPage(UserRequest ureq, Wiki wiki) { WikiPage recentChanges = wiki.getPage(WikiPage.WIKI_RECENT_CHANGES_PAGE); recentChanges.setContent(translate("nav.changes.desc") + wiki.getRecentChanges(ureq.getLocale())); clearPortfolioLink(); articleContent.contextPut("page", recentChanges); wikiArticleComp.setWikiContent(recentChanges.getContent()); setTabsEnabled(false); tabs.setSelectedPane(0); addToHistory(ureq, OresHelper.createOLATResourceableTypeWithoutCheck("lastChanges"), null); } private void openAtoZPage(UserRequest ureq, Wiki wiki) { WikiPage a2zPage = wiki.getPage(WikiPage.WIKI_A2Z_PAGE); articleContent.contextPut("page", a2zPage); a2zPage.setContent(translate("nav.a-z.desc") + wiki.getAllPageNamesSorted()); wikiArticleComp.setWikiContent(a2zPage.getContent()); clearPortfolioLink(); setTabsEnabled(false); tabs.setSelectedPane(0); addToHistory(ureq, OresHelper.createOLATResourceableTypeWithoutCheck("az"), null); } private void deliverMediaFile(UserRequest ureq, String command) { VFSItem item = WikiManager.getInstance().getMediaFolder(ores).resolve(command); if (item == null) { // try to replace blanck with _ item = WikiManager.getInstance().getMediaFolder(ores).resolve(command.replace(" ", "_")); } if (item instanceof VFSLeaf) { ureq.getDispatchResult().setResultingMediaResource(new VFSMediaResource((VFSLeaf) item)); } else { showError("wiki.error.file.not.found"); } } private void refreshTableDataModel(UserRequest ureq, Wiki wiki) { removeAsListenerAndDispose(mediaTableCtr); mediaTableCtr = new TableController(new TableGuiConfiguration(), ureq, getWindowControl(), getTranslator()); listenTo(mediaTableCtr); mediaTableCtr.setMultiSelect(true); mediaTableCtr.addMultiSelectAction(ACTION_DELETE_MEDIAS, ACTION_DELETE_MEDIAS); List<VFSItem> filelist = wiki.getMediaFileListWithMetadata(); Map<String, MediaFileElement> files = new HashMap<String, MediaFileElement>(); for (Iterator<VFSItem> iter = filelist.iterator(); iter.hasNext();) { VFSLeaf elem = (VFSLeaf) iter.next(); if (elem.getName().endsWith(METADATA_SUFFIX)) { // *.metadata files // go here Properties p = new Properties(); try { p.load(elem.getInputStream()); MediaFileElement mediaFileElement = new MediaFileElement(elem.getName(), p.getProperty(MEDIA_FILE_CREATED_BY), p.getProperty(MEDIA_FILE_CREATIONDATE)); mediaFileElement.setDeletedBy(p.getProperty(MEDIA_FILE_DELETED_BY)); mediaFileElement.setDeletionDate(p.getProperty(MEDIA_FILE_DELETIONDATE)); files.put(p.getProperty(MEDIA_FILE_FILENAME), mediaFileElement); } catch (IOException e) { throw new OLATRuntimeException("Could'n read properties from media file: " + elem.getName(), e); } } } for (Iterator<VFSItem> iter = filelist.iterator(); iter.hasNext();) { VFSLeaf elem = (VFSLeaf) iter.next(); if (!elem.getName().endsWith(METADATA_SUFFIX)) { if (!files.containsKey(elem.getName())) { // legacy file without metadata files.put(elem.getName(), new MediaFileElement(elem.getName(), 0, elem.getLastModified())); } else { // file with metadata, update name MediaFileElement element = files.get(elem.getName()); element.setFileName(elem.getName()); } } } mediaFilesTableModel = new MediaFilesTableModel(new ArrayList<MediaFileElement>(files.values()), getTranslator()); mediaFilesTableModel.addColumnDescriptors(mediaTableCtr); mediaTableCtr.setTableDataModel(mediaFilesTableModel); mediaTableCtr.setSortColumn(3, false); mediaTableCtr.modelChanged(); } private WikiPage handleRequestNewPageEvent(UserRequest ureq, RequestNewPageEvent requestPage, Wiki wiki) { if (!securityCallback.mayEditAndCreateArticle()) { if (ureq.getUserSession().getRoles().isGuestOnly()) { showInfo("guest.no.edit"); } else { showInfo("no.edit"); } return null; } // first check if no page exist WikiPage page = wiki.findPage(requestPage.getCommand()); if (page.getPageName().equals(Wiki.NEW_PAGE)) { // create new page log.debug("Page does not exist, create a new one..."); page = new WikiPage(requestPage.getCommand()); page.setCreationTime(System.currentTimeMillis()); page.setInitalAuthor(ureq.getIdentity().getKey().longValue()); wiki.addPage(page); WikiManager.getInstance().saveWikiPage(ores, page, false, wiki); log.debug("Safe new page=" + page); log.debug("Safe new pageId=" + page.getPageId()); } updatePageContext(ureq, page); doReleaseEditLock(); tryToSetEditLock(page, ureq, ores); tabs.setSelectedPane(2); return page; } @Override public void event(UserRequest ureq, Controller source, Event event) { Wiki wiki = getWiki(); if (wiki == null) { mainPanel.setContent(createVelocityContainer("deleted")); return; } // reload page from cache WikiPage page = wiki.getPage(pageId, true); // set recent page id to the page currently used this.pageId = page.getPageId(); if (event instanceof TreeEvent) { TreeEvent te = (TreeEvent) event; String nodeId = te.getNodeId(); if (navAZNode.getIdent().equals(nodeId)) { openAtoZPage(ureq, wiki); } else if (navChangesNode.getIdent().equals(nodeId)) { openLastChangesPage(ureq, wiki); } else if (navMainPageNode.getIdent().equals(nodeId)) { page = openIndexPage(ureq, wiki); } else if (wikiMenuNode.getIdent().equals(nodeId)) { page = openPage(ureq, WikiPage.WIKI_MENU_PAGE, wiki); } else { TreeNode node = wikiMenuModel.getNodeById(nodeId); if (node != null && node.getUserObject() instanceof String) { String link = (String) node.getUserObject(); page = openPage(ureq, link, wiki); } } if (page != null) { this.pageId = page.getPageId(); } } else if (source == versioningTableCtr) { /************************************************************************* * history table events ************************************************************************/ if (event.getCommand().equals(Table.COMMANDLINK_ROWACTION_CLICKED)) { TableEvent te = (TableEvent) event; if (te.getActionId().equals(ACTION_COMPARE)) { selectedPage = versioningTableModel.getObject(te.getRowId()); diffs = wiki.getDiff(page, selectedPage.getVersion() - 1, selectedPage.getVersion()); versioningContent.contextPut("diffs", diffs); versioningContent.remove(wikiVersionDisplayComp); versioningContent.contextPut("page", selectedPage); } else if (te.getActionId().equals(ACTION_SHOW)) { versioningContent.contextRemove("diffs"); selectedPage = versioningTableModel.getObject(te.getRowId()); selectedPage = wiki.loadVersion(selectedPage, selectedPage.getVersion()); wikiVersionDisplayComp.setWikiContent(selectedPage.getContent()); wikiVersionDisplayComp.setImageMapperUri(ureq, wikiContainer); versioningContent.put("versionDisplay", wikiVersionDisplayComp); versioningContent.contextPut("page", selectedPage); } } } else if (source == wikiUploadFileCtr) { if (event.getCommand().equals(FolderEvent.UPLOAD_EVENT)) { FolderEvent fEvent = (FolderEvent) event; createMediaMetadataFile(fEvent.getFilename(), ureq.getIdentity().getKey()); editContent.contextPut("fileList", wiki.getMediaFileList()); } cmc.deactivate(); cleanUp(); } else if (source == removePageDialogCtr) { if (DialogBoxUIFactory.isOkEvent(event)) { wiki.removePage(page); breadcrumpDropdown.removeComponent(page.getPageName()); WikiManager.getInstance().deleteWikiPage(ores, page); page = wiki.getPage(WikiPage.WIKI_INDEX_PAGE); updatePageContext(ureq, page); tabs.setSelectedPane(0); } } else if (source == mediaTableCtr) { if (event.getCommand().equals(Table.COMMANDLINK_ROWACTION_CLICKED)) { TableEvent te = (TableEvent) event; if (te.getActionId().equals(ACTION_DELETE_MEDIA)) { List<MediaFileElement> list = new ArrayList<MediaFileElement>(1); list.add(mediaFilesTableModel.getObject(te.getRowId())); deleteMediaFile(list, ureq); } else if (te.getActionId().equals(ACTION_SHOW_MEDIA)) { // show a selected file from the media folder MediaFileElement element = mediaFilesTableModel.getObject(te.getRowId()); if (isImage(element.getFilename())) { // show images inline // as modal overlay imageDisplay.contextPut("mediaElement", element); imageDisplay.contextPut("imageUri", wikiArticleComp.getImageBaseUri()); removeAsListenerAndDispose(cmc); cmc = new CloseableModalController(getWindowControl(), translate("close"), imageDisplay); listenTo(cmc); cmc.activate(); } else { deliverMediaFile(ureq, element.getFilename()); } } } else if (event.getCommand().equals(Table.COMMAND_MULTISELECT)) { TableMultiSelectEvent tmse = (TableMultiSelectEvent) event; if (tmse.getAction().equals(ACTION_DELETE_MEDIAS)) { deleteMediaFile(mediaFilesTableModel.getObjects(tmse.getSelection()), ureq); editContent.contextPut("fileList", wiki.getMediaFileList()); } } } else if (source == archiveWikiDialogCtr) { if (DialogBoxUIFactory.isOkEvent(event)) { WikiToCPResource rsrc = new WikiToCPResource(ores, getIdentity(), getTranslator()); ureq.getDispatchResult().setResultingMediaResource(rsrc); } } else if (source == createArticleForm) { calloutCtrl.deactivate(); String query = createArticleForm.getQuery(); if (!StringHelper.containsNonWhitespace(query)) { query = WikiPage.WIKI_INDEX_PAGE; } page = wiki.findPage(query); pageId = page.getPageId(); if (page.getPageName().equals(Wiki.NEW_PAGE)) { setTabsEnabled(false); } page.incrementViewCount(); updatePageContext(ureq, page); if (!page.getPageName().startsWith("O_")) { Link pageLink = LinkFactory.createToolLink(page.getPageName(), "select-page", page.getPageName(), this); breadcrumpDropdown.addComponent(pageLink); } tabs.setSelectedPane(0); } else if (source == wikiEditForm) { // set recent page id to the page currently used this.pageId = page.getPageId(); boolean wantPreview = false; boolean wantSave = false; boolean wantClose = false; if (event == Event.CANCELLED_EVENT) { wantClose = true; } else if (event == Event.DONE_EVENT) { wantSave = true; } else if (event.getCommand().equals("save.and.close")) { wantClose = true; wantSave = true; event = Event.DONE_EVENT; } else if (event.getCommand().equals("preview")) { wantPreview = true; event = Event.DONE_EVENT; } else if (event.getCommand().equals("delete.page")) { String msg = translate("question", page.getPageName()); removePageDialogCtr = activateOkCancelDialog(ureq, null, msg, removePageDialogCtr); return; } else if (event.getCommand().equals("media.upload")) { doUploadFiles(ureq); return; } else if (event.getCommand().equals("manage.media")) { doManageMedias(ureq, wiki); return; } boolean dirty = !wikiEditForm.getWikiContent().equals(page.getContent()); if (wantPreview) { WikiPage preview = new WikiPage("temp"); preview.setContent(wikiEditForm.getWikiContent()); wikiVersionDisplayComp.setWikiContent(preview.getContent()); editContent.put("versionDisplay", wikiVersionDisplayComp); } if (wantSave && dirty) { editContent.contextPut("isDirty", Boolean.valueOf(false)); page.setContent(wikiEditForm.getWikiContent()); page.setModifyAuthor(getIdentity().getKey().longValue()); page.setUpdateComment(wikiEditForm.getUpdateComment()); if (page.getInitalAuthor() == 0) page.setInitalAuthor(getIdentity().getKey().longValue()); // menu page only editable by admin and owner set new content if // changed if (page.getPageName().equals(WikiPage.WIKI_MENU_PAGE)) { updateWikiMenu(wiki); } WikiManager.getInstance().saveWikiPage(ores, page, true, wiki); // inform subscription context about changes NotificationsManager.getInstance().markPublisherNews(subsContext, ureq.getIdentity(), true); updatePageContext(ureq, page); } if (dirty && wantPreview && !wantSave) { // wikiEditForm.setDirty(true); editContent.contextPut("isDirty", Boolean.valueOf(dirty)); } if (wantClose) { tabs.setSelectedPane(0); doReleaseEditLock(); return; } } } private void cleanUp() { removeAsListenerAndDispose(cmc); removeAsListenerAndDispose(wikiUploadFileCtr); cmc = null; wikiUploadFileCtr = null; } private boolean isImage(String filename) { String fileSuffix = filename.substring(filename.lastIndexOf(".") + 1, filename.length()).toLowerCase(); if (fileSuffix.equals("jpg")) return true; if (fileSuffix.equals("jpeg")) return true; if (fileSuffix.equals("gif")) return true; if (fileSuffix.equals("png")) return true; return false; } private void createMediaMetadataFile(String filename, Long author) { VFSContainer mediaFolder = WikiManager.getInstance().getMediaFolder(ores); // only create metadatafile if base file exists if ((VFSLeaf) mediaFolder.resolve(filename) != null) { // metafile may exists when files get overwritten VFSLeaf metaFile = (VFSLeaf) mediaFolder.resolve(filename + METADATA_SUFFIX); if (metaFile == null) { // metafile does not exist => create one metaFile = mediaFolder.createChildLeaf(filename + METADATA_SUFFIX); } Properties p = new Properties(); p.setProperty(MEDIA_FILE_FILENAME, filename); p.setProperty(MEDIA_FILE_CREATIONDATE, String.valueOf(System.currentTimeMillis())); p.setProperty(MEDIA_FILE_CREATED_BY, String.valueOf(author)); try { p.store(metaFile.getOutputStream(false), "wiki media files meta properties"); } catch (IOException e) { throw new OLATRuntimeException(WikiManager.class, "failed to save media files properties for file: " + filename + " and olatresource: " + ores.getResourceableId(), e); } } } private void deleteMediaFile(List<MediaFileElement> toDelete, UserRequest ureq) { for (Iterator<MediaFileElement> iter = toDelete.iterator(); iter.hasNext();) { VFSContainer mediaFolder = WikiManager.getInstance().getMediaFolder(ores); MediaFileElement element = iter.next(); if (log.isDebug()) log.debug("deleting media file: " + element.getFilename()); if (!element.getFilename().endsWith(METADATA_SUFFIX)) { VFSLeaf file = (VFSLeaf) mediaFolder.resolve(element.getFilename()); if (file != null) { file.delete(); VFSLeaf metadata = (VFSLeaf) mediaFolder.resolve(element.getFilename() + METADATA_SUFFIX); if (metadata != null) { Properties p = new Properties(); try { p.load(metadata.getInputStream()); p.setProperty(MEDIA_FILE_DELETIONDATE, String.valueOf(System.currentTimeMillis())); p.setProperty(MEDIA_FILE_DELETED_BY, String.valueOf(ureq.getIdentity().getKey())); OutputStream os = metadata.getOutputStream(false); p.store(os, "wiki media file meta properties"); os.close(); } catch (IOException e) { throw new OLATRuntimeException( "Could'n read properties from media file: " + metadata.getName(), e); } } } } } getWindowControl().pop(); } private void setTabsEnabled(boolean enable) { tabs.setEnabled(1, enable); if (enable && securityCallback.mayEditAndCreateArticle()) { tabs.setEnabled(2, enable); } else { tabs.setEnabled(2, false); } tabs.setEnabled(3, enable); } private void doUploadFiles(UserRequest ureq) { removeAsListenerAndDispose(cmc); removeAsListenerAndDispose(wikiUploadFileCtr); wikiUploadFileCtr = new WikiFileUploadController(ureq, getWindowControl(), WikiManager.getInstance().getMediaFolder(ores)); listenTo(wikiUploadFileCtr); String title = translate("media.upload"); cmc = new CloseableModalController(getWindowControl(), translate("close"), wikiUploadFileCtr.getInitialComponent(), true, title, true); listenTo(cmc); cmc.activate(); } private void doManageMedias(UserRequest ureq, Wiki wiki) { if (wiki.getMediaFileListWithMetadata().size() > 0) { mediaMgntContent = createVelocityContainer("media"); refreshTableDataModel(ureq, wiki); mediaMgntContent.put("mediaMgmtTable", mediaTableCtr.getInitialComponent()); removeAsListenerAndDispose(cmc); cmc = new CloseableModalController(getWindowControl(), translate("close"), mediaMgntContent, true, translate("manage.media")); listenTo(cmc); cmc.activate(); } } @Override protected void doDispose() { if (wikiArticleComp != null) { wikiArticleComp.dispose(); } if (wikiVersionDisplayComp != null) { wikiVersionDisplayComp.dispose(); } ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_CLOSE, getClass()); doReleaseEditLock(); } @Override public Controller cloneController(UserRequest ureq, WindowControl wControl) { return WikiManager.getInstance().createWikiMainController(ureq, wControl, ores, securityCallback, null); } private void doReleaseEditLock() { if (lockEntry != null && lockEntry.isSuccess()) { CoordinatorManager.getInstance().getCoordinator().getLocker().releaseLock(lockEntry); lockEntry = null; } } /** * Try to aquire a lock on the page * * @param ureq * @param wikiOres */ private void tryToSetEditLock(WikiPage page, UserRequest ureq, OLATResourceable wikiOres) { lockEntry = CoordinatorManager.getInstance().getCoordinator().getLocker().acquireLock(wikiOres, ureq.getIdentity(), page.getPageName()); editContent.contextPut("lockEntry", lockEntry); } /** * update depended velocity contexts and componetens with latest globally * used page * */ private void updatePageContext(UserRequest ureq, WikiPage page) { if (page.getPageName().equals(WikiPage.WIKI_ERROR)) { wikiArticleComp.setWikiContent(translate(page.getContent())); } else { wikiArticleComp.setWikiContent(page.getContent()); } wikiEditForm.setPage(page); diffs.clear(); content.contextPut("page", page); articleContent.contextPut("page", page); discussionContent.contextPut("page", page); editContent.remove(wikiVersionDisplayComp); editContent.contextPut("page", page); versioningContent.remove(wikiVersionDisplayComp); versioningContent.contextPut("page", page); boolean userIsPageCreator = getIdentity().getKey().equals(page.getInitalAuthor()); if (userIsPageCreator) { String subPath = page.getPageName(); String businessPath = getWindowControl().getBusinessControl().getAsString(); businessPath += "[page=" + subPath + ":0]"; OLATResourceable wikiRes = OresHelper.createOLATResourceableInstance(WikiArtefact.ARTEFACT_TYPE, ores.getResourceableId()); if (portfolioModule.isEnabled()) { MediaCollectorComponent collectorCmp = new MediaCollectorComponent("portfolio-link", getWindowControl(), page, wikiMediaHandler, businessPath); navigationContent.put("portfolio-link", collectorCmp); } else { Controller ePFCollCtrl = EPUIFactory.createArtefactCollectWizzardController(ureq, getWindowControl(), wikiRes, businessPath); if (ePFCollCtrl != null) { navigationContent.put("portfolio-link", ePFCollCtrl.getInitialComponent()); } } } else { clearPortfolioLink(); } OLATResourceable pageRes = OresHelper.createOLATResourceableInstanceWithoutCheck("path=" + page.getPageName(), 0l); addToHistory(ureq, pageRes, null); } private void clearPortfolioLink() { navigationContent.put("portfolio-link", new Panel("empty")); } private Wiki getWiki() { return WikiManager.getInstance().getOrLoadWiki(ores); } }