/**
* 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.course.editor;
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.tree.GenericTreeModel;
import org.olat.core.gui.components.tree.InsertEvent;
import org.olat.core.gui.components.tree.InsertionPoint.Position;
import org.olat.core.gui.components.tree.InsertionTreeModel;
import org.olat.core.gui.components.tree.MenuTree;
import org.olat.core.gui.components.tree.TreeNode;
import org.olat.core.gui.components.tree.TreePosition;
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.messages.MessageUIFactory;
import org.olat.core.id.OLATResourceable;
import org.olat.core.logging.activity.CourseLoggingAction;
import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
import org.olat.core.util.nodes.INode;
import org.olat.core.util.tree.TreeHelper;
import org.olat.core.util.tree.TreeVisitor;
import org.olat.core.util.tree.Visitor;
import org.olat.course.CourseFactory;
import org.olat.course.CourseModule;
import org.olat.course.ICourse;
import org.olat.course.nodes.CourseNode;
import org.olat.course.tree.CourseEditorTreeNode;
import org.olat.util.logging.activity.LoggingResourceable;
/**
*
* Description:<br>
* TODO: guido Class Description for MoveCopySubtreeController
*
*/
public class MoveCopySubtreeController extends BasicController {
private MenuTree insertTree;
private Link selectButton, cancelButton;
private InsertTreeModel insertModel;
private boolean copy;
private String copyNodeId;
private OLATResourceable ores;
private CourseEditorTreeNode moveCopyFrom;
public MoveCopySubtreeController(UserRequest ureq, WindowControl wControl, OLATResourceable ores,
CourseEditorTreeNode moveCopyFrom, boolean copy) {
super(ureq, wControl);
this.ores = ores;
this.moveCopyFrom = moveCopyFrom;
this.copy = copy;
ICourse course = CourseFactory.getCourseEditSession(ores.getResourceableId());
addLoggingResourceable(LoggingResourceable.wrap(course));
addLoggingResourceable(LoggingResourceable.wrap(moveCopyFrom.getCourseNode()));
insertTree = new MenuTree(null, "copy_node_selection", this);
insertTree.enableInsertTool(true);
insertModel = new InsertTreeModel(course.getEditorTreeModel().getRootNode(), moveCopyFrom);
insertTree.setTreeModel(insertModel);
VelocityContainer mainVC = createVelocityContainer("moveCopyNode");
selectButton = LinkFactory.createButton("insertAtSelectedTreepos", mainVC, this);
selectButton.setCustomEnabledLinkCSS("btn btn-primary");
selectButton.setCustomDisabledLinkCSS("btn btn-default");
selectButton.setEnabled(false);
cancelButton = LinkFactory.createButton("cancel", mainVC, this);
int numOfNodes = TreeHelper.totalNodeCount(insertModel.getRootNode());
if(numOfNodes > CourseModule.getCourseNodeLimit()){
String msg = getTranslator().translate("warning.containsXXXormore.nodes",
new String[]{String.valueOf(numOfNodes),String.valueOf(CourseModule.getCourseNodeLimit()+1)});
Controller tmp = MessageUIFactory.createWarnMessage(ureq, wControl, null, msg);
listenTo(tmp);
mainVC.put("nodelimitexceededwarning", tmp.getInitialComponent());
}
mainVC.put("selection", insertTree);
putInitialPanel(mainVC);
}
@Override
protected void doDispose() {
// nothing to dispose
}
/**
* Returns node-id of a new copied node.
* @return Returns null when no copy-workflow happens.
*/
public String getCopyNodeId() {
return copyNodeId;
}
@Override
public void event(UserRequest ureq, Component source, Event event) {
if(source == cancelButton) {
fireEvent(ureq, Event.CANCELLED_EVENT);
} else if(source == selectButton) {
TreePosition tp = insertTree.getInsertionPosition();
if(tp != null) {
doInsert(ureq, tp);
}
} else if(event instanceof InsertEvent) {
boolean canSelect = insertTree.getInsertionPoint() != null;
selectButton.setEnabled(canSelect);
}
}
private void doInsert(UserRequest ureq, TreePosition tp) {
ICourse course = CourseFactory.getCourseEditSession(ores.getResourceableId());
int insertPos = tp.getChildpos();
CourseNode selectedNode = getCourseNode(tp.getParentTreeNode());
CourseEditorTreeNode insertParent = course.getEditorTreeModel().getCourseEditorNodeById(selectedNode.getIdent());
// check if insert position is within the to-be-copied tree
if (course.getEditorTreeModel().checkIfIsChild(insertParent, moveCopyFrom)) {
showError("movecopynode.error.overlap");
fireEvent(ureq, Event.CANCELLED_EVENT);
} else if (copy) { // do a copy
// copy subtree and save model
recursiveCopy(moveCopyFrom, insertParent, insertPos, true, CourseFactory.getCourseEditSession(ores.getResourceableId()));
CourseFactory.saveCourseEditorTreeModel(course.getResourceableId());
ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_EDITOR_NODE_COPIED, getClass());
fireEvent(ureq, Event.DONE_EVENT);
} else { // move only
if (insertParent.getIdent().equals(moveCopyFrom.getParent().getIdent())) {
// same parent, adjust insertPos
if (insertPos > moveCopyFrom.getPosition()) insertPos--;
}
insertParent.insert(moveCopyFrom, insertPos);
moveCopyFrom.setDirty(true);
//mark subtree as dirty
TreeVisitor tv = new TreeVisitor( new Visitor() {
@Override
public void visit(INode node) {
CourseEditorTreeNode cetn = (CourseEditorTreeNode)node;
cetn.setDirty(true);
}
},moveCopyFrom,true);
tv.visitAll();
CourseFactory.saveCourseEditorTreeModel(course.getResourceableId());
showInfo("movecopynode.info.condmoved");
ThreadLocalUserActivityLogger.log(CourseLoggingAction.COURSE_EDITOR_NODE_MOVED, getClass());
fireEvent(ureq, Event.DONE_EVENT);
}
}
private CourseNode getCourseNode(TreeNode tn) {
CourseEditorTreeNode ctn = (CourseEditorTreeNode) tn;
CourseNode cn = ctn.getCourseNode();
return cn;
}
private void recursiveCopy(CourseEditorTreeNode copyFrom2, CourseEditorTreeNode insertParent, int pos, boolean firstIteration, ICourse course) {
// create copy of course node
CourseNode copyOfNode = copyFrom2.getCourseNode().createInstanceForCopy(firstIteration, course, getIdentity());
copyNodeId = copyOfNode.getIdent();
// Insert at desired position
course.getEditorTreeModel().insertCourseNodeAt(copyOfNode, insertParent.getCourseNode(), pos);
CourseEditorTreeNode insertedEditorTreeNode = course.getEditorTreeModel().getCourseEditorNodeById(copyOfNode.getIdent());
for (int i = 0; i < copyFrom2.getChildCount(); i++) {
recursiveCopy(course.getEditorTreeModel().getCourseEditorNodeById(copyFrom2.getChildAt(i).getIdent()), insertedEditorTreeNode, i, false, course);
}
}
private static class InsertTreeModel extends GenericTreeModel implements InsertionTreeModel {
private static final long serialVersionUID = -3079238081023850884L;
private CourseEditorTreeNode source;
public InsertTreeModel(TreeNode rootNode, CourseEditorTreeNode source) {
this.setRootNode(rootNode);
this.source = source;
}
@Override
public boolean isSource(TreeNode node) {
return source == node;
}
@Override
public Position[] getInsertionPosition(TreeNode node) {
Position[] positions;
if(source == node) {
positions = new Position[0];
} else if(getRootNode() == node) {
positions = new Position[] { Position.under };
} else if(TreeHelper.isInParentLine(node, source)) {
positions = new Position[0];
} else {
positions = new Position[] { Position.up, Position.down, Position.under };
}
return positions;
}
}
}