/** * <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.fo.ui; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.olat.basesecurity.BaseSecurityModule; import org.olat.core.commons.modules.bc.meta.MetaInfo; import org.olat.core.commons.modules.bc.meta.tagged.MetaTagged; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.commons.services.mark.Mark; import org.olat.core.commons.services.mark.MarkResourceStat; import org.olat.core.commons.services.mark.MarkingService; import org.olat.core.dispatcher.mapper.Mapper; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.link.LinkPopupSettings; 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.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.gui.control.generic.wizard.Step; import org.olat.core.gui.control.generic.wizard.StepRunnerCallback; import org.olat.core.gui.control.generic.wizard.StepsMainRunController; import org.olat.core.gui.control.generic.wizard.StepsRunContext; import org.olat.core.gui.media.MediaResource; import org.olat.core.gui.media.NotFoundMediaResource; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.id.User; import org.olat.core.id.UserConstants; 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.activity.ThreadLocalUserActivityLogger; import org.olat.core.util.ConsumableBoolean; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.event.GenericEventListener; import org.olat.core.util.prefs.Preferences; 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.core.util.vfs.filters.VFSItemMetaFilter; import org.olat.course.nodes.FOCourseNode; import org.olat.modules.fo.Forum; import org.olat.modules.fo.ForumCallback; import org.olat.modules.fo.ForumChangedEvent; import org.olat.modules.fo.ForumLoggingAction; import org.olat.modules.fo.Message; import org.olat.modules.fo.MessageLight; import org.olat.modules.fo.MessageRef; import org.olat.modules.fo.Status; import org.olat.modules.fo.archiver.formatters.ForumDownloadResource; import org.olat.modules.fo.export.FinishCallback; import org.olat.modules.fo.export.SendMailStepForm; import org.olat.modules.fo.export.Step_1_SelectCourse; import org.olat.modules.fo.manager.ForumManager; import org.olat.modules.fo.portfolio.ForumMediaHandler; import org.olat.modules.fo.ui.MessageEditController.EditMode; import org.olat.modules.fo.ui.events.DeleteMessageEvent; import org.olat.modules.fo.ui.events.DeleteThreadEvent; import org.olat.modules.fo.ui.events.ErrorEditMessage; import org.olat.modules.fo.ui.events.SelectMessageEvent; import org.olat.modules.portfolio.PortfolioV2Module; import org.olat.modules.portfolio.ui.component.MediaCollectorComponent; import org.olat.portfolio.EPUIFactory; import org.olat.portfolio.manager.EPFrontendManager; import org.olat.properties.Property; import org.olat.properties.PropertyManager; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; import org.olat.resource.OLATResourceManager; import org.olat.user.DisplayPortraitController; import org.olat.user.UserManager; import org.olat.user.propertyhandlers.UserPropertyHandler; import org.olat.util.logging.activity.LoggingResourceable; import org.springframework.beans.factory.annotation.Autowired; /** * * The list of messages in a thread. * * Initial date: 10.11.2015<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class MessageListController extends BasicController implements GenericEventListener, Activateable2 { protected static final String USER_PROPS_ID = ForumUserListController.class.getCanonicalName(); protected static final String VIEWMODE_THREAD = "thread"; protected static final String VIEWMODE_FLAT = "flat"; protected static final String VIEWMODE_MESSAGE = "message"; private static final String GUI_PREFS_VIEWMODE_KEY = "forum.threadview.mode"; private static final String GUI_PREFS_VIEWMODE_CLASS = "org.olat.modules.fo.ForumThreadViewModeController"; private final VelocityContainer mainVC; private Link backLink, archiveThreadButton, stickyButton, removeStickyButton, closeThreadButton, openThreadButton, hideThreadButton, showThreadButton, allButton, allFlatButton, oneButton, markedButton, newButton; private CloseableModalController cmc; private MessageEditController editMessageCtrl, replyMessageCtrl; private DialogBoxController confirmDeleteCtrl, confirmSplitCtrl; private ForumMessageListController moveCtrl, messageTableCtrl; private StepsMainRunController exportCtrl; private Message thread; private boolean reloadList; private boolean hasMarkedNewMessages; private final Forum forum; private final boolean guestOnly; private final Formatter formatter; private final String thumbnailMapper; private final ForumCallback foCallback; private final OLATResourceable forumOres; private final boolean isAdministrativeUser; private final List<UserPropertyHandler> userPropertyHandlers; private LoadMode loadMode; private List<MessageView> backupViews; @Autowired private UserManager userManager; @Autowired private ForumManager forumManager; @Autowired private RepositoryManager repositoryManager; @Autowired private OLATResourceManager olatManager; @Autowired private MarkingService markingService; @Autowired private BaseSecurityModule securityModule; @Autowired private EPFrontendManager epMgr; @Autowired private PortfolioV2Module portfolioModule; @Autowired private ForumMediaHandler forumMediaHandler; public MessageListController(UserRequest ureq, WindowControl wControl, Forum forum, ForumCallback foCallback) { super(ureq, wControl); setTranslator(Util.createPackageTranslator(Forum.class, getLocale(), getTranslator())); this.forum = forum; this.foCallback = foCallback; formatter = Formatter.getInstance(getLocale()); forumOres = OresHelper.createOLATResourceableInstance("Forum", forum.getKey()); guestOnly = ureq.getUserSession().getRoles().isGuestOnly(); isAdministrativeUser = securityModule.isUserAllowedAdminProps(ureq.getUserSession().getRoles()); userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_PROPS_ID, isAdministrativeUser); thumbnailMapper = registerCacheableMapper(ureq, "fo_att_" + forum.getKey(), new AttachmentsMapper()); mainVC = createVelocityContainer("threadview"); mainVC.contextPut("threadMode", Boolean.TRUE); mainVC.contextPut("thumbMapper", thumbnailMapper); mainVC.contextPut("guestOnly", new Boolean(guestOnly)); messageTableCtrl = new ForumMessageListController(ureq, getWindowControl(), forum, false); listenTo(messageTableCtrl); mainVC.put("singleThreadTable", messageTableCtrl.getInitialComponent()); putInitialPanel(mainVC); initButtons(); // Register for forum events CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, getIdentity(), forum); } /** * @return true if the controller has marked some new messages as seen */ public boolean hasMarkedNewMessages() { return hasMarkedNewMessages; } @Override protected void doDispose() { CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, forum); } private void initButtons() { backLink = LinkFactory.createCustomLink("backLinkLT", "back", "listalltitles", Link.LINK_BACK, mainVC, this); archiveThreadButton = LinkFactory.createButtonSmall("archive.thread", mainVC, this); archiveThreadButton.setIconLeftCSS("o_icon o_icon-fw o_icon_archive_tool"); stickyButton = LinkFactory.createLink("msg.sticky", mainVC, this); stickyButton.setIconLeftCSS("o_icon o_icon-fw o_forum_status_sticky_icon"); removeStickyButton = LinkFactory.createLink("remove.sticky", mainVC, this); removeStickyButton.setIconLeftCSS("o_icon o_icon-fw o_forum_status_thread_icon"); closeThreadButton = LinkFactory.createLink("close.thread", mainVC, this); closeThreadButton.setIconLeftCSS("o_icon o_icon-fw o_forum_status_closed_icon"); openThreadButton = LinkFactory.createLink("open.thread", mainVC, this); openThreadButton.setIconLeftCSS("o_icon o_icon-fw o_forum_status_opened_icon"); hideThreadButton = LinkFactory.createLink("hide.thread", mainVC, this); hideThreadButton.setIconLeftCSS("o_icon o_icon-fw o_forum_status_hidden_icon"); showThreadButton = LinkFactory.createLink("show.thread", mainVC, this); showThreadButton.setIconLeftCSS("o_icon o_icon-fw o_forum_status_visible_icon"); allButton = LinkFactory.createButtonSmall("viewswitch.threadview", mainVC, this); allButton.setIconLeftCSS("o_icon o_icon-fw o_icon-flip-vertical o_forum_all_icon"); allButton.setElementCssClass("o_forum_all_messages"); allFlatButton = LinkFactory.createButtonSmall("viewswitch.flatview", mainVC, this); allFlatButton.setIconLeftCSS("o_icon o_icon-fw o_forum_all_flat_icon"); allFlatButton.setElementCssClass("o_forum_all_flat_messages"); oneButton = LinkFactory.createButtonSmall("viewswitch.messageview", mainVC, this); oneButton.setIconLeftCSS("o_icon o_icon-fw o_forum_one_icon"); oneButton.setElementCssClass("o_forum_one_message"); markedButton = LinkFactory.createButtonSmall("viewswitch.marked", mainVC, this); markedButton.setIconLeftCSS("o_icon o_icon-fw o_forum_marked_icon"); markedButton.setElementCssClass("o_forum_marked_messages"); newButton = LinkFactory.createButtonSmall("viewswitch.new", mainVC, this); newButton.setIconLeftCSS("o_icon o_icon-fw o_forum_new_icon"); newButton.setElementCssClass("o_forum_new_messages"); } private void updateButtons(Link activeLink) { allButton.setCustomEnabledLinkCSS(activeButton(allButton, activeLink)); allFlatButton.setCustomEnabledLinkCSS(activeButton(allFlatButton, activeLink)); oneButton.setCustomEnabledLinkCSS(activeButton(oneButton, activeLink)); markedButton.setCustomEnabledLinkCSS(activeButton(markedButton, activeLink)); newButton.setCustomEnabledLinkCSS(activeButton(newButton, activeLink)); archiveThreadButton.setVisible(thread != null && foCallback.mayArchiveForum()); if(thread == null || guestOnly || !foCallback.mayEditMessageAsModerator()) { closeThreadButton.setVisible(false); openThreadButton.setVisible(false); hideThreadButton.setVisible(false); showThreadButton.setVisible(false); stickyButton.setVisible(false); removeStickyButton.setVisible(false); } else { Status status = Status.getStatus(thread.getStatusCode()); boolean isClosed = status.isClosed(); boolean isHidden = status.isHidden(); boolean isSticky = status.isSticky(); closeThreadButton.setVisible(!isClosed); openThreadButton.setVisible(isClosed); hideThreadButton.setVisible(!isHidden); showThreadButton.setVisible(isHidden); stickyButton.setVisible(foCallback.mayEditMessageAsModerator() && thread != null && !isSticky); removeStickyButton.setVisible(foCallback.mayEditMessageAsModerator() && thread != null && isSticky); } } private String activeButton(Link link, Link activeLink) { return "btn btn-sm btn-default o_forum_tool " + (link == activeLink ? "active" : ""); } private void reloadModel(UserRequest ureq, Message message) { reloadList = false; if(loadMode == LoadMode.thread) { loadThread(ureq, thread); String settings = doShowBySettings(ureq); if(VIEWMODE_MESSAGE.equals(settings)) { if(message != null && message.getKey() != null) { doSelectTheOne(ureq, message.getKey()); } } else { scrollTo(message); } } else if(message != null) { MessageView view = loadView(ureq, message); backupViews.add(view); mainVC.contextPut("messages", backupViews); messageTableCtrl.loadMessages(new ArrayList<>(0)); updateButtons(allFlatButton); mainVC.contextPut("threadMode", Boolean.FALSE); scrollTo(message); } } /** * The method doesn't scroll has the delete poped a blue box * @param ureq * @param message */ private void reloadModelAfterDelete(UserRequest ureq, MessageView message) { if(loadMode == LoadMode.thread) { loadThread(ureq, thread); doShowBySettings(ureq); } else if(message != null) { for(MessageView msg:backupViews) { if(msg.getKey().equals(message.getKey())) { backupViews.remove(msg); break; } } mainVC.contextPut("messages", backupViews); messageTableCtrl.loadMessages(new ArrayList<>(0)); updateButtons(allFlatButton); mainVC.contextPut("threadMode", Boolean.FALSE); } } public void scrollTo(MessageRef ref) { if(ref != null && (thread == null || !thread.getKey().equals(ref.getKey()))) { mainVC.contextPut("goToMessage", new ConsumableBoolean(true)); mainVC.contextPut("goToMessageId", ref.getKey()); } } public void loadUserMessages(UserRequest ureq, Identity user) { loadMode = LoadMode.userMessages; List<MessageLight> messages = forumManager.getLightMessagesByUser(forum, user); backupViews = loadThread(ureq, messages, false); messageTableCtrl.loadMessages(new ArrayList<>(0)); allButton.setVisible(false); updateButtons(allFlatButton); mainVC.contextPut("threadMode", Boolean.FALSE); mainVC.contextPut("filteredForFirstName", user.getUser().getProperty(UserConstants.FIRSTNAME, getLocale())); mainVC.contextPut("filteredForLastName", user.getUser().getProperty(UserConstants.LASTNAME, getLocale())); } public void loadUserMessagesUnderPseudo(UserRequest ureq, Identity user, String pseudonym) { loadMode = LoadMode.userMessagesUnderPseudo; List<MessageLight> messages = forumManager.getLightMessagesByUserUnderPseudo(forum, user, pseudonym); backupViews = loadThread(ureq, messages, false); messageTableCtrl.loadMessages(new ArrayList<>(0)); allButton.setVisible(false); updateButtons(allFlatButton); mainVC.contextPut("threadMode", Boolean.FALSE); mainVC.contextRemove("filteredForFirstName"); mainVC.contextPut("filteredForLastName", pseudonym); } public void loadGuestMessages(UserRequest ureq) { loadMode = LoadMode.guestMessages; List<MessageLight> messages = forumManager.getLightMessagesOfGuests(forum); backupViews = loadThread(ureq, messages, false); messageTableCtrl.loadMessages(new ArrayList<>(0)); allButton.setVisible(false); updateButtons(allFlatButton); mainVC.contextPut("threadMode", Boolean.FALSE); mainVC.contextRemove("filteredForFirstName"); mainVC.contextPut("filteredForLastName", translate("guest")); } public void loadThread(UserRequest ureq, Message threadMessage) { loadMode = LoadMode.thread; thread = threadMessage; List<MessageLight> messages = forumManager.getLightMessagesByThread(forum, thread); messages.add(0, thread); backupViews = loadThread(ureq, messages, true); messageTableCtrl.loadMessages(new ArrayList<>(0)); allButton.setVisible(true); updateButtons(allButton); mainVC.contextPut("threadMode", Boolean.TRUE); mainVC.contextRemove("filteredForFirstName"); mainVC.contextRemove("filteredForLastName"); } private MessageView loadView(UserRequest ureq, MessageLight message) { Set<Long> rms = null; Map<String,Mark> marks = Collections.emptyMap(); Map<String,Long> artefactStats = Collections.emptyMap(); List<String> subPaths = Collections.singletonList(message.getKey().toString()); if(!guestOnly) { String businessPath = BusinessControlFactory.getInstance().getAsString(getWindowControl().getBusinessControl()) + "[Message:" + message.getKey() + "]"; artefactStats = epMgr.getNumOfArtefactsByStartingBusinessPath(businessPath, getIdentity()); marks = new HashMap<>(); List<Mark> markList = markingService.getMarkManager().getMarks(forumOres, getIdentity(), subPaths); for(Mark mark:markList) { marks.put(mark.getResSubPath(), mark); } } List<MarkResourceStat> statList = markingService.getMarkManager().getStats(forumOres, subPaths, getIdentity()); Map<String,MarkResourceStat> stats = new HashMap<String,MarkResourceStat>(statList.size() * 2 + 1); for(MarkResourceStat stat:statList) { stats.put(stat.getSubPath(), stat); } MessageView view = new MessageView(message, userPropertyHandlers, getLocale()); view.setNumOfChildren(0); addMessageToCurrentMessagesAndVC(ureq, message, view, marks, stats, artefactStats, rms); return view; } private List<MessageView> loadThread(UserRequest ureq, List<MessageLight> messages, boolean reorder) { Set<Long> rms = null; Map<String,Mark> marks = Collections.emptyMap(); Map<String,Long> artefactStats = Collections.emptyMap(); if(!guestOnly) { rms = forumManager.getReadSet(getIdentity(), forum); String businessPath = BusinessControlFactory.getInstance().getAsString(getWindowControl().getBusinessControl()) + "[Message:"; artefactStats = epMgr.getNumOfArtefactsByStartingBusinessPath(businessPath, getIdentity()); marks = new HashMap<>(marks.size() * 2 + 1); List<Mark> markList = markingService.getMarkManager().getMarks(forumOres, getIdentity(), null); for(Mark mark:markList) { marks.put(mark.getResSubPath(), mark); } } List<MarkResourceStat> statList = markingService.getMarkManager().getStats(forumOres, null, getIdentity()); Map<String,MarkResourceStat> stats = new HashMap<String,MarkResourceStat>(statList.size() * 2 + 1); for(MarkResourceStat stat:statList) { stats.put(stat.getSubPath(), stat); } if(reorder) { List<MessageLight> orderedMessages = new ArrayList<MessageLight>(); orderMessagesThreaded(messages, orderedMessages, thread); messages = orderedMessages; } List<MessageView> views = new ArrayList<>(messages.size()); Map<Long,MessageView> keyToViews = new HashMap<>(); for(MessageLight msg:messages) { MessageView view = new MessageView(msg, userPropertyHandlers, getLocale()); view.setNumOfChildren(0); views.add(view); keyToViews.put(msg.getKey(), view); } //calculate depth and number of children for(MessageView view:views) { if(view.getParentKey() == null) { view.setDepth(0); } else { view.setDepth(1); for(MessageView parent = keyToViews.get(view.getParentKey()); parent != null; parent = keyToViews.get(parent.getParentKey())) { parent.setNumOfChildren(parent.getNumOfChildren() + 1); view.setDepth(view.getDepth() + 1); } } } //append ui things for (MessageLight msg: messages) { addMessageToCurrentMessagesAndVC(ureq, msg, keyToViews.get(msg.getKey()), marks, stats, artefactStats, rms); } mainVC.contextPut("messages", views); return views; } /** * Orders the messages in the logical instead of chronological order. * @param messages * @param orderedList * @param startMessage */ private void orderMessagesThreaded(List<MessageLight> messages, List<MessageLight> orderedList, MessageLight startMessage) { if (messages == null || orderedList == null || startMessage == null) return; Map<Long, MessageNode> messagesMap = new HashMap<>(); if(startMessage != null) { messagesMap.put(startMessage.getKey(), new MessageNode(startMessage)); } for(MessageLight message:messages) { if(message.getParentKey() != null) { messagesMap.put(message.getKey(), new MessageNode(message)); } } List<MessageNode> roots = new ArrayList<>(); for(MessageLight message:messages) { Long parentKey = message.getParentKey(); if(parentKey != null && messagesMap.containsKey(parentKey)) { MessageNode parentMessage = messagesMap.get(parentKey); MessageNode messageNode = messagesMap.get(message.getKey()); parentMessage.getChildren().add(messageNode); } else { MessageNode rootMessage = messagesMap.get(message.getKey()); roots.add(rootMessage); } } for(MessageNode root:roots) { orderMessagesThreaded(root, orderedList); } } private void orderMessagesThreaded(MessageNode message, List<MessageLight> orderedList) { if(message == null) { return; } if(message.getMessage() != null) { orderedList.add(message.getMessage()); } List<MessageNode> children = message.getChildren(); if(children.size() > 0) { if(children.size() > 1) { Collections.sort(children); } for(MessageNode child:children) { orderMessagesThreaded(child, orderedList); } } } private void markRead(MessageLight message) { if(!guestOnly) { forumManager.markAsRead(getIdentity(), forum, message); hasMarkedNewMessages = true; } } private void addMessageToCurrentMessagesAndVC(UserRequest ureq, MessageLight m, MessageView messageView, Map<String,Mark> marks, Map<String,MarkResourceStat> stats, Map<String,Long> artefactStats, Set<Long> readSet) { // all values belonging to a message are stored in this map // these values can be accessed in velocity. make sure you clean up // everything // you create here in disposeCurrentMessages()! String keyString = m.getKey().toString(); if (readSet == null || readSet.contains(m.getKey())) { messageView.setNewMessage(false); } else {// mark now as read markRead(m); messageView.setNewMessage(true); } // add some data now messageView.setFormattedCreationDate(formatter.formatDateAndTime(m.getCreationDate())); messageView.setFormattedLastModified(formatter.formatDateAndTime(m.getLastModified())); Identity creator = m.getCreator(); Identity modifier = m.getModifier(); if (modifier != null) { messageView.setModified(true); if(modifier.equals(creator) && StringHelper.containsNonWhitespace(m.getPseudonym())) { messageView.setModifierPseudonym(m.getPseudonym()); } else { messageView.setModifierFirstName(modifier.getUser().getProperty(UserConstants.FIRSTNAME, getLocale())); messageView.setModifierLastName(modifier.getUser().getProperty(UserConstants.LASTNAME, getLocale())); } } else { messageView.setModified(false); } boolean userIsMsgCreator = false; //keeps the first 15 chars if(creator != null) { userIsMsgCreator = getIdentity().equals(creator); if(!StringHelper.containsNonWhitespace(m.getPseudonym())) { messageView.setCreatorFirstname(Formatter.truncate(creator.getUser().getProperty(UserConstants.FIRSTNAME, getLocale()), 18)); messageView.setCreatorLastname(Formatter.truncate(creator.getUser().getProperty(UserConstants.LASTNAME, getLocale()), 18)); } } // message attachments VFSContainer msgContainer = forumManager.getMessageContainer(forum.getKey(), m.getKey()); messageView.setMessageContainer(msgContainer); List<VFSItem> attachments = new ArrayList<VFSItem>(msgContainer.getItems(new VFSItemMetaFilter())); messageView.setAttachments(attachments); // number of children and modify/delete permissions int numOfChildren = messageView.getNumOfChildren(); messageView.setAuthor(userIsMsgCreator); boolean threadTop = m.getThreadtop() == null; messageView.setThreadTop(threadTop); boolean isThreadClosed; if(threadTop) { isThreadClosed = Status.getStatus(m.getStatusCode()).isClosed(); } else { if(thread == null) { isThreadClosed = Status.getStatus(m.getThreadtop().getStatusCode()).isClosed(); } else { isThreadClosed = Status.getStatus(thread.getStatusCode()).isClosed(); } } messageView.setClosed(isThreadClosed); if(!guestOnly && !m.isGuest() && creator != null && !StringHelper.containsNonWhitespace(m.getPseudonym())) { // add portrait to map for later disposal and key for rendering in velocity DisplayPortraitController portrait = new DisplayPortraitController(ureq, getWindowControl(), creator, true, true, false, true); messageView.setPortrait(portrait); mainVC.put("portrait_".concat(keyString), portrait.getInitialComponent()); // Add link with username that is clickable String creatorFullName = StringHelper.escapeHtml(UserManager.getInstance().getUserDisplayName(creator)); Link visitingCardLink = LinkFactory.createCustomLink("vc_".concat(keyString), "vc", creatorFullName, Link.LINK_CUSTOM_CSS + Link.NONTRANSLATED, mainVC, this); visitingCardLink.setUserObject(messageView); LinkPopupSettings settings = new LinkPopupSettings(800, 600, "_blank"); visitingCardLink.setPopup(settings); } if(!isThreadClosed) { if((numOfChildren == 0 && userIsMsgCreator && foCallback.mayDeleteOwnMessage()) || foCallback.mayDeleteMessageAsModerator()) { Link deleteLink = LinkFactory.createCustomLink("dl_".concat(keyString), "dl", "msg.delete", Link.BUTTON_SMALL, mainVC, this); deleteLink.setIconLeftCSS("o_icon o_icon-fw o_icon_delete_item"); deleteLink.setUserObject(messageView); } if((numOfChildren == 0 && userIsMsgCreator && foCallback.mayEditOwnMessage()) || foCallback.mayEditMessageAsModerator()) { Link editLink = LinkFactory.createCustomLink("ed_".concat(keyString), "ed", "msg.update", Link.BUTTON_SMALL, mainVC, this); editLink.setIconLeftCSS("o_icon o_icon-fw o_icon_edit"); editLink.setUserObject(messageView); } if(foCallback.mayReplyMessage()) { Link quoteLink = LinkFactory.createCustomLink("qt_".concat(keyString), "qt", "msg.quote", Link.BUTTON_SMALL, mainVC, this); quoteLink.setElementCssClass("o_sel_forum_reply_quoted"); quoteLink.setIconLeftCSS("o_icon o_icon-fw o_icon_reply_with_quote"); quoteLink.setUserObject(messageView); Link replyLink = LinkFactory.createCustomLink("rp_".concat(keyString), "rp", "msg.reply", Link.BUTTON_SMALL, mainVC, this); replyLink.setElementCssClass("o_sel_forum_reply"); replyLink.setIconLeftCSS("o_icon o_icon-fw o_icon_reply"); replyLink.setUserObject(messageView); } if(foCallback.mayEditMessageAsModerator()) { if (!threadTop) { Link splitLink = LinkFactory.createCustomLink("split_".concat(keyString), "split", "msg.split", Link.LINK, mainVC, this); splitLink.setIconLeftCSS("o_icon o_icon-fw o_icon_split"); splitLink.setUserObject(messageView); Link moveLink = LinkFactory.createCustomLink("move_".concat(keyString), "move", "msg.move", Link.LINK, mainVC, this); moveLink.setIconLeftCSS("o_icon o_icon-fw o_icon_move"); moveLink.setUserObject(messageView); } Link exileLink = LinkFactory.createCustomLink("exile_".concat(keyString), "exile", "msg.exile", Link.LINK, mainVC, this); exileLink.setIconLeftCSS("o_icon o_icon-fw o_forum_status_thread_icon"); exileLink.setUserObject(messageView); } } Mark currentMark = marks.get(keyString); MarkResourceStat stat = stats.get(keyString); if(!guestOnly) { String businessPath = currentMark == null ? getWindowControl().getBusinessControl().getAsString() + "[Message:" + m.getKey() + "]" : currentMark.getBusinessPath(); Controller markCtrl = markingService.getMarkController(ureq, getWindowControl(), currentMark, stat, forumOres, keyString, businessPath); mainVC.put("mark_".concat(keyString), markCtrl.getInitialComponent()); } if(userIsMsgCreator && !StringHelper.containsNonWhitespace(m.getPseudonym())) { OLATResourceable messageOres = OresHelper.createOLATResourceableInstance("Forum", m.getKey()); String businessPath = BusinessControlFactory.getInstance().getAsString(getWindowControl().getBusinessControl()) + "[Message:" + m.getKey() + "]"; Long artefact = artefactStats.get(businessPath); int numOfArtefact = artefact == null ? 0 : artefact.intValue(); if(portfolioModule.isEnabled()) { String collectorId = "eportfolio_" + keyString; Component collectorCmp = new MediaCollectorComponent(collectorId, getWindowControl(), m, forumMediaHandler, businessPath); mainVC.put(collectorId, collectorCmp); } else { Controller ePFCollCtrl = EPUIFactory .createArtefactCollectWizzardController(ureq, getWindowControl(), numOfArtefact, messageOres, businessPath); if (ePFCollCtrl != null) { messageView.setArtefact(ePFCollCtrl); mainVC.put("eportfolio_" + keyString, ePFCollCtrl.getInitialComponent()); } } } } @Override public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { // } @Override public void event(Event event) { if(event instanceof ForumChangedEvent) { ForumChangedEvent fce = (ForumChangedEvent)event; if(ForumChangedEvent.CHANGED_MESSAGE.equals(fce.getCommand()) || ForumChangedEvent.NEW_MESSAGE.equals(fce.getCommand()) || ForumChangedEvent.DELETED_MESSAGE.equals(fce.getCommand()) ) { Long threadtopKey = fce.getThreadtopKey(); Long senderId = fce.getSendByIdentityKey(); if(thread != null && threadtopKey != null && thread.getKey().equals(threadtopKey) && (senderId == null || !senderId.equals(getIdentity().getKey()))) { reloadList = true; } } } } @Override protected void event(UserRequest ureq, Component source, Event event) { if(backLink == source) { fireEvent(ureq, Event.BACK_EVENT); } else if(archiveThreadButton == source) { doArchiveThread(ureq, thread); } else if (closeThreadButton == source) { doCloseThread(); } else if (openThreadButton == source) { doOpenThread(); } else if (hideThreadButton == source) { doHideThread(); } else if (showThreadButton == source) { doShowThread(); } else if (allButton == source) { doShowAll(ureq); saveViewSettings(ureq, VIEWMODE_THREAD); } else if (allFlatButton == source) { doShowAllFlat(ureq); saveViewSettings(ureq, VIEWMODE_FLAT); } else if (oneButton == source) { doShowOne(ureq); saveViewSettings(ureq, VIEWMODE_MESSAGE); } else if (markedButton == source) { doShowMarked(ureq); } else if (newButton == source) { doShowNew(ureq); } else if(stickyButton == source || removeStickyButton == source) { doToogleSticky(); } else if (source instanceof Link) { Link link = (Link)source; String command = link.getCommand(); Object uobject = link.getUserObject(); if (command.startsWith("qt")) { doReply(ureq, (MessageView)uobject, true); } else if (command.startsWith("rp")) { doReply(ureq, (MessageView)uobject, false); } else if (command.startsWith("dl")) { doConfirmDeleteMessage(ureq, (MessageView)uobject); } else if (command.startsWith("ed")) { doEditMessage(ureq, (MessageView)uobject); } else if (command.startsWith("split")) { doConfirmSplit(ureq, (MessageView)uobject); } else if (command.startsWith("move")) { doMoveMessage(ureq, (MessageView)uobject); } else if (command.startsWith("exile")) { doExportForumItem(ureq, (MessageView)uobject); } } else if(mainVC == source) { String cmd = event.getCommand(); if (cmd.startsWith("attachment")) { doDeliverAttachment(ureq, cmd); } } } private void doDeliverAttachment(UserRequest ureq, String cmd) { MediaResource res = null; try { int index = cmd.lastIndexOf("_"); String attachmentPosition = cmd.substring(cmd.indexOf("_") + 1, index); String messageKey = cmd.substring(index + 1); int position = Integer.parseInt(attachmentPosition); Long key = new Long(messageKey); for(MessageView view:backupViews) { if(view.getKey().equals(key)) { List<VFSItem> attachments = view.getAttachments(); VFSLeaf attachment = (VFSLeaf)attachments.get(position - 1);//velocity counter start with 1 VFSMediaResource fileResource = new VFSMediaResource(attachment); fileResource.setDownloadable(true); // prevent XSS attack res = fileResource; } } } catch (Exception e) { logError("Cannot deliver message attachment", e); } if(res == null) { res = new NotFoundMediaResource(cmd); } ureq.getDispatchResult().setResultingMediaResource(res); } @Override protected void event(UserRequest ureq, Controller source, Event event) { if (source == exportCtrl) { if(event == Event.CANCELLED_EVENT || event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { if (event == Event.CHANGED_EVENT) { StepsRunContext runContext = exportCtrl.getRunContext(); Message originTopMessage = (Message)runContext.get(SendMailStepForm.START_THREADTOP); originTopMessage = forumManager.loadMessage(originTopMessage.getKey()); if (originTopMessage != null) { //refresh origin thread fireEvent(ureq, new SelectMessageEvent(SelectMessageEvent.SELECT_THREAD, originTopMessage.getKey())); } else { fireEvent(ureq, Event.BACK_EVENT); } } getWindowControl().pop(); removeAsListenerAndDispose(exportCtrl); exportCtrl = null; } } else if (source == confirmDeleteCtrl) { if (DialogBoxUIFactory.isYesEvent(event) || DialogBoxUIFactory.isOkEvent(event)) { MessageView deletedMessage = (MessageView)confirmDeleteCtrl.getUserObject(); doDeleteMessage(ureq, deletedMessage); } } else if(editMessageCtrl == source) { // edit done -> save if(event instanceof ErrorEditMessage) { handleEditError(ureq); } else { Message message = editMessageCtrl.getMessage(); if(message != null) { if(thread != null && thread.getKey().equals(message.getKey())) { thread = message; } reloadModel(ureq, message); } else { showInfo("header.cannoteditmessage"); } } cmc.deactivate(); } else if(replyMessageCtrl == source) { if(event instanceof ErrorEditMessage) { handleEditError(ureq); } else { Message reply = replyMessageCtrl.getMessage(); if(reply != null) { reloadModel(ureq, reply); } else { showInfo("header.cannotsavemessage"); } } cmc.deactivate(); } else if(messageTableCtrl == source) { if(event instanceof SelectMessageEvent) { SelectMessageEvent sme = (SelectMessageEvent)event; doSelectTheOne(ureq, sme.getMessageKey()); } } else if(moveCtrl == source) { if(event instanceof SelectMessageEvent) { SelectMessageEvent sme = (SelectMessageEvent)event; doFinalizeMove(ureq, moveCtrl.getSelectView(), sme.getMessageKey()); cmc.deactivate(); } } else if(confirmSplitCtrl == source) { if (DialogBoxUIFactory.isYesEvent(event) || DialogBoxUIFactory.isOkEvent(event)) { MessageView splitedMessage = (MessageView)confirmSplitCtrl.getUserObject(); doSplitThread(ureq, splitedMessage); } } else if(source == cmc) { cleanUp(); } super.event(ureq, source, event); } private void cleanUp() { removeAsListenerAndDispose(replyMessageCtrl); removeAsListenerAndDispose(editMessageCtrl); removeAsListenerAndDispose(cmc); replyMessageCtrl = null; editMessageCtrl = null; cmc = null; } private void handleEditError(UserRequest ureq) { if(thread == null) { fireEvent(ureq, Event.BACK_EVENT); showWarning("error.message.deleted"); } else if(forumManager.existsMessageById(thread.getKey())) { reloadModel(ureq, null); showWarning("error.message.deleted"); } else { fireEvent(ureq, Event.BACK_EVENT); showWarning("error.message.deleted"); } } private void doReply(UserRequest ureq, MessageView parent, boolean quote) { // user has clicked on button 'reply' if (foCallback.mayReplyMessage()) { Message newMessage = forumManager.createMessage(forum, getIdentity(), guestOnly); Message parentMessage = forumManager.getMessageById(parent.getKey()); if(parentMessage == null) { handleEditError(ureq); return; } String reString = ""; if(parent != null && parent.isThreadTop()) { //add reString only for the first answer reString = translate("msg.title.re"); } newMessage.setTitle(reString + parentMessage.getTitle()); if (quote) { // load message to form as quotation StringBuilder quoteSb = new StringBuilder(); quoteSb.append("<p></p><div class=\"o_quote_wrapper\"><div class=\"o_quote_author mceNonEditable\">"); String date = formatter.formatDateAndTime(parentMessage.getCreationDate()); String creatorName; if(StringHelper.containsNonWhitespace(parentMessage.getPseudonym())) { creatorName = parentMessage.getPseudonym(); } else if(parentMessage.isGuest()) { creatorName = translate("guest"); } else { User creator = parentMessage.getCreator().getUser(); creatorName = creator.getProperty(UserConstants.FIRSTNAME, getLocale()) + " " + creator.getProperty(UserConstants.LASTNAME, getLocale()); } quoteSb.append(translate("msg.quote.intro", new String[]{ date, creatorName})) .append("</div><blockquote class=\"o_quote\">") .append(parentMessage.getBody()) .append("</blockquote></div>") .append("<p></p>"); newMessage.setBody(quoteSb.toString()); } replyMessageCtrl = new MessageEditController(ureq, getWindowControl(), forum, foCallback, newMessage, parentMessage, EditMode.reply); listenTo(replyMessageCtrl); String title = quote ? translate("msg.quote") : translate("msg.reply"); cmc = new CloseableModalController(getWindowControl(), "close", replyMessageCtrl.getInitialComponent(), true, title); listenTo(cmc); cmc.activate(); } else { showInfo("may.not.reply.msg"); } } private void doConfirmDeleteMessage(UserRequest ureq, MessageView message) { // user has clicked on button 'delete' // -> display modal dialog 'Do you really want to delete this message?' // 'yes': back to allThreadTable, 'no' back to messageDetails int numOfChildren = forumManager.countMessageChildren(message.getKey()); boolean children = numOfChildren > 0; boolean userIsMsgCreator = message.isAuthor() ; String currentMsgTitle = StringHelper.escapeHtml(message.getTitle()); if (foCallback.mayDeleteMessageAsModerator()) { // user is forum-moderator -> may delete every message on every level if (numOfChildren == 0) { confirmDeleteCtrl = activateYesNoDialog(ureq, null, translate("reallydeleteleaf", currentMsgTitle), confirmDeleteCtrl); confirmDeleteCtrl.setUserObject(message); } else if (numOfChildren == 1) { confirmDeleteCtrl = activateYesNoDialog(ureq, null, translate("reallydeletenode1", currentMsgTitle), confirmDeleteCtrl); confirmDeleteCtrl.setUserObject(message); } else { confirmDeleteCtrl = activateYesNoDialog(ureq, null, getTranslator().translate("reallydeletenodeN", new String[] { currentMsgTitle, Integer.toString(numOfChildren) }), confirmDeleteCtrl); confirmDeleteCtrl.setUserObject(message); } } else if (userIsMsgCreator && !children ) { // user may delete his own message if it has no children confirmDeleteCtrl = activateYesNoDialog(ureq, null, translate("reallydeleteleaf", currentMsgTitle), confirmDeleteCtrl); confirmDeleteCtrl.setUserObject(message); } else if (userIsMsgCreator && children) { // user may not delete his own message because it has at least one child showWarning("may.not.delete.msg.as.author"); } else { // user isn't author of the current message showInfo("may.not.delete.msg"); } } private void doDeleteMessage(UserRequest ureq, MessageView message) { boolean userIsMsgCreator = message.isAuthor(); if (foCallback.mayDeleteMessageAsModerator() || (userIsMsgCreator && forumManager.countMessageChildren(message.getKey()) == 0)) { Message reloadedMessage = forumManager.getMessageById(message.getKey()); if(reloadedMessage != null) { //this delete the topic / thread if(reloadedMessage.getParent() == null) { forumManager.deleteMessageTree(forum.getKey(), reloadedMessage); //delete topics ThreadLocalUserActivityLogger.log(ForumLoggingAction.FORUM_THREAD_DELETE, getClass(), LoggingResourceable.wrap(reloadedMessage)); //back to thread list fireEvent(ureq, new DeleteThreadEvent()); ForumChangedEvent event = new ForumChangedEvent(ForumChangedEvent.DELETED_THREAD, reloadedMessage.getKey(), reloadedMessage.getKey(), getIdentity()); CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(event, forum); } else { Message threadTop = reloadedMessage.getThreadtop(); forumManager.deleteMessageTree(forum.getKey(), reloadedMessage); threadTop = forumManager.updateMessage(threadTop, true); if(thread != null) { thread = threadTop;//update with the fresh version } showInfo("deleteok"); ThreadLocalUserActivityLogger.log(ForumLoggingAction.FORUM_MESSAGE_DELETE, getClass(), LoggingResourceable.wrap(reloadedMessage)); //reload reloadModelAfterDelete(ureq, message); fireEvent(ureq, new DeleteMessageEvent()); ForumChangedEvent event = new ForumChangedEvent(ForumChangedEvent.DELETED_MESSAGE, threadTop.getKey(), message.getKey(), getIdentity()); CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(event, forum); } } } else { showWarning("may.not.delete.msg.as.author"); } } private void doEditMessage(UserRequest ureq, MessageView message) { // user has clicked on button 'edit' boolean userIsMsgCreator = message.isAuthor(); boolean children = forumManager.countMessageChildren(message.getKey()) > 0; if (foCallback.mayEditMessageAsModerator() || (userIsMsgCreator && !children)) { Message reloadedMessage = forumManager.loadMessage(message.getKey()); if(reloadedMessage == null) { showWarning("error.message.deleted"); reloadModel(ureq, null); } else { editMessageCtrl = new MessageEditController(ureq, getWindowControl(), forum, foCallback, reloadedMessage, null, EditMode.edit); listenTo(editMessageCtrl); String title = translate("msg.update"); cmc = new CloseableModalController(getWindowControl(), "close", editMessageCtrl.getInitialComponent(), true, title); listenTo(editMessageCtrl); cmc.activate(); } } else if ((userIsMsgCreator) && (children == true)) { // user is author of the current message but it has already at least // one child showWarning("may.not.save.msg.as.author"); } else { // user isn't author of the current message showInfo("may.not.edit.msg"); } } private void doConfirmSplit(UserRequest ureq, MessageView message) { if (foCallback.mayEditMessageAsModerator()) { // user is forum-moderator -> may delete every message on every level int numOfChildren = forumManager.countMessageChildren(message.getKey()); // provide yesNoSplit as argument, this ensures that dc is disposed before newly created String text = translate("reallysplitthread", new String[] { message.getTitle(), Integer.toString(numOfChildren) }); confirmSplitCtrl = activateYesNoDialog(ureq, null, text, confirmSplitCtrl); confirmSplitCtrl.setUserObject(message); } } private void doSplitThread(UserRequest ureq, MessageView message) { if (foCallback.mayEditMessageAsModerator()) { Message reloadedMessage = forumManager.getMessageById(message.getKey()); Message newTopMessage = forumManager.splitThread(reloadedMessage); //do logging ThreadLocalUserActivityLogger.log(ForumLoggingAction.FORUM_THREAD_SPLIT, getClass(), LoggingResourceable.wrap(newTopMessage)); showInfo("new.thread.location"); //open the new thread fireEvent(ureq, new SelectMessageEvent(SelectMessageEvent.SELECT_THREAD, newTopMessage.getKey())); } else { showWarning("may.not.split.thread"); } } private void doArchiveThread(UserRequest ureq, Message currMsg) { Message m = currMsg.getThreadtop(); Long topMessageId = (m == null) ? currMsg.getKey() : m.getKey(); VFSContainer forumContainer = forumManager.getForumContainer(forum.getKey()); ForumDownloadResource download = new ForumDownloadResource("Forum", forum, foCallback, topMessageId, forumContainer, getLocale()); ureq.getDispatchResult().setResultingMediaResource(download); } private void doToogleSticky() { Status status = Status.getStatus(thread.getStatusCode()); status.setSticky(!status.isSticky()); thread.setStatusCode(Status.getStatusCode(status)); thread = forumManager.updateMessage(thread, false); DBFactory.getInstance().commit(); stickyButton.setVisible(!status.isSticky() && foCallback.mayEditMessageAsModerator()); removeStickyButton.setVisible(status.isSticky() && foCallback.mayEditMessageAsModerator()); mainVC.setDirty(true); ForumChangedEvent event = new ForumChangedEvent(ForumChangedEvent.STICKY, thread.getKey(), null, getIdentity()); CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(event, forumOres); ThreadLocalUserActivityLogger.log(ForumLoggingAction.FORUM_MESSAGE_EDIT, getClass(), LoggingResourceable.wrap(thread)); } /** * Sets the closed status to the thread message. * @param ureq * @param msg * @param closed */ private void doCloseThread() { if (thread != null) { thread = forumManager.getMessageById(thread.getKey()); Status status = Status.getStatus(thread.getStatusCode()); status.setClosed(true); thread.setStatusCode(Status.getStatusCode(status)); thread = forumManager.updateMessage(thread, false); DBFactory.getInstance().commit();// before sending async event closeThreadButton.setVisible(false); openThreadButton.setVisible(true && !guestOnly); mainVC.setDirty(true); ForumChangedEvent event = new ForumChangedEvent(ForumChangedEvent.CLOSE, thread.getKey(), null, getIdentity()); CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(event, forumOres); ThreadLocalUserActivityLogger.log(ForumLoggingAction.FORUM_THREAD_CLOSE, getClass(), LoggingResourceable.wrap(thread)); } } private void doOpenThread() { if (thread != null) { thread = forumManager.getMessageById(thread.getKey()); Status status = Status.getStatus(thread.getStatusCode()); status.setClosed(false); thread.setStatusCode(Status.getStatusCode(status)); thread = forumManager.updateMessage(thread, true); DBFactory.getInstance().commit();// before sending async event closeThreadButton.setVisible(true && !guestOnly); openThreadButton.setVisible(false); mainVC.setDirty(true); ForumChangedEvent event = new ForumChangedEvent(ForumChangedEvent.OPEN, thread.getKey(), null, getIdentity()); CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(event, forumOres); ThreadLocalUserActivityLogger.log(ForumLoggingAction.FORUM_THREAD_REOPEN, getClass(), LoggingResourceable.wrap(thread)); } } /** * Sets the hidden status to the thread message. * @param ureq * @param msg * @param hidden */ private void doHideThread() { if (thread != null) { thread = forumManager.getMessageById(thread.getKey()); Status status = Status.getStatus(thread.getStatusCode()); status.setHidden(true); thread.setStatusCode(Status.getStatusCode(status)); thread = forumManager.updateMessage(thread, false); DBFactory.getInstance().commit();// before sending async event hideThreadButton.setVisible(false); showThreadButton.setVisible(true && !guestOnly); mainVC.setDirty(true); ForumChangedEvent event = new ForumChangedEvent(ForumChangedEvent.HIDE, thread.getKey(), null, getIdentity()); CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(event, forumOres); ThreadLocalUserActivityLogger.log(ForumLoggingAction.FORUM_THREAD_HIDE, getClass(), LoggingResourceable.wrap(thread)); } } /** * Sets the hidden status to the threadtop message. * @param ureq * @param msg * @param hidden */ private void doShowThread() { if (thread != null) { thread = forumManager.getMessageById(thread.getKey()); Status status = Status.getStatus(thread.getStatusCode()); status.setHidden(false); thread.setStatusCode(Status.getStatusCode(status)); thread = forumManager.updateMessage(thread, true); DBFactory.getInstance().commit();// before sending async event hideThreadButton.setVisible(true && !guestOnly); showThreadButton.setVisible(false); mainVC.setDirty(true); ForumChangedEvent event = new ForumChangedEvent(ForumChangedEvent.SHOW, thread.getKey(), null, getIdentity()); CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(event, forumOres); ThreadLocalUserActivityLogger.log(ForumLoggingAction.FORUM_THREAD_SHOW, getClass(), LoggingResourceable.wrap(thread)); } } protected String doShowBySettings(UserRequest ureq) { String viewSettings = getViewSettings(ureq); switch(viewSettings) { case VIEWMODE_THREAD: doShowAll(ureq); break; case VIEWMODE_FLAT: doShowAllFlat(ureq); break; case VIEWMODE_MESSAGE: doShowOne(ureq); break; default: doShowAll(ureq); } return viewSettings == null ? VIEWMODE_THREAD : viewSettings; } private void doShowAll(UserRequest ureq) { if(reloadList) { reloadModel(ureq, null); } updateButtons(allButton); mainVC.contextPut("threadMode", Boolean.TRUE); mainVC.contextPut("messages", backupViews); mainVC.contextRemove("mode"); } private void doShowAllFlat(UserRequest ureq) { if(reloadList) { reloadModel(ureq, null); } updateButtons(allFlatButton); mainVC.contextPut("threadMode", Boolean.FALSE); mainVC.contextPut("messages", backupViews); mainVC.contextRemove("mode"); } private void doShowOne(UserRequest ureq) { if(reloadList) { reloadModel(ureq, null); } updateButtons(oneButton); mainVC.contextPut("mode", "one"); mainVC.contextPut("threadMode", Boolean.FALSE); if(backupViews != null && backupViews.size() > 0) { List<MessageView> oneView = new ArrayList<>(1); oneView.add(backupViews.get(0)); mainVC.contextPut("messages", oneView); messageTableCtrl.setSelectView(oneView.get(0)); messageTableCtrl.loadMessages(new ArrayList<>(backupViews)); } } private String getViewSettings(UserRequest ureq) { Preferences prefs = ureq.getUserSession().getGuiPreferences(); Object setting = prefs.get(GUI_PREFS_VIEWMODE_CLASS, GUI_PREFS_VIEWMODE_KEY); if(VIEWMODE_THREAD.equals(setting) || VIEWMODE_FLAT.equals(setting) || VIEWMODE_MESSAGE.equals(setting)) { return (String)setting; } return VIEWMODE_THREAD; } private void saveViewSettings(UserRequest ureq, String viewMode) { Preferences prefs = ureq.getUserSession().getGuiPreferences(); prefs.putAndSave(GUI_PREFS_VIEWMODE_CLASS, GUI_PREFS_VIEWMODE_KEY, viewMode); } private void doSelectTheOne(UserRequest ureq, Long messageKey) { if(reloadList) { reloadModel(ureq, null); } updateButtons(oneButton); mainVC.contextPut("mode", "one"); mainVC.contextPut("threadMode", Boolean.FALSE); if(backupViews != null && backupViews.size() > 0) { List<MessageView> oneView = new ArrayList<>(1); for(MessageView message:backupViews) { if(message.getKey().equals(messageKey)) { oneView.add(message); } } mainVC.contextPut("messages", oneView); messageTableCtrl.setSelectView(oneView.get(0)); messageTableCtrl.loadMessages(new ArrayList<>(backupViews)); } } protected void doShowMarked(UserRequest ureq) { if(reloadList) { reloadModel(ureq, null); } updateButtons(markedButton); mainVC.contextPut("threadMode", Boolean.FALSE); mainVC.contextPut("mode", "marked"); List<Mark> markList = markingService.getMarkManager().getMarks(forumOres, getIdentity(), null); Set<String> marks = new HashSet<>(markList.size() * 2 + 1); for(Mark mark:markList) { marks.add(mark.getResSubPath()); } List<MessageView> views = new ArrayList<>(); for(MessageView view:backupViews) { if(marks.contains(view.getKey().toString())) { views.add(view); } } mainVC.contextPut("messages", views); } protected void doShowNew(UserRequest ureq) { if(reloadList) { reloadModel(ureq, null); } updateButtons(newButton); mainVC.contextPut("threadMode", Boolean.FALSE); mainVC.contextPut("mode", "new"); List<MessageView> views = new ArrayList<>(); for(MessageView view:backupViews) { if(view.isNewMessage()) { views.add(view); } } mainVC.contextPut("messages", views); } private void doMoveMessage(UserRequest ureq, MessageView message) { removeAsListenerAndDispose(moveCtrl); removeAsListenerAndDispose(cmc); if (foCallback.mayEditMessageAsModerator()) { moveCtrl = new ForumMessageListController(ureq, getWindowControl(), forum, true); moveCtrl.loadAllMessages(); moveCtrl.setSelectView(message); listenTo(moveCtrl); //push the modal dialog with the table as content String title = ""; cmc = new CloseableModalController(getWindowControl(), "close", moveCtrl.getInitialComponent(), true, title); listenTo(cmc); cmc.activate(); } } private void doExportForumItem(UserRequest ureq, MessageView messageToMove) { if (foCallback.mayEditMessageAsModerator()) { StepRunnerCallback finish = new FinishCallback(); Translator trans = Util.createPackageTranslator(Step_1_SelectCourse.class, getLocale()); Step start = new Step_1_SelectCourse(ureq); Message message = forumManager.getMessageById(messageToMove.getKey()); String wizardTitle = trans.translate("title.wizard.movethread", new String[]{message.getTitle()}); exportCtrl = new StepsMainRunController(ureq, getWindowControl(), start, finish, null, wizardTitle, "o_sel_bulk_assessment_wizard"); StepsRunContext runContext = exportCtrl.getRunContext(); // can be threadtop message runContext.put(SendMailStepForm.MESSAGE_TO_MOVE, message); // starting thread runContext.put(SendMailStepForm.START_THREADTOP, message.getThreadtop() == null ? message : message.getThreadtop()); // get start course PropertyManager propertyManager = PropertyManager.getInstance(); Long forumResourceableId = forum.getResourceableId(); Property forumproperty = propertyManager.getPropertyByLongValue(forumResourceableId, FOCourseNode.FORUM_KEY); if (forumproperty != null) { Long resourcetypeId = forumproperty.getResourceTypeId(); String resourcetypeName = forumproperty.getResourceTypeName(); OLATResourceable olatResourceable = olatManager.findResourceable(resourcetypeId, resourcetypeName); RepositoryEntry startCourse = repositoryManager.lookupRepositoryEntry(olatResourceable, false); if (startCourse != null) { runContext.put(SendMailStepForm.START_COURSE, startCourse); } } listenTo(exportCtrl); getWindowControl().pushAsModalDialog(exportCtrl.getInitialComponent()); } else { showWarning("may.not.move.message"); } } private void doFinalizeMove(UserRequest ureq, MessageView messageToMove, Long parentMessageKey) { if (foCallback.mayEditMessageAsModerator()) { Message message = forumManager.getMessageById(messageToMove.getKey()); Message parentMessage = forumManager.getMessageById(parentMessageKey); message = forumManager.moveMessage(message, parentMessage); markRead(message); DBFactory.getInstance().commit();//commit before sending event ThreadLocalUserActivityLogger.log(ForumLoggingAction.FORUM_MESSAGE_MOVE, getClass(), LoggingResourceable.wrap(message)); Long threadKey = parentMessage.getThreadtop() == null ? parentMessage.getKey() : parentMessage.getThreadtop().getKey(); fireEvent(ureq, new SelectMessageEvent(SelectMessageEvent.SELECT_THREAD, threadKey, message.getKey())); } else { showWarning("may.not.move.message"); } } public enum LoadMode { thread, userMessages, userMessagesUnderPseudo, guestMessages, } private static class MessageNode implements Comparable<MessageNode> { private final MessageLight message; private final List<MessageNode> children = new ArrayList<>(); private MessageNode(MessageLight message) { this.message = message; } public MessageLight getMessage() { return message; } public List<MessageNode> getChildren() { return children; } @Override public int compareTo(MessageNode arg0) { return message.getCreationDate().compareTo(arg0.getMessage().getCreationDate()); } } private class AttachmentsMapper implements Mapper { @Override public MediaResource handle(String relPath, HttpServletRequest request) { String[] query = relPath.split("/"); // expected path looks like this /messageId/attachmentUUID/filename if (query.length == 4) { try { Long mId = Long.valueOf(Long.parseLong(query[1])); MessageView view = null; for (MessageView m : backupViews) { // search for message in current message map if (m.getKey().equals(mId)) { view = m; break; } } if (view != null) { List<VFSItem> attachments = view.getAttachments(); for (VFSItem vfsItem : attachments) { MetaInfo meta = ((MetaTagged)vfsItem).getMetaInfo(); if (meta.getUUID().equals(query[2])) { if (meta.isThumbnailAvailable()) { VFSLeaf thumb = meta.getThumbnail(200, 200, false); if(thumb != null) { // Positive lookup, send as response return new VFSMediaResource(thumb); } } break; } } } } catch (NumberFormatException e) { // } } // In any error case, send not found return new NotFoundMediaResource(request.getRequestURI()); } } }