/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
// www.projectforge.org
//
// Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 3 of the License.
//
// This community edition is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////
package org.projectforge.web.task;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.table.HeadersToolbar;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.NoRecordsToolbar;
import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree;
import org.apache.wicket.extensions.markup.html.repeater.tree.TableTree;
import org.apache.wicket.extensions.markup.html.repeater.tree.content.Folder;
import org.apache.wicket.extensions.markup.html.repeater.tree.table.NodeBorder;
import org.apache.wicket.extensions.markup.html.repeater.tree.table.NodeModel;
import org.apache.wicket.extensions.markup.html.repeater.tree.table.TreeColumn;
import org.apache.wicket.extensions.markup.html.repeater.tree.theme.WindowsTheme;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.OddEvenItem;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.ResourceModel;
import org.projectforge.access.AccessChecker;
import org.projectforge.fibu.AuftragsPositionVO;
import org.projectforge.registry.Registry;
import org.projectforge.task.TaskDao;
import org.projectforge.task.TaskFilter;
import org.projectforge.task.TaskNode;
import org.projectforge.task.TaskTree;
import org.projectforge.user.ProjectForgeGroup;
import org.projectforge.user.UserGroupCache;
import org.projectforge.web.calendar.DateTimeFormatter;
import org.projectforge.web.core.PriorityFormatter;
import org.projectforge.web.fibu.ISelectCallerPage;
import org.projectforge.web.fibu.OrderPositionsPanel;
import org.projectforge.web.user.UserFormatter;
import org.projectforge.web.user.UserPropertyColumn;
import org.projectforge.web.wicket.AbstractListPage;
import org.projectforge.web.wicket.AbstractSecuredPage;
import org.projectforge.web.wicket.CellItemListener;
import org.projectforge.web.wicket.CellItemListenerPropertyColumn;
import org.projectforge.web.wicket.DatePropertyColumn;
import org.projectforge.web.wicket.ListSelectActionPanel;
/**
* @author Kai Reinhard (k.reinhard@micromata.de)
*
*/
public class TaskTreeBuilder implements Serializable
{
private static final long serialVersionUID = -2425308275690643856L;
private final Behavior theme = new WindowsTheme();
private Integer highlightedTaskNodeId;
private boolean selectMode, showRootNode, showCost, showOrders;
private AccessChecker accessChecker;
private TaskFormatter taskFormatter;
private PriorityFormatter priorityFormatter;
private UserFormatter userFormatter;
private DateTimeFormatter dateTimeFormatter;
private UserGroupCache userGroupCache;
private TableTree<TaskNode, String> tree;
private AbstractSecuredPage parentPage;
private ISelectCallerPage caller;
private String selectProperty;
/**
* @param id
*/
public TaskTreeBuilder()
{
}
@SuppressWarnings("serial")
public AbstractTree<TaskNode> createTree(final String id, final AbstractSecuredPage parentPage, final TaskFilter taskFilter)
{
this.parentPage = parentPage;
final List<IColumn<TaskNode, String>> columns = createColumns();
tree = new TableTree<TaskNode, String>(id, columns, new TaskTreeProvider(taskFilter).setShowRootNode(showRootNode), Integer.MAX_VALUE,
TaskTreeExpansion.getExpansionModel()) {
private static final long serialVersionUID = 1L;
@Override
protected Component newContentComponent(final String id, final IModel<TaskNode> model)
{
return TaskTreeBuilder.this.newContentComponent(id, this, model);
}
@Override
protected Item<TaskNode> newRowItem(final String id, final int index, final IModel<TaskNode> model)
{
return new OddEvenItem<TaskNode>(id, index, model);
}
};
tree.getTable().addTopToolbar(new HeadersToolbar<String>(tree.getTable(), null));
tree.getTable().addBottomToolbar(new NoRecordsToolbar(tree.getTable()));
tree.add(new Behavior() {
@Override
public void onComponentTag(final Component component, final ComponentTag tag)
{
theme.onComponentTag(component, tag);
}
@Override
public void renderHead(final Component component, final IHeaderResponse response)
{
theme.renderHead(component, response);
}
});
tree.getTable().add(AttributeModifier.append("class", "tableTree"));
return tree;
}
/**
* @return
*/
@SuppressWarnings("serial")
private List<IColumn<TaskNode, String>> createColumns()
{
final TaskTree taskTree = getTaskTree();
final CellItemListener<TaskNode> cellItemListener = new CellItemListener<TaskNode>() {
public void populateItem(final Item<ICellPopulator<TaskNode>> item, final String componentId, final IModel<TaskNode> rowModel)
{
final TaskNode taskNode = rowModel.getObject();
TaskListPage.appendCssClasses(item, taskNode.getTask(), highlightedTaskNodeId);
}
};
final List<IColumn<TaskNode, String>> columns = new ArrayList<IColumn<TaskNode, String>>();
columns.add(new TreeColumn<TaskNode, String>(new ResourceModel("task")) {
@Override
public void populateItem(final Item<ICellPopulator<TaskNode>> cellItem, final String componentId, final IModel<TaskNode> rowModel)
{
final RepeatingView view = new RepeatingView(componentId);
cellItem.add(view);
final TaskNode taskNode = rowModel.getObject();
if (selectMode == false) {
view.add(new ListSelectActionPanel(view.newChildId(), rowModel, TaskEditPage.class, taskNode.getId(), parentPage, ""));
} else {
view.add(new ListSelectActionPanel(view.newChildId(), rowModel, caller, selectProperty, taskNode.getId(), ""));
}
AbstractListPage.addRowClick(cellItem);
final NodeModel<TaskNode> nodeModel = (NodeModel<TaskNode>) rowModel;
final Component nodeComponent = getTree().newNodeComponent(view.newChildId(), nodeModel.getWrappedModel());
nodeComponent.add(new NodeBorder(nodeModel.getBranches()));
view.add(nodeComponent);
cellItemListener.populateItem(cellItem, componentId, rowModel);
}
});
columns.add(new CellItemListenerPropertyColumn<TaskNode>(new ResourceModel("task.consumption"), null, "task", cellItemListener) {
@Override
public void populateItem(final Item<ICellPopulator<TaskNode>> item, final String componentId, final IModel<TaskNode> rowModel)
{
final TaskNode node = rowModel.getObject();
item.add(TaskListPage.getConsumptionBarPanel(tree, componentId, getTaskTree(), selectMode, node));
cellItemListener.populateItem(item, componentId, rowModel);
}
});
if (showCost == true) {
columns.add(new CellItemListenerPropertyColumn<TaskNode>(new ResourceModel("fibu.kost2"), null, "task.kost2", cellItemListener) {
@Override
public void populateItem(final Item<ICellPopulator<TaskNode>> item, final String componentId, final IModel<TaskNode> rowModel)
{
final Label label = TaskListPage.getKostLabel(componentId, getTaskTree(), rowModel.getObject().getTask());
item.add(label);
cellItemListener.populateItem(item, componentId, rowModel);
}
});
}
if (taskTree.hasOrderPositionsEntries() == true && showOrders == true) {
columns.add(new CellItemListenerPropertyColumn<TaskNode>(new ResourceModel("fibu.auftrag.auftraege"), null, null, cellItemListener) {
@Override
public void populateItem(final Item<ICellPopulator<TaskNode>> item, final String componentId, final IModel<TaskNode> rowModel)
{
final TaskNode taskNode = rowModel.getObject();
final Set<AuftragsPositionVO> orderPositions = taskTree.getOrderPositionEntries(taskNode.getId());
if (CollectionUtils.isEmpty(orderPositions) == true) {
final Label label = new Label(componentId, ""); // Empty label.
item.add(label);
} else {
final OrderPositionsPanel orderPositionsPanel = new OrderPositionsPanel(componentId) {
@Override
protected void onBeforeRender()
{
super.onBeforeRender();
// Lazy initialization because getString(...) of OrderPositionsPanel fails if panel.init(orderPositions) is called directly
// after instantiation.
init(orderPositions);
};
};
item.add(orderPositionsPanel);
}
cellItemListener.populateItem(item, componentId, rowModel);
}
});
}
columns.add(new CellItemListenerPropertyColumn<TaskNode>(new ResourceModel("shortDescription"), null, "task.shortDescription",
cellItemListener));
if (accessChecker.isLoggedInUserMemberOfGroup(ProjectForgeGroup.FINANCE_GROUP) == true) {
columns.add(new DatePropertyColumn<TaskNode>(dateTimeFormatter, parentPage.getString("task.protectTimesheetsUntil.short"), null,
"task.protectTimesheetsUntil", cellItemListener));
}
columns.add(new CellItemListenerPropertyColumn<TaskNode>(new ResourceModel("task.reference"), null, "reference", cellItemListener));
columns.add(new CellItemListenerPropertyColumn<TaskNode>(new ResourceModel("priority"), null, "priority", cellItemListener) {
@Override
public void populateItem(final Item<ICellPopulator<TaskNode>> item, final String componentId, final IModel<TaskNode> rowModel)
{
final Label label = TaskListPage.getPriorityLabel(componentId, priorityFormatter, rowModel.getObject().getTask());
item.add(label);
cellItemListener.populateItem(item, componentId, rowModel);
}
});
columns.add(new CellItemListenerPropertyColumn<TaskNode>(new ResourceModel("status"), null, "status", cellItemListener) {
@Override
public void populateItem(final Item<ICellPopulator<TaskNode>> item, final String componentId, final IModel<TaskNode> rowModel)
{
final Label label = TaskListPage.getStatusLabel(componentId, taskFormatter, rowModel.getObject().getTask());
item.add(label);
cellItemListener.populateItem(item, componentId, rowModel);
}
});
final UserPropertyColumn<TaskNode> userPropertyColumn = new UserPropertyColumn<TaskNode>(parentPage.getString("task.assignedUser"),
null, "task.responsibleUserId", cellItemListener).withUserFormatter(userFormatter).setUserGroupCache(userGroupCache);
columns.add(userPropertyColumn);
return columns;
}
protected void addColumn(final WebMarkupContainer parent, final Component component, final String cssStyle)
{
if (cssStyle != null) {
component.add(AttributeModifier.append("style", new Model<String>(cssStyle)));
}
parent.add(component);
}
/**
* @param id
* @param model
* @return
*/
@SuppressWarnings("serial")
protected Component newContentComponent(final String id, final TableTree<TaskNode, String> tree, final IModel<TaskNode> model)
{
return new Folder<TaskNode>(id, tree, model) {
@Override
protected IModel< ? > newLabelModel(final IModel<TaskNode> model)
{
return new PropertyModel<String>(model, "task.title");
}
};
}
/**
* @param selectMode the selectMode to set
* @return this for chaining.
*/
public TaskTreeBuilder setSelectMode(final boolean selectMode)
{
this.selectMode = selectMode;
return this;
}
/**
* @param showRootNode the showRootNode to set
* @return this for chaining.
*/
public TaskTreeBuilder setShowRootNode(final boolean showRootNode)
{
this.showRootNode = showRootNode;
return this;
}
/**
* @param showCost the showCost to set
* @return this for chaining.
*/
public TaskTreeBuilder setShowCost(final boolean showCost)
{
this.showCost = showCost;
return this;
}
/**
* @param showOrders the showOrders to set
* @return this for chaining.
*/
public TaskTreeBuilder setShowOrders(final boolean showOrders)
{
this.showOrders = showOrders;
return this;
}
public TaskTreeBuilder set(final AccessChecker accessChecker, final TaskDao taskDao, final TaskFormatter taskFormatter,
final PriorityFormatter priorityFormatter, final UserFormatter userFormatter, final DateTimeFormatter dateTimeFormatter,
final UserGroupCache userGroupCache)
{
this.accessChecker = accessChecker;
this.taskFormatter = taskFormatter;
this.priorityFormatter = priorityFormatter;
this.userFormatter = userFormatter;
this.dateTimeFormatter = dateTimeFormatter;
this.userGroupCache = userGroupCache;
this.dateTimeFormatter = dateTimeFormatter;
return this;
}
/**
* @param caller the caller to set
* @return this for chaining.
*/
public TaskTreeBuilder setCaller(final ISelectCallerPage caller)
{
this.caller = caller;
return this;
}
/**
* @param selectProperty the selectProperty to set
* @return this for chaining.
*/
public TaskTreeBuilder setSelectProperty(final String selectProperty)
{
this.selectProperty = selectProperty;
return this;
}
/**
* @param highlightedTaskNodeId the highlightedTaskNodeId to set
* @return this for chaining.
*/
public TaskTreeBuilder setHighlightedTaskNodeId(final Integer highlightedTaskNodeId)
{
this.highlightedTaskNodeId = highlightedTaskNodeId;
final TaskNode node = getTaskTree().getTaskNodeById(highlightedTaskNodeId);
if (node == null) {
// Shouldn't occur.
return this;
}
// Open all ancestor nodes of the highlighted node:
final Set<TaskNode> set = TaskTreeExpansion.getExpansionModel().getObject();
TaskNode parent = node.getParent();
while (parent != null) {
set.add(parent);
parent = parent.getParent();
}
return this;
}
private TaskTree getTaskTree() {
return Registry.instance().getTaskTree();
}
}