/**
* <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.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.olat.basesecurity.BaseSecurityModule;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
import org.olat.core.gui.components.form.flexible.elements.FlexiTableSort;
import org.olat.core.gui.components.form.flexible.elements.FlexiTableSortOptions;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormEvent;
import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableCssDelegate;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableRendererType;
import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent;
import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlexiCellRenderer;
import org.olat.core.gui.components.form.flexible.impl.elements.table.TextFlexiCellRenderer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.id.UserConstants;
import org.olat.core.util.Util;
import org.olat.modules.fo.Forum;
import org.olat.modules.fo.MessageLight;
import org.olat.modules.fo.Status;
import org.olat.modules.fo.manager.ForumManager;
import org.olat.modules.fo.ui.ForumMessageDataModel.ForumMessageCols;
import org.olat.modules.fo.ui.events.SelectMessageEvent;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.UserPropertyHandler;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* Initial date: 12.11.2015<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class ForumMessageListController extends FormBasicController {
protected static final String USER_PROPS_ID = ForumUserListController.class.getCanonicalName();
public static final int USER_PROPS_OFFSET = 500;
private FlexiTableElement tableEl;
private ForumMessageDataModel dataModel;
private final Forum forum;
private final boolean withType;
private final boolean isAdministrativeUser;
private final List<UserPropertyHandler> userPropertyHandlers;
private MessageView userObject, selectView;
@Autowired
private UserManager userManager;
@Autowired
private ForumManager forumManager;
@Autowired
private BaseSecurityModule securityModule;
public ForumMessageListController(UserRequest ureq, WindowControl wControl,
Forum forum, boolean withType) {
super(ureq, wControl, LAYOUT_BAREBONE);
setTranslator(Util.createPackageTranslator(Forum.class, getLocale(), getTranslator()));
setTranslator(userManager.getPropertyHandlerTranslator(getTranslator()));
this.forum = forum;
this.withType = withType;
isAdministrativeUser = securityModule.isUserAllowedAdminProps(ureq.getUserSession().getRoles());
userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_PROPS_ID, isAdministrativeUser);
initForm(ureq);
}
public MessageView getUserObject() {
return userObject;
}
public void setUserObject(MessageView userObject) {
this.userObject = userObject;
}
public MessageView getSelectView() {
return selectView;
}
public void setSelectView(MessageView selectView) {
this.selectView = selectView;
}
public void loadAllMessages() {
List<MessageLight> allMessages = forumManager.getLightMessagesByForum(forum);
List<MessageLightView> views = new ArrayList<>(allMessages.size());
Map<Long,MessageLightView> keyToViews = new HashMap<>();
for(MessageLight message:allMessages) {
MessageLightView view = new MessageLightView(message, userPropertyHandlers, getLocale());
views.add(view);
keyToViews.put(view.getKey(), view);
}
//calculate depth
Map<Long, List<Long>> keyToParentline = new HashMap<>();
for(MessageLightView view:views) {
if(view.getParentKey() == null) {
view.setDepth(0);
} else {
List<Long> parentLine = new ArrayList<>(5);
view.setDepth(1);
for(MessageLightView parent = keyToViews.get(view.getParentKey()); parent != null; parent = keyToViews.get(parent.getParentKey())) {
view.setDepth(view.getDepth() + 1);
parentLine.add(parent.getKey());
}
keyToParentline.put(view.getKey(), parentLine);
}
}
//order
List<MessageNode> threads = convertToThreadTrees(views);
Collections.sort(threads, new MessageNodeComparator());
List<MessageLightView> orderedViews = new ArrayList<>(allMessages.size());
flatTree(threads, orderedViews);
dataModel.setObjects(orderedViews);
}
public void loadMessages(List<MessageLightView> views) {
dataModel.setObjects(views);
tableEl.reloadData();
tableEl.reset();
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
List<FlexiTableSort> sorts = new ArrayList<>();
sorts.add(new FlexiTableSort(translate("natural.sort"), "natural"));
FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
if(withType) {
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ForumMessageCols.type, new StatusTypeCellRenderer()));
sorts.add(new FlexiTableSort(translate(ForumMessageCols.type.i18nHeaderKey()), ForumMessageCols.type.name()));
}
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ForumMessageCols.thread,
"select", new StaticFlexiCellRenderer("select", new IndentCellRenderer())));
sorts.add(new FlexiTableSort(translate(ForumMessageCols.thread.i18nHeaderKey()), ForumMessageCols.thread.name()));
int colPos = USER_PROPS_OFFSET;
for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) {
if (userPropertyHandler == null) continue;
String propName = userPropertyHandler.getName();
boolean visible = userManager.isMandatoryUserProperty(USER_PROPS_ID , userPropertyHandler);
FlexiColumnModel col;
if(UserConstants.FIRSTNAME.equals(propName) || UserConstants.LASTNAME.equals(propName)) {
col = new DefaultFlexiColumnModel(userPropertyHandler.i18nColumnDescriptorLabelKey(), colPos, "select", true, propName,
new StaticFlexiCellRenderer("select", new TextFlexiCellRenderer()));
} else {
col = new DefaultFlexiColumnModel(visible, userPropertyHandler.i18nColumnDescriptorLabelKey(), colPos, true, propName);
}
sorts.add(new FlexiTableSort(translate(userPropertyHandler.i18nColumnDescriptorLabelKey()), propName));
columnsModel.addFlexiColumnModel(col);
colPos++;
}
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ForumMessageCols.lastModified));
sorts.add(new FlexiTableSort(translate(ForumMessageCols.lastModified.i18nHeaderKey()), ForumMessageCols.lastModified.name()));
dataModel = new ForumMessageDataModel(columnsModel, getTranslator());
tableEl = uifactory.addTableElement(getWindowControl(), "messages", dataModel, getTranslator(), formLayout);
tableEl.setCssDelegate(new MessageCssDelegate());
FlexiTableSortOptions sortOptions = new FlexiTableSortOptions();
sortOptions.setFromColumnModel(false);
sortOptions.setSorts(sorts);
tableEl.setSortSettings(sortOptions);
}
@Override
protected void doDispose() {
//
}
@Override
protected void formOK(UserRequest ureq) {
//
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if(tableEl == source) {
if(event instanceof SelectionEvent) {
SelectionEvent se = (SelectionEvent)event;
String cmd = se.getCommand();
if("select".equals(cmd)) {
MessageLightView message = dataModel.getObject(se.getIndex());
fireEvent(ureq, new SelectMessageEvent(SelectMessageEvent.SELECT_MESSAGE, message.getKey()));
}
}
}
super.formInnerEvent(ureq, source, event);
}
private void flatTree(List<MessageNode> nodes, List<MessageLightView> orderedViews) {
for(MessageNode node:nodes) {
orderedViews.add(node.getView());
if(node.hasChildren()) {
flatTree(node.getChildren(), orderedViews);
}
}
}
private List<MessageNode> convertToThreadTrees(List<MessageLightView> messages){
List<MessageNode> topNodeList = new ArrayList<>();
for (Iterator<MessageLightView> iterTop = messages.iterator(); iterTop.hasNext();) {
MessageLightView msg = iterTop.next();
if (msg.getParentKey() == null) {
iterTop.remove();
MessageNode topNode = new MessageNode(msg);
addChildren(messages, topNode);
topNodeList.add(topNode);
}
}
return topNodeList;
}
private void addChildren(List<MessageLightView> messages, MessageNode mn){
for(Iterator<MessageLightView> iterMsg = messages.iterator(); iterMsg.hasNext(); ) {
MessageLightView msg = iterMsg.next();
if ((msg.getParentKey() != null) && (msg.getParentKey().equals(mn.getKey()))){
MessageNode childNode = new MessageNode(msg);
mn.addChild(childNode);
addChildren(messages, childNode);
}
}
}
private class MessageCssDelegate extends DefaultFlexiTableCssDelegate {
@Override
public String getRowCssClass(FlexiTableRendererType type, int pos) {
MessageLightView row = dataModel.getObject(pos);
return row != null && selectView != null && row.getKey().equals(selectView.getKey()) ? "o_row_selected" : null;
}
}
private static class MessageNode {
private final MessageLightView view;
private List<MessageNode> children;
public MessageNode(MessageLightView view) {
this.view = view;
}
public Long getKey() {
return view.getKey();
}
public boolean isSticky() {
return Status.getStatus(view.getStatusCode()).isSticky();
}
public Date getLastModified() {
return view.getLastModified();
}
public MessageLightView getView() {
return view;
}
public boolean hasChildren() {
return children != null && children.size() > 0;
}
public void addChild(MessageNode child) {
if(children == null) {
children = new ArrayList<>();
}
children.add(child);
}
public List<MessageNode> getChildren() {
return children;
}
}
public static class MessageNodeComparator implements Comparator<MessageNode> {
@Override
public int compare(final MessageNode m1, final MessageNode m2) {
if(m1.isSticky() && m2.isSticky()) {
return m2.getLastModified().compareTo(m1.getLastModified()); //last first
} else if(m1.isSticky()) {
return -1;
} else if(m2.isSticky()){
return 1;
} else {
return m2.getLastModified().compareTo(m1.getLastModified()); //last first
}
}
}
}