/**
* 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.
* <p>
*/
package org.olat.course.nodes.ta;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import org.olat.core.commons.modules.bc.FolderConfig;
import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
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.table.BaseTableDataModelWithoutFilter;
import org.olat.core.gui.components.table.DefaultColumnDescriptor;
import org.olat.core.gui.components.table.StaticColumnDescriptor;
import org.olat.core.gui.components.table.Table;
import org.olat.core.gui.components.table.TableController;
import org.olat.core.gui.components.table.TableDataModel;
import org.olat.core.gui.components.table.TableEvent;
import org.olat.core.gui.components.table.TableGuiConfiguration;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.ControllerEventListener;
import org.olat.core.gui.control.DefaultController;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.translator.PackageTranslator;
import org.olat.core.gui.translator.Translator;
import org.olat.core.id.Identity;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.Tracing;
import org.olat.core.util.Formatter;
import org.olat.core.util.Util;
import org.olat.core.util.vfs.VFSItem;
import org.olat.core.util.vfs.VFSLeaf;
import org.olat.core.util.vfs.VFSMediaResource;
import org.olat.course.nodes.CourseNode;
import org.olat.course.nodes.TACourseNode;
import org.olat.course.properties.CoursePropertyManager;
import org.olat.course.run.environment.CourseEnvironment;
import org.olat.modules.ModuleConfiguration;
import org.olat.properties.Property;
/**
* Initial Date: 02.09.2004
*
* @author Mike Stock
*
* Comment:
*
*/
public class TaskController extends DefaultController implements ControllerEventListener {
private static final String PACKAGE = Util.getPackageName(TaskController.class);
private static final String VELOCITY_ROOT = Util.getPackageVelocityRoot(PACKAGE);
private static final String ACTION_PREVIEW = "ta.preview";
private static final String VC_NOMORETASKS = "nomoretasks";
private static final String VC_ASSIGNEDTASK = "assignedtask";
private static final String VC_ASSIGNEDTASK_NEWWINDOW = "newwindow";
private static final String VC_TASKTEXT = "taskText";
static final String PROP_SAMPLED = "smpl";
/** Property key for "assigned" property. */
public static final String PROP_ASSIGNED = "ass";
/** Configuration parameter indicating manual selection task type. */
public static final String TYPE_MANUAL = "manual";
/** Configuration parameter indicating auto selection task type. */
public static final String TYPE_AUTO = "auto";
/** Configuration parameter indicating task-preview mode. */
public static final String WITH_PREVIEW = "preview";
/** Configuration parameter indicating non task-preview mode. */
public static final String WITHOUT_PREVIEW = "no_preview";
// config
private String taskType;
private String taskText;
private File taskFolder;
private boolean samplingWithReplacement = true;
private CourseEnvironment courseEnv;
private CourseNode node;
private Translator translator;
private VelocityContainer myContent;
private Link taskLaunchButton;
private TableController tableCtr;
private TaskTableModel taskTableModel;
private String assignedTask;
private Mapper mapper;
/**
* Implements a task component.
* @param ureq
* @param wControl
* @param config
* @param node
* @param courseEnv
*/
public TaskController(UserRequest ureq, WindowControl wControl, ModuleConfiguration config,
CourseNode node, CourseEnvironment courseEnv) {
super(wControl);
this.node = node;
this.courseEnv = courseEnv;
readConfig(config);
translator = new PackageTranslator(PACKAGE, ureq.getLocale());
myContent = new VelocityContainer("taskVC", VELOCITY_ROOT + "/taskAssigned.html", translator, this);
taskLaunchButton = LinkFactory.createButtonSmall("task.launch", myContent, this);
taskLaunchButton.setTarget("_blank");
taskLaunchButton.setAjaxEnabled(false); // opened in new window
if ( (taskText != null) && (taskText.length() > 0) ) {
myContent.contextPut(VC_TASKTEXT, taskText);
}
// check if user already chose a task
assignedTask = getAssignedTask(ureq.getIdentity(), courseEnv, node);
if (assignedTask != null) { //
pushTaskToVC();
} else {
// prepare choose task
if (taskType.equals(TYPE_AUTO)) { // automatically choose a task
assignedTask = assignTask(ureq.getIdentity());
if (assignedTask != null) {
pushTaskToVC();
} else {
myContent.contextPut(VC_NOMORETASKS, translator.translate("task.nomoretasks"));
}
} else { // let user choose a task
myContent.setPage(VELOCITY_ROOT + "/taskChoose.html");
TableGuiConfiguration tableConfig = new TableGuiConfiguration();
tableCtr = new TableController(tableConfig, ureq, wControl, translator, this);
// Switch between non-task-preview or task-preview
Boolean hasPreview = config.getBooleanEntry(TACourseNode.CONF_TASK_PREVIEW);
if ( (hasPreview == null) || (hasPreview.booleanValue()==false )) {
// No Preview Mode, Show only file-name
tableCtr.addColumnDescriptor(new DefaultColumnDescriptor("task.table.th_task", 0, null, ureq.getLocale()));
} else {
// Preview Mode, Show only file-name
DefaultColumnDescriptor columnDescriptor = new DefaultColumnDescriptor("task.table.th_task", 0, ACTION_PREVIEW, ureq.getLocale());
columnDescriptor.setIsPopUpWindowAction(true,DefaultColumnDescriptor.DEFAULT_POPUP_ATTRIBUTES );
tableCtr.addColumnDescriptor(columnDescriptor);
}
tableCtr.addColumnDescriptor(new StaticColumnDescriptor("seltask", "task.table.th_action", translator.translate("task.table.choose")));
List availableTasks = compileAvailableTasks();
if (availableTasks.size() == 0) { // no more tasks available
myContent.contextPut(VC_NOMORETASKS, translator.translate("task.nomoretasks"));
}
taskTableModel = new TaskTableModel(availableTasks);
tableCtr.setTableDataModel(taskTableModel);
myContent.put("taskTable", tableCtr.getInitialComponent());
}
}
setInitialComponent(myContent);
}
/**
* @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, org.olat.core.gui.components.Component, org.olat.core.gui.control.Event)
*/
public void event(UserRequest ureq, Component source, Event event) {
if (Tracing.isDebugEnabled(TaskController.class)){
Tracing.logDebug("Test Component.event source" + source + " , event=" + event, TaskController.class);
}
if (source == taskLaunchButton) {
//deliver files the same way as in preview
doFileDelivery(ureq, assignedTask);
}
}
/**
* @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event)
*/
public void event(UserRequest ureq, Controller source, Event event) {
if (Tracing.isDebugEnabled(TaskController.class)){
Tracing.logDebug("Test Controller.event source" + source + " , event=" + event, TaskController.class);
}
if (source == tableCtr) {
if (event.getCommand().equals(Table.COMMANDLINK_ROWACTION_CLICKED)) {
TableEvent ta = (TableEvent) event;
if (ta.getActionId().equals(TaskController.ACTION_PREVIEW)) {
String previewTask = taskTableModel.getRowData(ta.getRowId());
doFileDelivery(ureq, previewTask);
} else {
// select a task
assignedTask = taskTableModel.getRowData(ta.getRowId());
List availableTasks = compileAvailableTasks();
if (!availableTasks.contains(assignedTask)) {
getWindowControl().setWarning(translator.translate("task.chosen"));
taskTableModel.setTasks(availableTasks);
tableCtr.modelChanged();
if (availableTasks.size() == 0) { // no more tasks available
myContent.contextPut(VC_NOMORETASKS, translator.translate("task.nomoretasks"));
}
} else {
setAssignedTask(ureq.getIdentity(), assignedTask);
if (!samplingWithReplacement) markTaskAsSampled(assignedTask);
pushTaskToVC();
}
}
}
}
}
private void pushTaskToVC() {
if (assignedTask == null) return;
myContent.contextPut(VC_ASSIGNEDTASK, assignedTask);
/*myContent.contextPut(VC_ASSIGNEDTASK_NEWWINDOW,
new Boolean(!(assignedTask.toLowerCase().endsWith(".html") || assignedTask.toLowerCase().endsWith(".htm"))));*/
myContent.contextPut(VC_ASSIGNEDTASK_NEWWINDOW,Boolean.TRUE);
myContent.setPage(VELOCITY_ROOT + "/taskAssigned.html");
}
/**
* Auto-assign a task to an identity and mark it as sampled if necessary.
* @param identity
* @return name of the assigned task or null if no more tasks are available.
*/
private String assignTask(Identity identity) {
List availableTasks = compileAvailableTasks();
if (availableTasks.size() == 0 && samplingWithReplacement) {
unmarkAllSampledTasks(); // unmark all tasks if samplingWithReplacement and no more tasks available
availableTasks = compileAvailableTasks(); // refetch tasks
}
if (availableTasks.size() == 0) return null; // no more task available
String task = (String)availableTasks.get((new Random()).nextInt(availableTasks.size()));
setAssignedTask(identity, task); // assignes the file to this identity
if (!samplingWithReplacement)
markTaskAsSampled(task); // remove the file from available files
return task;
}
public static String getAssignedTask(Identity identity, CourseEnvironment courseEnv, CourseNode node) {
List samples = courseEnv.getCoursePropertyManager()
.findCourseNodeProperties(node, identity, null, PROP_ASSIGNED);
if (samples.size() == 0) return null; // no sample assigned yet
return ((Property)samples.get(0)).getStringValue();
}
private void setAssignedTask(Identity identity, String task) {
CoursePropertyManager cpm = courseEnv.getCoursePropertyManager();
Property p = cpm.createCourseNodePropertyInstance(node, identity, null, PROP_ASSIGNED, null, null, task, null);
cpm.saveProperty(p);
}
private void markTaskAsSampled(String task) {
CoursePropertyManager cpm = courseEnv.getCoursePropertyManager();
Property p = cpm.createCourseNodePropertyInstance(node, null, null, PROP_SAMPLED, null, null, task, null);
cpm.saveProperty(p);
}
private void unmarkAllSampledTasks() {
courseEnv.getCoursePropertyManager().deleteNodeProperties(node, PROP_SAMPLED);
}
/**
* Compiles a list of tasks based on the available files in the task folder,
* which have not been sampled so far.
* @return List of available tasks.
*/
private List compileAvailableTasks() {
File[] taskSources = taskFolder.listFiles();
List tasks = new ArrayList(taskSources.length);
List sampledTasks = compileSampledTasks();
for (int i = 0; i < taskSources.length; i++) {
File nextTask = taskSources[i];
if (nextTask.isFile() && !sampledTasks.contains(nextTask.getName()))
tasks.add(nextTask.getName());
}
return tasks;
}
/**
* Compile a list of tasks marked as sampled.
* @return List of sampled tasks.
*/
private List compileSampledTasks() {
List sampledTasks = new ArrayList();
List samples = courseEnv.getCoursePropertyManager()
.findCourseNodeProperties(node, null, null, PROP_SAMPLED);
for (Iterator iter = samples.iterator(); iter.hasNext();) {
Property sample = (Property) iter.next();
sampledTasks.add(sample.getStringValue());
}
return sampledTasks;
}
private void readConfig(ModuleConfiguration config) {
// get task type
taskType = (String)config.get(TACourseNode.CONF_TASK_TYPE);
if (!(taskType.equals(TYPE_MANUAL) || taskType.equals(TYPE_AUTO)))
throw new AssertException("Invalid task type: " + taskType);
// get folder path
String sTaskFolder = FolderConfig.getCanonicalRoot() + TACourseNode.getTaskFolderPathRelToFolderRoot(courseEnv, node);
taskFolder = new File(sTaskFolder);
if (!taskFolder.exists() && !taskFolder.mkdirs())
throw new AssertException("Task folder " + sTaskFolder + " does not exist.");
// get sampling type
Boolean bSampling = (Boolean)config.get(TACourseNode.CONF_TASK_SAMPLING_WITH_REPLACEMENT);
samplingWithReplacement = (bSampling == null) ? true : bSampling.booleanValue();
// get task introductory text
taskText = (String)config.get(TACourseNode.CONF_TASK_TEXT);
}
/**
* Model holding available tasks.
*/
public class TaskTableModel extends BaseTableDataModelWithoutFilter implements TableDataModel {
private List tasks;
private static final int columnCount = 1;
/**
* @param tasks
*/
public TaskTableModel(List tasks) { this.tasks = tasks; }
/**
* @param tasks
*/
public void setTasks(List tasks) { this.tasks = tasks; }
/**
* @see org.olat.core.gui.components.table.TableDataModel#getColumnCount()
*/
public int getColumnCount() { return columnCount; }
/**
* @see org.olat.core.gui.components.table.TableDataModel#getRowCount()
*/
public int getRowCount() { return tasks.size(); }
/**
* @see org.olat.core.gui.components.table.TableDataModel#getValueAt(int, int)
*/
public Object getValueAt(int row, int col) { return getRowData(row); }
/**
* @param row
* @return selected task name.
*/
public String getRowData(int row) { return (String)tasks.get(row); }
}
/**
*
* @see org.olat.core.gui.control.DefaultController#doDispose(boolean)
*/
protected void doDispose() {
if (tableCtr != null) {
tableCtr.dispose();
tableCtr = null;
}
}
/**
* deliver the selected file and show in a popup
*
* @param ureq
* @param command
*/
private void doFileDelivery(UserRequest ureq, String taskFile) {
OlatRootFolderImpl forumContainer = new OlatRootFolderImpl(TACourseNode.getTaskFolderPathRelToFolderRoot(courseEnv, node), null);
VFSItem item = forumContainer.resolve(taskFile);
if (item instanceof VFSLeaf) {
VFSLeaf leaf = (VFSLeaf)item;
ureq.getDispatchResult().setResultingMediaResource(new VFSMediaResource(leaf));
} else if (item==null) {
Tracing.logWarn("Can not cast to VFSLeaf. item==null, taskFile=" + taskFile,null, TaskController.class);
} else {
Tracing.logWarn("Can not cast to VFSLeaf. item.class.name=" + item.getClass().getName() + ", taskFile="+taskFile,null, TaskController.class);
}
}
}