Initial version of basic project task management application.
git-svn-id: https://samskivert.googlecode.com/svn/trunk@897 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
twodue.war
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<!-- build configuration for 'Two Due' project -->
|
||||||
|
<project name="Two Due" default="compile" basedir=".">
|
||||||
|
|
||||||
|
<!-- import properties -->
|
||||||
|
<property file="build.properties"/>
|
||||||
|
|
||||||
|
<!-- configuration parameters -->
|
||||||
|
<property name="app.name" value="twodue"/>
|
||||||
|
<property name="web.home" value="/export/wayward/pages"/>
|
||||||
|
<property name="deploy.dir" value="${web.home}/${app.name}"/>
|
||||||
|
<property name="dist.war" value="${app.name}.war"/>
|
||||||
|
<property name="build.compiler" value="jikes"/>
|
||||||
|
|
||||||
|
<!-- we want to access the environment -->
|
||||||
|
<property environment="env"/>
|
||||||
|
<property name="java.libraries" value="${env.JAVA_LIBS}"/>
|
||||||
|
|
||||||
|
<!-- prepares the application directories -->
|
||||||
|
<target name="prepare">
|
||||||
|
<mkdir dir="${deploy.dir}"/>
|
||||||
|
<copy todir="${deploy.dir}">
|
||||||
|
<fileset dir="web"/>
|
||||||
|
</copy>
|
||||||
|
<mkdir dir="${deploy.dir}/WEB-INF"/>
|
||||||
|
<copy file="etc/web.xml" tofile="${deploy.dir}/WEB-INF/web.xml"/>
|
||||||
|
<copy todir="${deploy.dir}/WEB-INF/classes">
|
||||||
|
<fileset dir="etc" includes="**/*.properties"/>
|
||||||
|
</copy>
|
||||||
|
<mkdir dir="${deploy.dir}/WEB-INF/classes"/>
|
||||||
|
<mkdir dir="${deploy.dir}/WEB-INF/lib"/>
|
||||||
|
<copy todir="${deploy.dir}/WEB-INF/lib">
|
||||||
|
<fileset dir="lib"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- cleans out the installed application -->
|
||||||
|
<target name="clean">
|
||||||
|
<delete dir="${deploy.dir}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- build the java class files -->
|
||||||
|
<target name="compile" depends="prepare">
|
||||||
|
<javac srcdir="src" destdir="${deploy.dir}/WEB-INF/classes"
|
||||||
|
debug="on" optimize="off" deprecation="on">
|
||||||
|
<classpath>
|
||||||
|
<pathelement location="${deploy.dir}/WEB-INF/classes"/>
|
||||||
|
<fileset dir="${java.libraries}" includes="**/*.jar"/>
|
||||||
|
<fileset dir="lib" includes="**/*.jar"/>
|
||||||
|
</classpath>
|
||||||
|
</javac>
|
||||||
|
<copy todir="${deploy.dir}/WEB-INF/classes">
|
||||||
|
<fileset dir="src" includes="**/*.properties"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- the default target is to rebuild everything -->
|
||||||
|
<target name="all" depends="clean,prepare,compile"/>
|
||||||
|
|
||||||
|
<!-- builds our distribution files (war and jar) -->
|
||||||
|
<target name="dist" depends="prepare,compile">
|
||||||
|
<jar file="${dist.war}" basedir="${deploy.dir}"/>
|
||||||
|
</target>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
repository.properties
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#
|
||||||
|
# $Id: exceptionmap.properties,v 1.1 2002/11/08 09:14:20 mdb Exp $
|
||||||
|
#
|
||||||
|
# Maps exceptions to friendly error messages
|
||||||
|
|
||||||
|
java.sql.SQLException: A database error has occurred: {m}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- mode: makefile -*-
|
||||||
|
#
|
||||||
|
# $Id: messages.properties,v 1.1 2002/11/08 09:14:20 mdb Exp $
|
||||||
|
#
|
||||||
|
# Translation messages for Two Due application
|
||||||
|
|
||||||
|
#
|
||||||
|
# common translations
|
||||||
|
|
||||||
|
app_name=Two Due
|
||||||
|
|
||||||
|
#
|
||||||
|
# header.wm
|
||||||
|
|
||||||
|
header.combined_title={0}: {1}
|
||||||
|
header.title={0}
|
||||||
|
|
||||||
|
#
|
||||||
|
# app_footer.wm
|
||||||
|
|
||||||
|
app_footer.summary_link=Task Summary
|
||||||
|
app_footer.account_link=Login/out
|
||||||
|
|
||||||
|
#
|
||||||
|
# index.wm
|
||||||
|
|
||||||
|
index.title=Task Summary
|
||||||
|
|
||||||
|
index.message.task_created = Task created.
|
||||||
|
index.message.task_claimed = Task claimed.
|
||||||
|
index.message.task_completed = Task marked as completed.
|
||||||
|
|
||||||
|
index.error.missing_summary = No summary provided for new task.
|
||||||
|
index.error.missing_category = No category provided for new task.
|
||||||
|
index.error.missing_complexity = No complexity selection for new task.
|
||||||
|
index.error.invalid_priority = No priority selection for new task.
|
||||||
|
index.error.missing_taskid = Internal error. No task id provided.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#
|
||||||
|
# $Id: velocity.properties,v 1.1 2002/11/08 09:14:20 mdb Exp $
|
||||||
|
#
|
||||||
|
# Velocity engine configuration overrides
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------------
|
||||||
|
# F O R E A C H P R O P E R T I E S
|
||||||
|
#----------------------------------------------------------------------------
|
||||||
|
# These properties control how the counter is accessed in the #foreach
|
||||||
|
# directive. By default the reference $velocityCount will be available
|
||||||
|
# in the body of the #foreach directive. The default starting value
|
||||||
|
# for this reference is 1.
|
||||||
|
#----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
directive.foreach.counter.name = vidx
|
||||||
|
directive.foreach.counter.initial.value = 0
|
||||||
|
|
||||||
|
# temporarily make life easier
|
||||||
|
file.resource.loader.path = /home/mdb/projects/twodue/web
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
|
||||||
|
<!DOCTYPE web-app
|
||||||
|
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
|
||||||
|
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
|
||||||
|
|
||||||
|
<web-app>
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>dispatcher</servlet-name>
|
||||||
|
<servlet-class>com.samskivert.velocity.DispatcherServlet</servlet-class>
|
||||||
|
<init-param>
|
||||||
|
<param-name>org.apache.velocity.properties</param-name>
|
||||||
|
<param-value>/velocity.properties</param-value>
|
||||||
|
</init-param>
|
||||||
|
<init-param>
|
||||||
|
<param-name>app_class</param-name>
|
||||||
|
<param-value>com.samskivert.twodue.TwoDueApp</param-value>
|
||||||
|
</init-param>
|
||||||
|
<init-param>
|
||||||
|
<param-name>logic_package</param-name>
|
||||||
|
<param-value>com.samskivert.twodue.logic</param-value>
|
||||||
|
</init-param>
|
||||||
|
<init-param>
|
||||||
|
<param-name>messages_path</param-name>
|
||||||
|
<param-value>messages</param-value>
|
||||||
|
</init-param>
|
||||||
|
<!--
|
||||||
|
<init-param>
|
||||||
|
<param-name>site_jar_path</param-name>
|
||||||
|
<param-value>/usr/share/java/webapps/site-data</param-value>
|
||||||
|
</init-param>
|
||||||
|
<init-param>
|
||||||
|
<param-name>site_messages_path</param-name>
|
||||||
|
<param-value>site_messages</param-value>
|
||||||
|
</init-param>
|
||||||
|
-->
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>dispatcher</servlet-name>
|
||||||
|
<url-pattern>*.wm</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<welcome-file-list>
|
||||||
|
<welcome-file>index.wm</welcome-file>
|
||||||
|
</welcome-file-list>
|
||||||
|
</web-app>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
*.jar
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
commons-collections.jar
|
||||||
|
commons-lang.jar
|
||||||
|
jakarta-regexp-1.2.jar
|
||||||
|
mm.mysql-2.0.14-bin.jar
|
||||||
|
samskivert.jar
|
||||||
|
velocity-1.4-dev.jar
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// $Id: Log.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.twodue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A placeholder class that contains a reference to the log object used by
|
||||||
|
* this package.
|
||||||
|
*/
|
||||||
|
public class Log
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This is the log instance that will be used to log all messages for
|
||||||
|
* this package.
|
||||||
|
*/
|
||||||
|
public static com.samskivert.util.Log log =
|
||||||
|
new com.samskivert.util.Log("twodue");
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void debug (String message)
|
||||||
|
{
|
||||||
|
log.debug(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void info (String message)
|
||||||
|
{
|
||||||
|
log.info(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void warning (String message)
|
||||||
|
{
|
||||||
|
log.warning(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void logStackTrace (Throwable t)
|
||||||
|
{
|
||||||
|
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
//
|
||||||
|
// $Id: TwoDueApp.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.twodue;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
import javax.servlet.ServletConfig;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import com.samskivert.io.PersistenceException;
|
||||||
|
import com.samskivert.jdbc.ConnectionProvider;
|
||||||
|
import com.samskivert.jdbc.StaticConnectionProvider;
|
||||||
|
|
||||||
|
import com.samskivert.servlet.JDBCTableSiteIdentifier;
|
||||||
|
import com.samskivert.servlet.SiteIdentifier;
|
||||||
|
import com.samskivert.servlet.user.UserManager;
|
||||||
|
import com.samskivert.util.ServiceUnavailableException;
|
||||||
|
import com.samskivert.velocity.Application;
|
||||||
|
|
||||||
|
import com.samskivert.twodue.data.TaskRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains references to application-wide resources (like the database
|
||||||
|
* repository) and handles initialization and cleanup for those resources.
|
||||||
|
*/
|
||||||
|
public class TwoDueApp extends Application
|
||||||
|
{
|
||||||
|
/** Returns the connection provider in use by this application. */
|
||||||
|
public final ConnectionProvider getConnectionProvider ()
|
||||||
|
{
|
||||||
|
return _conprov;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the task repository in use by the application. */
|
||||||
|
public TaskRepository getRepository ()
|
||||||
|
{
|
||||||
|
return _taskrep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the user manager in use by the application. */
|
||||||
|
public UserManager getUserManager ()
|
||||||
|
{
|
||||||
|
return _usermgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void willInit (ServletConfig config)
|
||||||
|
{
|
||||||
|
super.willInit(config);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// create a static connection provider
|
||||||
|
_conprov = new StaticConnectionProvider(CONN_CONFIG);
|
||||||
|
|
||||||
|
// initialize the user manager
|
||||||
|
Properties props = new Properties();
|
||||||
|
props.put("login_url", "/register/login.wm?from=%R");
|
||||||
|
_usermgr = new UserManager(props, _conprov);
|
||||||
|
|
||||||
|
// initialize the task repository
|
||||||
|
_taskrep = new TaskRepository(_conprov);
|
||||||
|
|
||||||
|
Log.info("TwoDue application initialized.");
|
||||||
|
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Log.warning("Error initializing application: " + t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown ()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
_usermgr.shutdown();
|
||||||
|
Log.info("TwoDue application shutdown.");
|
||||||
|
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Log.warning("Error shutting down repository: " + t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** We want a special site identifier. */
|
||||||
|
protected SiteIdentifier createSiteIdentifier (ServletContext ctx)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return new JDBCTableSiteIdentifier(_conprov);
|
||||||
|
} catch (PersistenceException pe) {
|
||||||
|
throw new ServiceUnavailableException(
|
||||||
|
"Can't access site database.", pe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A reference to our connection provider. */
|
||||||
|
protected ConnectionProvider _conprov;
|
||||||
|
|
||||||
|
/** A reference to our user manager. */
|
||||||
|
protected UserManager _usermgr;
|
||||||
|
|
||||||
|
/** A reference to our task repository. */
|
||||||
|
protected TaskRepository _taskrep;
|
||||||
|
|
||||||
|
/** The path to our database configuration file. */
|
||||||
|
protected static final String CONN_CONFIG = "repository.properties";
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// $Id: Task.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.twodue.data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.sql.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the basic data associated with a task.
|
||||||
|
*/
|
||||||
|
public class Task
|
||||||
|
{
|
||||||
|
public int taskId;
|
||||||
|
|
||||||
|
public String summary;
|
||||||
|
|
||||||
|
public String category;
|
||||||
|
|
||||||
|
public String complexity;
|
||||||
|
|
||||||
|
public int priority;
|
||||||
|
|
||||||
|
public String creator;
|
||||||
|
|
||||||
|
public Date creation;
|
||||||
|
|
||||||
|
public String owner;
|
||||||
|
|
||||||
|
public String completor;
|
||||||
|
|
||||||
|
public Date completion;
|
||||||
|
|
||||||
|
public transient ArrayList notes;
|
||||||
|
}
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
//
|
||||||
|
// $Id: TaskRepository.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.twodue.data;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.Date;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import com.samskivert.io.PersistenceException;
|
||||||
|
import com.samskivert.jdbc.ConnectionProvider;
|
||||||
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
|
import com.samskivert.jdbc.JDBCUtil;
|
||||||
|
import com.samskivert.jdbc.JORARepository;
|
||||||
|
import com.samskivert.jdbc.jora.Cursor;
|
||||||
|
import com.samskivert.jdbc.jora.Session;
|
||||||
|
import com.samskivert.jdbc.jora.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to the task table.
|
||||||
|
*/
|
||||||
|
public class TaskRepository extends JORARepository
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The database identifier that the repository will use when fetching
|
||||||
|
* a connection from the connection provider. The value is
|
||||||
|
* <code>twodue</code> which you'll probably need to know to properly
|
||||||
|
* configure your connection provider.
|
||||||
|
*/
|
||||||
|
public static final String REPOSITORY_DB_IDENT = "twodue";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the repository and opens the task database. A connection
|
||||||
|
* provider should be supplied that will be used to obtain the
|
||||||
|
* necessary database connection. The database identifier used to
|
||||||
|
* obtain our connection is documented by {@link
|
||||||
|
* #REPOSITORY_DB_IDENT}.
|
||||||
|
*
|
||||||
|
* @param provider a connection provider via which the repository will
|
||||||
|
* get its database connection.
|
||||||
|
*/
|
||||||
|
public TaskRepository (ConnectionProvider provider)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
super(provider, REPOSITORY_DB_IDENT);
|
||||||
|
|
||||||
|
// make sure we can get our database connection
|
||||||
|
Connection conn = provider.getConnection(REPOSITORY_DB_IDENT);
|
||||||
|
provider.releaseConnection(REPOSITORY_DB_IDENT, conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documented inherited
|
||||||
|
protected void createTables (Session session)
|
||||||
|
{
|
||||||
|
// create our table objects
|
||||||
|
_ttable = new Table(Task.class.getName(),
|
||||||
|
"TASKS", session, "TASK_ID", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a task and inserts it into the repository.
|
||||||
|
*
|
||||||
|
* @return the newly created task.
|
||||||
|
*/
|
||||||
|
public Task createTask (String summary, String category, String complexity,
|
||||||
|
int priority, String creator)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
// create and configure a task instance
|
||||||
|
Task task = new Task();
|
||||||
|
task.summary = summary;
|
||||||
|
task.category = category;
|
||||||
|
task.complexity = complexity;
|
||||||
|
task.priority = priority;
|
||||||
|
task.creator = creator;
|
||||||
|
createTask(task);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the supplied (properly populated) new task into the task
|
||||||
|
* repository.
|
||||||
|
*/
|
||||||
|
public void createTask (final Task task)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
task.creation = new Date(System.currentTimeMillis());
|
||||||
|
execute(new Operation() {
|
||||||
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
|
throws PersistenceException, SQLException
|
||||||
|
{
|
||||||
|
// insert the task into the entry table
|
||||||
|
_ttable.insert(task);
|
||||||
|
// update the taskId now that it's known
|
||||||
|
task.taskId = liaison.lastInsertedId(conn);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads up the specified task.
|
||||||
|
*/
|
||||||
|
public Task loadTask (int taskId)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
return (Task)execute(new Operation () {
|
||||||
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
|
throws PersistenceException, SQLException
|
||||||
|
{
|
||||||
|
String query = "where COMPLETOR IS NULL AND OWNER IS NULL " +
|
||||||
|
"ORDER BY CATEGORY, PRIORITY";
|
||||||
|
Cursor tc = _ttable.select(query);
|
||||||
|
return tc.toArrayList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads up and returns all unowned, uncompleted tasks, ordered by
|
||||||
|
* priority.
|
||||||
|
*/
|
||||||
|
public ArrayList loadTasks ()
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
return loadTasks("where COMPLETOR IS NULL AND OWNER IS NULL " +
|
||||||
|
"ORDER BY PRIORITY DESC");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads up all owned tasks, ordered by priority.
|
||||||
|
*/
|
||||||
|
public ArrayList loadOwnedTasks ()
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
return loadTasks("where COMPLETOR IS NULL AND OWNER IS NOT NULL " +
|
||||||
|
"ORDER BY PRIORITY DESC");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads up and returns all completed tasks, sorted by completion
|
||||||
|
* date.
|
||||||
|
*
|
||||||
|
* @param start the offset into the sorted list of completed tasks to
|
||||||
|
* start returning tasks.
|
||||||
|
* @param limit the limit to the number of tasks to be returned, or -1
|
||||||
|
* if all completed should be returned.
|
||||||
|
*/
|
||||||
|
public ArrayList loadCompletedTasks (int start, int limit)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
String query = "where COMPLETOR IS NOT NULL " +
|
||||||
|
"ORDER BY COMPLETION DESC LIMIT " + start;
|
||||||
|
if (limit != -1) {
|
||||||
|
query += (", " + limit);
|
||||||
|
}
|
||||||
|
return loadTasks(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Loads lists of tasks. */
|
||||||
|
protected ArrayList loadTasks (final String query)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
return (ArrayList)execute(new Operation () {
|
||||||
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
|
throws PersistenceException, SQLException
|
||||||
|
{
|
||||||
|
Cursor tc = _ttable.select(query);
|
||||||
|
return tc.toArrayList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the specified task as completed by the specified user.
|
||||||
|
*/
|
||||||
|
public void completeTask (final int taskId, final String completor)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
execute(new Operation () {
|
||||||
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
|
throws PersistenceException, SQLException
|
||||||
|
{
|
||||||
|
String query = "update TASKS " +
|
||||||
|
"set COMPLETOR = ?, COMPLETION = ? where TASK_ID = ?";
|
||||||
|
PreparedStatement stmt = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
stmt = conn.prepareStatement(query);
|
||||||
|
stmt.setString(1, completor);
|
||||||
|
stmt.setDate(2, new Date(System.currentTimeMillis()));
|
||||||
|
stmt.setInt(3, taskId);
|
||||||
|
JDBCUtil.checkedUpdate(stmt, 1);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
JDBCUtil.close(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the specified task as owned by the specified user.
|
||||||
|
*/
|
||||||
|
public void claimTask (final int taskId, final String owner)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
execute(new Operation () {
|
||||||
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
|
throws PersistenceException, SQLException
|
||||||
|
{
|
||||||
|
String query = "update TASKS " +
|
||||||
|
"set OWNER = ? where TASK_ID = ?";
|
||||||
|
PreparedStatement stmt = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
stmt = conn.prepareStatement(query);
|
||||||
|
stmt.setString(1, owner);
|
||||||
|
stmt.setInt(2, taskId);
|
||||||
|
JDBCUtil.checkedUpdate(stmt, 1);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
JDBCUtil.close(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a task that was previously loaded from the repository.
|
||||||
|
*/
|
||||||
|
public void updateTask (final Task task)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
execute(new Operation() {
|
||||||
|
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
|
throws PersistenceException, SQLException
|
||||||
|
{
|
||||||
|
_ttable.update(task);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Table _ttable;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// $Id: UserLogic.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.twodue.logic;
|
||||||
|
|
||||||
|
import com.samskivert.servlet.user.User;
|
||||||
|
import com.samskivert.velocity.Application;
|
||||||
|
import com.samskivert.velocity.InvocationContext;
|
||||||
|
import com.samskivert.velocity.Logic;
|
||||||
|
|
||||||
|
import com.samskivert.twodue.TwoDueApp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base logic class for pages that require an authenticated user.
|
||||||
|
*/
|
||||||
|
public abstract class UserLogic implements Logic
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Logic classes should implement this method to perform their normal
|
||||||
|
* duties.
|
||||||
|
*
|
||||||
|
* @param ctx the context in which the request is being invoked.
|
||||||
|
* @param app the web application.
|
||||||
|
* @param user the user record for the authenticated user.
|
||||||
|
*/
|
||||||
|
public abstract void invoke (
|
||||||
|
InvocationContext ctx, TwoDueApp app, User user)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void invoke (Application app, InvocationContext ctx)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
TwoDueApp tdapp = (TwoDueApp)app;
|
||||||
|
User user = tdapp.getUserManager().requireUser(ctx.getRequest());
|
||||||
|
invoke(ctx, tdapp, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
//
|
||||||
|
// $Id: index.java,v 1.1 2002/11/08 09:14:21 mdb Exp $
|
||||||
|
|
||||||
|
package com.samskivert.twodue.logic;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import com.samskivert.util.QuickSort;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
import com.samskivert.servlet.RedirectException;
|
||||||
|
import com.samskivert.servlet.user.User;
|
||||||
|
import com.samskivert.servlet.util.ParameterUtil;
|
||||||
|
import com.samskivert.velocity.InvocationContext;
|
||||||
|
|
||||||
|
import com.samskivert.twodue.Log;
|
||||||
|
import com.samskivert.twodue.TwoDueApp;
|
||||||
|
import com.samskivert.twodue.data.Task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a summary out outstanding and completed tasks.
|
||||||
|
*/
|
||||||
|
public class index extends UserLogic
|
||||||
|
{
|
||||||
|
public void invoke (InvocationContext ctx, TwoDueApp app, User user)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
HttpServletRequest req = ctx.getRequest();
|
||||||
|
|
||||||
|
// display any message we've been asked to display
|
||||||
|
String msg = ParameterUtil.getParameter(req, "message", true);
|
||||||
|
if (!StringUtil.blank(msg)) {
|
||||||
|
ctx.put("error", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we use this to determine whether to show "complete" buttons for
|
||||||
|
// tasks
|
||||||
|
ctx.put("username", user.username);
|
||||||
|
|
||||||
|
// if they've submitted the form, we create a new task and stick
|
||||||
|
// it into the dataabse
|
||||||
|
if (ParameterUtil.parameterEquals(
|
||||||
|
ctx.getRequest(), "action", "create")) {
|
||||||
|
// set the creator from the username of the calling user
|
||||||
|
Task task = new Task();
|
||||||
|
task.creator = user.username;
|
||||||
|
|
||||||
|
// parse our fields
|
||||||
|
task.summary = ParameterUtil.requireParameter(
|
||||||
|
ctx.getRequest(), "summary", "index.error.missing_summary");
|
||||||
|
task.category = ParameterUtil.requireParameter(
|
||||||
|
ctx.getRequest(), "category",
|
||||||
|
"index.error.missing_category");
|
||||||
|
task.complexity = ParameterUtil.requireParameter(
|
||||||
|
ctx.getRequest(), "complexity",
|
||||||
|
"index.error.missing_complexity");
|
||||||
|
task.priority = ParameterUtil.requireIntParameter(
|
||||||
|
ctx.getRequest(), "priority",
|
||||||
|
"index.error.invalid_priority");
|
||||||
|
|
||||||
|
// insert the task into the repository
|
||||||
|
app.getRepository().createTask(task);
|
||||||
|
|
||||||
|
// flip back to this same page minus our query parameters to
|
||||||
|
// clear out the creation form
|
||||||
|
throw new RedirectException(
|
||||||
|
"index.wm?msg=index.message.task_created");
|
||||||
|
|
||||||
|
} else if (ParameterUtil.parameterEquals(
|
||||||
|
ctx.getRequest(), "action", "complete")) {
|
||||||
|
int taskId = ParameterUtil.requireIntParameter(
|
||||||
|
ctx.getRequest(), "task", "index.error.missing_taskid");
|
||||||
|
app.getRepository().completeTask(taskId, user.username);
|
||||||
|
|
||||||
|
// let the user know we updated the database
|
||||||
|
ctx.put("error", "index.message.task_completed");
|
||||||
|
|
||||||
|
} else if (ParameterUtil.parameterEquals(
|
||||||
|
ctx.getRequest(), "action", "claim")) {
|
||||||
|
int taskId = ParameterUtil.requireIntParameter(
|
||||||
|
ctx.getRequest(), "task", "index.error.missing_taskid");
|
||||||
|
app.getRepository().claimTask(taskId, user.username);
|
||||||
|
|
||||||
|
// let the user know we updated the database
|
||||||
|
ctx.put("error", "index.message.task_claimed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// load up outstanding tasks and break them down by complexity
|
||||||
|
String expand = ParameterUtil.getParameter(req, "expand", false);
|
||||||
|
ArrayList tasks = app.getRepository().loadTasks();
|
||||||
|
CatList[] xtasks = categorize(tasks, expand, new Categorizer() {
|
||||||
|
public String category (Task task) {
|
||||||
|
return task.complexity;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.put("xtasks", xtasks);
|
||||||
|
|
||||||
|
// load up owned tasks and break them down by owner
|
||||||
|
tasks = app.getRepository().loadOwnedTasks();
|
||||||
|
CatList[] otasks = categorize(tasks, null, new Categorizer() {
|
||||||
|
public String category (Task task) {
|
||||||
|
return task.owner;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.put("otasks", otasks);
|
||||||
|
|
||||||
|
// load up recently completed tasks
|
||||||
|
tasks = app.getRepository().loadCompletedTasks(0, 6);
|
||||||
|
ctx.put("dtasks", tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class CatList
|
||||||
|
implements Comparable
|
||||||
|
{
|
||||||
|
public String name;
|
||||||
|
public ArrayList tasks;
|
||||||
|
public int pruned;
|
||||||
|
public int compareTo (Object other)
|
||||||
|
{
|
||||||
|
return name.compareTo(((CatList)other).name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static interface Categorizer
|
||||||
|
{
|
||||||
|
public String category (Task task);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CatList[] categorize (
|
||||||
|
ArrayList tasks, String expand, Categorizer catter)
|
||||||
|
{
|
||||||
|
HashMap cmap = new HashMap();
|
||||||
|
int tcount = tasks.size();
|
||||||
|
for (int ii = 0; ii < tcount; ii++) {
|
||||||
|
Task task = (Task)tasks.get(ii);
|
||||||
|
String category = catter.category(task);
|
||||||
|
CatList clist = (CatList)cmap.get(category);
|
||||||
|
if (clist == null) {
|
||||||
|
clist = new CatList();
|
||||||
|
clist.name = category;
|
||||||
|
clist.tasks = new ArrayList();
|
||||||
|
cmap.put(category, clist);
|
||||||
|
}
|
||||||
|
if (expand == null || clist.tasks.size() < 2 ||
|
||||||
|
(expand.equals(category))) {
|
||||||
|
clist.tasks.add(task);
|
||||||
|
} else {
|
||||||
|
clist.pruned++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CatList[] ctasks = new CatList[cmap.size()];
|
||||||
|
Iterator iter = cmap.values().iterator();
|
||||||
|
for (int ii = 0; iter.hasNext(); ii++) {
|
||||||
|
ctasks[ii] = (CatList)iter.next();
|
||||||
|
}
|
||||||
|
QuickSort.sort(ctasks);
|
||||||
|
|
||||||
|
return ctasks;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* $Id: tasks-schema.sql,v 1.1 2002/11/08 09:14:21 mdb Exp $
|
||||||
|
*
|
||||||
|
* Schema for the Two Due tasks and notes table.
|
||||||
|
*/
|
||||||
|
|
||||||
|
drop table if exists TASKS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains basic data for every task in the system.
|
||||||
|
*/
|
||||||
|
CREATE TABLE TASKS
|
||||||
|
(
|
||||||
|
/**
|
||||||
|
* A unique identifier for this task.
|
||||||
|
*/
|
||||||
|
TASK_ID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The summary description of this task.
|
||||||
|
*/
|
||||||
|
SUMMARY VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The comma separated list of categories occupied by this task.
|
||||||
|
*/
|
||||||
|
CATEGORY VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The complexity identifier for this task.
|
||||||
|
*/
|
||||||
|
COMPLEXITY VARCHAR(32) NOT NULL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The priority of this task.
|
||||||
|
*/
|
||||||
|
PRIORITY INTEGER UNSIGNED NOT NULL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user to which this task is currently assigned.
|
||||||
|
*/
|
||||||
|
OWNER VARCHAR(255),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the creator of this task.
|
||||||
|
*/
|
||||||
|
CREATOR VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time of creation of this task.
|
||||||
|
*/
|
||||||
|
CREATION DATE NOT NULL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the completor(s) of this task.
|
||||||
|
*/
|
||||||
|
COMPLETOR VARCHAR(255),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time of completion of this task.
|
||||||
|
*/
|
||||||
|
COMPLETION DATE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines our table keys.
|
||||||
|
*/
|
||||||
|
PRIMARY KEY (TASK_ID),
|
||||||
|
KEY (CATEGORY),
|
||||||
|
KEY (PRIORITY),
|
||||||
|
KEY (CREATION),
|
||||||
|
KEY (COMPLETION)
|
||||||
|
);
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Two Due:
|
||||||
|
<a href="index.wm">$i18n.xlate("app_footer.summary_link")</a> |
|
||||||
|
<a href="/register/">$i18n.xlate("app_footer.account_link")</a>
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#set ($title = $i18n.xlate("edit.title"))
|
||||||
|
#import ("/header.wm")
|
||||||
|
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<form action="index.wm">
|
||||||
|
$form.fixedHidden("action", "update")
|
||||||
|
|
||||||
|
<table cellpadding="2" cellspacing="0" border="0">
|
||||||
|
<tr><td colspan="3" bgcolor="$scolor"> <br></td></tr>
|
||||||
|
|
||||||
|
<tr><td colspan="3">Summary:<br>
|
||||||
|
<textarea name="summary" rows="5" cols="50">
|
||||||
|
</textarea>
|
||||||
|
</td></tr>
|
||||||
|
|
||||||
|
<tr><td>Category:<br>
|
||||||
|
<font face="courier">
|
||||||
|
$form.text("category", "size='10'", "")
|
||||||
|
</font></td>
|
||||||
|
|
||||||
|
<td>Complexity:<br>
|
||||||
|
<font face="courier">
|
||||||
|
<select name="complexity">
|
||||||
|
$form.option("complexity", "1 - Simple hack", "Simple hack", "1 - Simple hack")
|
||||||
|
$form.option("complexity", "2 - Minor feature", "Minor feature", "1 - Simple hack")
|
||||||
|
$form.option("complexity", "3 - Major feature", "Major feature", "1 - Simple hack")
|
||||||
|
$form.option("complexity", "4 - Subsystem", "Subsystem", "1 - Simple hack")
|
||||||
|
$form.option("complexity", "5 - Major refactor", "Major refactor", "1 - Simple hack")
|
||||||
|
</select>
|
||||||
|
</font></td>
|
||||||
|
|
||||||
|
<td>Priority:<br>
|
||||||
|
<font face="courier">
|
||||||
|
<select name="priority">
|
||||||
|
$form.option("priority", "50", "Urgent", "15")
|
||||||
|
$form.option("priority", "25", "Next release", "15")
|
||||||
|
$form.option("priority", "15", "Soon", "15")
|
||||||
|
$form.option("priority", "10", "Before launch", "15")
|
||||||
|
$form.option("priority", "5", "Post launch", "15")
|
||||||
|
$form.option("priority", "1", "On the list", "15")
|
||||||
|
</select>
|
||||||
|
</font></td></tr>
|
||||||
|
|
||||||
|
<tr><td colspan="3" align="right" bgcolor="$scolor">
|
||||||
|
$form.submit("submit", "Update")</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
#import ("/footer.wm")
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<p>
|
||||||
|
<table cellpadding=6 cellspacing=0 border=0 width="100%">
|
||||||
|
<tr bgcolor="$hcolor">
|
||||||
|
<td>
|
||||||
|
#import ("app_footer.wm")
|
||||||
|
</td>
|
||||||
|
<!--
|
||||||
|
<td align="right">
|
||||||
|
©2002
|
||||||
|
<a href="http://www.samskivert.com/">Michael Bayne</a>
|
||||||
|
</td>
|
||||||
|
-->
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
## This is the header and footer color
|
||||||
|
#set ($hcolor = "#CCCCCC")
|
||||||
|
## These colors are used for common UI elements
|
||||||
|
#set ($tcolor = "#99CCFF")
|
||||||
|
#set ($scolor = "#CCCCCC")
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>$i18n.xlate("header.combined_title", $i18n.xlate("app_name"), $title)</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/twodue/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body bgcolor="#FFFFFF" vlink="#003366">
|
||||||
|
|
||||||
|
<table cellpadding="6" cellspacing="0" border="0" width="100%">
|
||||||
|
<tr bgcolor="$hcolor"><td align=right>
|
||||||
|
<span class="big"><b>$i18n.xlate("header.title", $i18n.xlate("app_name"))</b></span><br>
|
||||||
|
$title</td>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
#if ($error)
|
||||||
|
<p>
|
||||||
|
<table cellpadding="6" cellspacing="0" border="0" width="100%">
|
||||||
|
<tr><td><font color="#FF0000">$i18n.xlate($error)</font></td></tr>
|
||||||
|
</table>
|
||||||
|
#end
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
#set ($title = $i18n.xlate("index.title"))
|
||||||
|
#import ("/header.wm")
|
||||||
|
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<table cellpadding="0" cellspacing="15" border="0">
|
||||||
|
<tr><td width="50%"><b>Owned tasks:</b></td>
|
||||||
|
|
||||||
|
<td width="50%"><b>Outstanding tasks:</b></td></tr>
|
||||||
|
|
||||||
|
<tr><td valign="top">
|
||||||
|
<table cellpadding="2" cellspacing="0" border="0" width="100%">
|
||||||
|
|
||||||
|
#foreach ($otask in $otasks)
|
||||||
|
#if ($vidx > 0)
|
||||||
|
<tr><td colspan="3"> </td></tr>
|
||||||
|
#end
|
||||||
|
<tr><td colspan="3" align="left" bgcolor="$scolor">$otask.name</td></tr>
|
||||||
|
|
||||||
|
#foreach ($task in $otask.tasks)
|
||||||
|
<form action="index.wm">
|
||||||
|
$form.fixedHidden("action", "complete")
|
||||||
|
$form.fixedHidden("task", "$task.taskId")
|
||||||
|
#if ($vidx%2 == 0)
|
||||||
|
#set ($rowcolor = "#FFFFFF")
|
||||||
|
#else
|
||||||
|
#set ($rowcolor = "#EEEEEE")
|
||||||
|
#end
|
||||||
|
<tr bgcolor="$rowcolor"><td>
|
||||||
|
#if ($username == $task.owner)
|
||||||
|
$form.imageSubmit("submit", "", "images/complete.png", "Mark as completed")
|
||||||
|
#else
|
||||||
|
|
||||||
|
#end
|
||||||
|
</td>
|
||||||
|
<td>$task.summary</td>
|
||||||
|
<td valign="top">$task.category</td></tr>
|
||||||
|
</form>
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<center class="small">Click the check box to mark a task as completed.</center>
|
||||||
|
|
||||||
|
</td><td valign="top">
|
||||||
|
|
||||||
|
<table cellpadding="2" cellspacing="0" border="0" width="100%">
|
||||||
|
#foreach ($xtask in $xtasks)
|
||||||
|
#if ($vidx > 0)
|
||||||
|
<tr><td colspan="3"> </td></tr>
|
||||||
|
#end
|
||||||
|
<tr bgcolor="$scolor"><td colspan="2" align="left">$xtask.name</td>
|
||||||
|
<td>
|
||||||
|
#if ($xtask.pruned > 0)
|
||||||
|
<a href="index.wm?expand=$xtask.name">($xtask.pruned ...)</a>
|
||||||
|
#else
|
||||||
|
|
||||||
|
#end
|
||||||
|
</td></tr>
|
||||||
|
|
||||||
|
#foreach ($task in $xtask.tasks)
|
||||||
|
<form action="index.wm">
|
||||||
|
$form.fixedHidden("task", "$task.taskId")
|
||||||
|
#if ($vidx%2 == 0)
|
||||||
|
#set ($rowcolor = "#FFFFFF")
|
||||||
|
#else
|
||||||
|
#set ($rowcolor = "#EEEEEE")
|
||||||
|
#end
|
||||||
|
<tr bgcolor="$rowcolor">
|
||||||
|
<td>$form.imageSubmit("action", "claim", "images/claim.png", "Claim this task")</td>
|
||||||
|
<td>$task.summary
|
||||||
|
<span class="small">[$task.creator]</span></td>
|
||||||
|
<td valign="top">$task.category</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<center class="small">Click the wrench to claim a task.</center>
|
||||||
|
|
||||||
|
</td></tr>
|
||||||
|
|
||||||
|
<tr><td colspan="2"> </td></tr>
|
||||||
|
|
||||||
|
<tr><td><b>Recent completed tasks:</b></td>
|
||||||
|
<td><b>Create new task:</b></td></tr>
|
||||||
|
|
||||||
|
<tr><td valign="top">
|
||||||
|
<table cellpadding="2" cellspacing="0" border="0" width="100%">
|
||||||
|
#foreach ($task in $dtasks)
|
||||||
|
#if ($vidx%2 == 0)
|
||||||
|
#set ($rowcolor = "#EEEEEE")
|
||||||
|
#else
|
||||||
|
#set ($rowcolor = "#FFFFFF")
|
||||||
|
#end
|
||||||
|
<tr bgcolor="$rowcolor">
|
||||||
|
<td valign="top" align="center">$task.completion<br>[$task.completor]</td>
|
||||||
|
<td>$task.summary</td>
|
||||||
|
<td valign="top">$task.category</td></tr>
|
||||||
|
#end
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</td><td valign="top">
|
||||||
|
|
||||||
|
<form action="index.wm">
|
||||||
|
$form.fixedHidden("action", "create")
|
||||||
|
|
||||||
|
<table cellpadding="2" cellspacing="0" border="0">
|
||||||
|
<tr><td colspan="3" bgcolor="$scolor"> <br></td></tr>
|
||||||
|
|
||||||
|
<tr><td colspan="3">Summary:<br>
|
||||||
|
<textarea name="summary" rows="5" cols="50">
|
||||||
|
</textarea>
|
||||||
|
</td></tr>
|
||||||
|
|
||||||
|
<tr><td>Category:<br>
|
||||||
|
<font face="courier">
|
||||||
|
$form.text("category", "size='10'", "")
|
||||||
|
</font></td>
|
||||||
|
|
||||||
|
<td>Complexity:<br>
|
||||||
|
<font face="courier">
|
||||||
|
<select name="complexity">
|
||||||
|
$form.option("complexity", "1 - Simple hack", "Simple hack", "1 - Simple hack")
|
||||||
|
$form.option("complexity", "2 - Minor feature", "Minor feature", "1 - Simple hack")
|
||||||
|
$form.option("complexity", "3 - Major feature", "Major feature", "1 - Simple hack")
|
||||||
|
$form.option("complexity", "4 - Subsystem", "Subsystem", "1 - Simple hack")
|
||||||
|
$form.option("complexity", "5 - Major refactor", "Major refactor", "1 - Simple hack")
|
||||||
|
</select>
|
||||||
|
</font></td>
|
||||||
|
|
||||||
|
<td>Priority:<br>
|
||||||
|
<font face="courier">
|
||||||
|
<select name="priority">
|
||||||
|
$form.option("priority", "50", "Urgent", "15")
|
||||||
|
$form.option("priority", "25", "Next release", "15")
|
||||||
|
$form.option("priority", "15", "Soon", "15")
|
||||||
|
$form.option("priority", "10", "Before launch", "15")
|
||||||
|
$form.option("priority", "5", "Post launch", "15")
|
||||||
|
$form.option("priority", "1", "On the list", "15")
|
||||||
|
</select>
|
||||||
|
</font></td></tr>
|
||||||
|
|
||||||
|
<tr><td colspan="3" align="right" bgcolor="$scolor">
|
||||||
|
$form.submit("submit", "Create")</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
#import ("/footer.wm")
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 12pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td, center, div { /* ns 4 */
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 12pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: Courier;
|
||||||
|
font-size: 12pt
|
||||||
|
}
|
||||||
|
|
||||||
|
.big { font-size: 16pt }
|
||||||
|
.small { font-size: 10pt }
|
||||||
Reference in New Issue
Block a user