Home » Eclipse Projects » Mylyn » Trac Timing and Estimation Plugin mylyn integration
Trac Timing and Estimation Plugin mylyn integration [message #70191] |
Sat, 04 July 2009 23:16 ![Go to next message Go to next message](theme/Solstice/images/down.png) |
phil![Friend of Eclipse Friend](/donate/web-api/friends_decorator.php?email=trevo.palev%40gmail.com) Messages: 6 Registered: July 2009 |
Junior Member |
|
|
Yesterday I installed mylyn and was blown away. However, what I found to
be lacking was integration with the Trac Timing and Estimation Plugin so
that time tracking happens automatically when a task is de/activated.
Today I started working on a patch for the e_3_3_m_3_x branch and to my
surprise it works. I'm pretty sure my design is ugly as hell, but I don't
have time to dig in right now and do it properly. What I did:
* added a totalhours field and setters/getters to AbstractTask
* when the tooltips are generated in TaskListToolTip I just return the
time stored in AbstractTask if there's any and the standard
TaskActivityManager().getElapsedTime() otherwise
* in TaskActivityManager.de/activateTask() I call a chain of functions
ultimately leading to TracXmlRpcClient.de/activateTask() where a XML-RPC
request is sent to Trac
* in TaskActivityMonitor.stop() i deactivate the currently active task so
that Trac doesn't continue counting the time if somebody forgets to
deactivate the active task before quitting
It's not perfect but it works. The elapsed time stored in Trac is only
visible after a refresh and isn't stored locally. Here's the patch:
### Eclipse Workspace Patch 1.0
#P org.eclipse.mylyn.trac.core
Index:
src/org/eclipse/mylyn/internal/trac/core/client/AbstractTrac Client.java
============================================================ =======
RCS file:
/cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/client/AbstractTra cClient.java,v
retrieving revision 1.9
diff -u -r1.9 AbstractTracClient.java
---
src/org/eclipse/mylyn/internal/trac/core/client/AbstractTrac Client.java 22
Jun 2009 02:00:53 -0000 1.9
+++
src/org/eclipse/mylyn/internal/trac/core/client/AbstractTrac Client.java 4
Jul 2009 22:49:40 -0000
@@ -37,6 +37,7 @@
import org.eclipse.mylyn.internal.trac.core.model.TracTicketStatus;
import org.eclipse.mylyn.internal.trac.core.model.TracTicketType;
import org.eclipse.mylyn.internal.trac.core.model.TracVersion;
+import org.eclipse.mylyn.tasks.core.ITask;
/**
* @author Steffen Pingel
@@ -232,4 +233,9 @@
return null;
}
+ public void activateTask(ITask task) {
+ }
+
+ public void deactivateTask() {
+ }
}
Index:
src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcCl ient.java
============================================================ =======
RCS file:
/cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcC lient.java,v
retrieving revision 1.20
diff -u -r1.20 TracXmlRpcClient.java
---
src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcCl ient.java 22
Jun 2009 02:00:53 -0000 1.20
+++
src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcCl ient.java 4
Jul 2009 22:49:40 -0000
@@ -80,6 +80,7 @@
import org.eclipse.mylyn.internal.trac.core.util.TracUtil;
import org.eclipse.mylyn.internal.trac.core.util.TracXmlRpcClientRe quest;
import
org.eclipse.mylyn.internal.trac.core.util.TracHttpClientTran sportFactory.TracHttpException;
+import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.osgi.util.NLS;
/**
@@ -1125,10 +1126,10 @@
* An InputStream of the content of the attachment
* @param replace
* whether to overwrite an existing attachment with the same
filename
- * @return The (possibly transformed) filename of the attachment. If
<code>replace</code> is <code>true</code>, the
- * returned name is always the same as the argument
<code>fileName</code>; if <code>replace</code> is
- * <code>false</code> and an attachment with name
<code>fileName</code> already exists, a number is appended
- * to the file name (before suffix) and the generated filename
of the attachment is returned.
+ * @return The (possibly transformed) filename of the attachment. If
<code>replace</code> is <code>true</code>,
+ * the returned name is always the same as the argument
<code>fileName</code>; if <code>replace</code>
+ * is <code>false</code> and an attachment with name
<code>fileName</code> already exists, a number is
+ * appended to the file name (before suffix) and the generated
filename of the attachment is returned.
* @throws TracException
*/
public String putWikiPageAttachmentData(String pageName, String
fileName, String description, InputStream in,
@@ -1146,4 +1147,21 @@
call(monitor, "ticket.delete", ticketId); //$NON-NLS-1$
}
+ @Override
+ public void activateTask(ITask task) {
+ try {
+ call(null, "worklog.startWork", Integer.parseInt(task.getTaskId()));
+ } catch (TracException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void deactivateTask() {
+ try {
+ call(null, "worklog.stopWork", "", 0);
+ } catch (TracException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
\ No newline at end of file
Index: src/org/eclipse/mylyn/internal/trac/core/client/ITracClient. java
============================================================ =======
RCS file:
/cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/client/ITracClient .java,v
retrieving revision 1.10
diff -u -r1.10 ITracClient.java
--- src/org/eclipse/mylyn/internal/trac/core/client/ITracClient. java 20
Jun 2009 07:32:02 -0000 1.10
+++ src/org/eclipse/mylyn/internal/trac/core/client/ITracClient. java 4 Jul
2009 22:49:40 -0000
@@ -29,6 +29,7 @@
import org.eclipse.mylyn.internal.trac.core.model.TracTicketStatus;
import org.eclipse.mylyn.internal.trac.core.model.TracTicketType;
import org.eclipse.mylyn.internal.trac.core.model.TracVersion;
+import org.eclipse.mylyn.tasks.core.ITask;
/**
* Defines the requirements for classes that provide remote access to
Trac repositories.
@@ -209,4 +210,7 @@
void deleteTicket(int ticketId, IProgressMonitor monitor) throws
TracException;
+ public void activateTask(ITask task);
+
+ public void deactivateTask();
}
Index: src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandler .java
============================================================ =======
RCS file:
/cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandle r.java,v
retrieving revision 1.73
diff -u -r1.73 TracTaskDataHandler.java
--- src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandler .java 2
Jul 2009 06:52:44 -0000 1.73
+++ src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandler .java 4
Jul 2009 22:49:40 -0000
@@ -660,4 +660,31 @@
}
}
+ public void activateTask(TaskRepository repository, ITask task) {
+ if (repository == null) {
+ return;
+ }
+
+ ITracClient server =
connector.getClientManager().getTracClient(repository);
+
+ if (server == null) {
+ return;
+ }
+
+ server.activateTask(task);
+ }
+
+ public void deactivateTask(TaskRepository repository) {
+ if (repository == null) {
+ return;
+ }
+
+ ITracClient server =
connector.getClientManager().getTracClient(repository);
+
+ if (server == null) {
+ return;
+ }
+
+ server.deactivateTask();
+ }
}
Index: src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java
============================================================ =======
RCS file:
/cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.jav a,v
retrieving revision 1.25
diff -u -r1.25 TracCorePlugin.java
--- src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java 13 Jan
2009 07:05:20 -0000 1.25
+++ src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java 4 Jul
2009 22:49:39 -0000
@@ -106,8 +106,8 @@
return new RepositoryStatus(IStatus.ERROR, ID_PLUGIN,
RepositoryStatus.ERROR_IO,
Messages.TracCorePlugin_Repository_URL_is_invalid, e);
} else {
- return new RepositoryStatus(IStatus.ERROR, ID_PLUGIN,
RepositoryStatus.ERROR_INTERNAL, Messages.TracCorePlugin_Unexpected_error,
- e);
+ return new RepositoryStatus(IStatus.ERROR, ID_PLUGIN,
RepositoryStatus.ERROR_INTERNAL,
+ Messages.TracCorePlugin_Unexpected_error, e);
}
}
Index:
src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConne ctor.java
============================================================ =======
RCS file:
/cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConn ector.java,v
retrieving revision 1.106
diff -u -r1.106 TracRepositoryConnector.java
---
src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConne ctor.java 14
Jun 2009 23:38:26 -0000 1.106
+++
src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConne ctor.java 4
Jul 2009 22:49:39 -0000
@@ -31,6 +31,7 @@
import org.eclipse.mylyn.commons.net.AuthenticationCredentials;
import org.eclipse.mylyn.commons.net.AuthenticationType;
import org.eclipse.mylyn.commons.net.Policy;
+import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.trac.core.client.AbstractWikiHand ler;
import org.eclipse.mylyn.internal.trac.core.client.ITracClient;
import org.eclipse.mylyn.internal.trac.core.client.ITracWikiClient;
@@ -652,6 +653,18 @@
task.setCompletionDate(null);
}
}
+
+ if (task instanceof AbstractTask) {
+ Map<String, TaskAttribute> attributes =
taskData.getRoot().getAttributes();
+
+ if (attributes.get("totalhours") != null) {
+ AbstractTask atask = (AbstractTask) task;
+ float totalHours =
Float.parseFloat(attributes.get("totalhours").getValue());
+
+ atask.setTotalTimeHours(totalHours);
+ }
+ }
+
task.setUrl(taskRepository.getRepositoryUrl() + ITracClient.TICKET_URL
+ taskData.getTaskId());
if (!taskData.isPartial()) {
task.setAttribute(TASK_KEY_SUPPORTS_SUBTASKS,
Boolean.toString(taskDataHandler.supportsSubtasks(taskData)) );
@@ -696,4 +709,13 @@
return new TracTaskMapper(taskData, client);
}
+ @Override
+ public void activateTask(TaskRepository repository, ITask task) {
+ taskDataHandler.activateTask(repository, task);
+ }
+
+ @Override
+ public void deactivateTask(TaskRepository repository) {
+ taskDataHandler.deactivateTask(repository);
+ }
}
#P org.eclipse.mylyn.tasks.ui
Index: src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java
============================================================ =======
RCS file:
/cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.ui/ src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java,v
retrieving revision 1.69
diff -u -r1.69 TaskListToolTip.java
--- src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java 22
Jun 2009 21:19:28 -0000 1.69
+++ src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java 4
Jul 2009 22:49:42 -0000
@@ -183,15 +183,20 @@
for (ITask child : container.getChildren()) {
if (child instanceof AbstractTask) {
estimateTotal += ((AbstractTask) child).getEstimatedTimeHours();
- elapsedTotal +=
TasksUiPlugin.getTaskActivityManager().getElapsedTime(child,
- container.getDateRange());
+
+ if (((AbstractTask) child).getTotalTimeHours() > 0) {
+ elapsedTotal += (long) (((AbstractTask) child).getTotalTimeHours()
* 3600 * 1000);
+ } else {
+ elapsedTotal +=
TasksUiPlugin.getTaskActivityManager().getElapsedTime(child,
+ container.getDateRange());
+ }
}
}
StringBuilder sb = new StringBuilder();
sb.append(NLS.bind(Messages.TaskListToolTip_Estimate, estimateTotal));
- sb.append("\n"); //$NON-NLS-1$
+ sb.append("\n");
sb.append(NLS.bind(Messages.TaskListToolTip_Elapsed,
DateUtil.getFormattedDurationShort(elapsedTotal)));
- sb.append("\n"); //$NON-NLS-1$
+ sb.append("\n");
return sb.toString();
} else if (element instanceof ITask) {
ITask task = (ITask) element;
@@ -204,21 +209,21 @@
}
String key = task.getTaskKey();
if (key != null) {
- sb.append(" "); //$NON-NLS-1$
+ sb.append(" ");
sb.append(key);
}
String taskKind = task.getTaskKind();
if (taskKind != null && taskKind.length() > 0 &&
!taskKind.equals(kindLabel)) {
- sb.append(" ("); //$NON-NLS-1$
+ sb.append(" (");
sb.append(taskKind);
- sb.append(") "); //$NON-NLS-1$
+ sb.append(") ");
}
- sb.append(", "); //$NON-NLS-1$
+ sb.append(", ");
sb.append(task.getPriority());
- sb.append(" ["); //$NON-NLS-1$
+ sb.append(" [");
sb.append(getRepositoryLabel(task.getConnectorKind(),
task.getRepositoryUrl()));
- sb.append("]"); //$NON-NLS-1$
- sb.append("\n"); //$NON-NLS-1$
+ sb.append("]");
+ sb.append("\n");
return sb.toString();
} else {
return null;
@@ -281,8 +286,13 @@
}
long elapsed =
TasksUiPlugin.getTaskActivityManager().getElapsedTime(task);
+
+ if (task.getTotalTimeHours() > 0) {
+ elapsed = (int) (task.getTotalTimeHours() * 3600 * 1000);
+ }
+
sb.append(NLS.bind(Messages.TaskListToolTip_Elapsed,
DateUtil.getFormattedDurationShort(elapsed)));
- sb.append("\n"); //$NON-NLS-1$
+ sb.append("\n");
return sb.toString();
}
Index: src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java
============================================================ =======
RCS file:
/cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.ui/ src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java,v
retrieving revision 1.22
diff -u -r1.22 TaskActivityMonitor.java
--- src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java 1 May
2009 22:59:22 -0000 1.22
+++ src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java 4 Jul
2009 22:49:41 -0000
@@ -115,6 +115,7 @@
}
public void stop() {
+ taskActivityManager.deactivateActiveTask();
contextManager.removeActivityMetaContextListener(CONTEXT_LIS TENER);
}
#P org.eclipse.mylyn.tasks.core
Index: src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnector .java
============================================================ =======
RCS file:
/cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.cor e/src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnect or.java,v
retrieving revision 1.114
diff -u -r1.114 AbstractRepositoryConnector.java
--- src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnector .java 17
Dec 2008 00:34:32 -0000 1.114
+++ src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnector .java 4
Jul 2009 22:49:43 -0000
@@ -29,9 +29,7 @@
/**
* Encapsulates common operations that can be performed on a task
repository. Extend to connect with a Java API or WS
- * API for accessing the repository.
- *
- * Only methods that take a progress monitor can do network I/O.
+ * API for accessing the repository. Only methods that take a progress
monitor can do network I/O.
*
* @author Mik Kersten
* @author Rob Elves
@@ -215,9 +213,8 @@
}
/**
- * Runs <code>query</code> on <code>repository</code>, results are
passed to <code>collector</code>. If a repository
- * does not return the full task data for a result, {@link
TaskData#isPartial()} will return true.
- *
+ * Runs <code>query</code> on <code>repository</code>, results are
passed to <code>collector</code>. If a
+ * repository does not return the full task data for a result, {@link
TaskData#isPartial()} will return true.
* <p>
* Implementors must complete executing <code>query</code> before
returning from this method.
*
@@ -278,4 +275,9 @@
*/
public abstract void updateTaskFromTaskData(TaskRepository
taskRepository, ITask task, TaskData taskData);
+ public void activateTask(TaskRepository repository, ITask task) {
+ }
+
+ public void deactivateTask(TaskRepository repository) {
+ }
}
Index: src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManage r.java
============================================================ =======
RCS file:
/cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.cor e/src/org/eclipse/mylyn/internal/tasks/core/TaskActivityMana ger.java,v
retrieving revision 1.72
diff -u -r1.72 TaskActivityManager.java
--- src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManage r.java 11
Mar 2009 00:12:44 -0000 1.72
+++ src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManage r.java 4
Jul 2009 22:49:43 -0000
@@ -29,6 +29,7 @@
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.mylyn.commons.core.StatusHandler;
+import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITaskActivationListener;
import org.eclipse.mylyn.tasks.core.ITaskActivityListener;
@@ -103,7 +104,6 @@
* Set the first day of the week (Calendar.SUNDAY | Calendar.MONDAY)
*
* @see
http://en.wikipedia.org/wiki/Days_of_the_week#First_day_of_t he_week
- *
* @param startDay
* (Calendar.SUNDAY | Calendar.MONDAY)
*/
@@ -417,6 +417,12 @@
activeTask = task;
((AbstractTask) activeTask).setActive(true);
+ AbstractRepositoryConnector connector =
repositoryManager.getRepositoryConnector(task.getConnectorKi nd());
+
+ if (connector != null) {
+ connector.activateTask(repositoryManager.getRepository(task. getRepositoryUrl()),
task);
+ }
+
for (ITaskActivationListener listener : new
ArrayList<ITaskActivationListener>(activationListeners)) {
try {
listener.taskActivated(task);
@@ -452,6 +458,12 @@
((AbstractTask) activeTask).setActive(false);
activeTask = null;
+ AbstractRepositoryConnector connector =
repositoryManager.getRepositoryConnector(task.getConnectorKi nd());
+
+ if (connector != null) {
+ connector.deactivateTask(repositoryManager.getRepository(tas k.getRepositoryUrl()));
+ }
+
for (ITaskActivationListener listener : new
ArrayList<ITaskActivationListener>(activationListeners)) {
try {
listener.taskDeactivated(task);
Index: src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.java
============================================================ =======
RCS file:
/cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.cor e/src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.jav a,v
retrieving revision 1.34
diff -u -r1.34 AbstractTask.java
--- src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.java 8 May 2009
04:43:30 -0000 1.34
+++ src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.java 4 Jul 2009
22:49:43 -0000
@@ -84,6 +84,8 @@
private int estimatedTimeHours = 1;
+ private float totalTimeHours = -1;
+
private boolean markReadPending;
// TODO 4.0 make private
@@ -303,6 +305,14 @@
this.estimatedTimeHours = estimated;
}
+ public float getTotalTimeHours() {
+ return totalTimeHours;
+ }
+
+ public void setTotalTimeHours(float total) {
+ this.totalTimeHours = total;
+ }
+
void addParentContainer(AbstractTaskContainer container) {
containers.add(container);
}
Index: .refactorings/2009/7/27/refactorings.index
============================================================ =======
RCS file: .refactorings/2009/7/27/refactorings.index
diff -N .refactorings/2009/7/27/refactorings.index
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ .refactorings/2009/7/27/refactorings.index 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,1 @@
+1246743674234 Rename method 'acticateTask'
Index: .refactorings/2009/7/27/refactorings.history
============================================================ =======
RCS file: .refactorings/2009/7/27/refactorings.history
diff -N .refactorings/2009/7/27/refactorings.history
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ .refactorings/2009/7/27/refactorings.history 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<session version="1.0">
+<refactoring comment="Rename method
'org.eclipse.mylyn.tasks.core.AbstractRepositoryCon nector.acticateTask(...)'
to 'activateTask'
- Original project:
'org.eclipse.mylyn.tasks.core'
&a mp;#x0A;- Original element:
'org.eclipse.mylyn.tasks.core.AbstractRepositoryCon nector.acticateTask(...)'
-
Renamed element:
'org.eclipse.mylyn.tasks.core.AbstractRepositoryCon nector.activateTask(...)'
-
Update references to refactored element" delegate="false"
deprecate="false" description="Rename method 'acticateTask'"
flags="589830" id="org.eclipse.jdt.ui.rename.method"
input=" /src<org.eclipse.mylyn.tasks.core{AbstractRepositoryC onnector.java[AbstractRepositoryConnector~acticateTask~QITas k; "
name="activateTask" references="true" stamp="1246743674234" version="1.0"/>
+</session>
|
|
|
Re: Trac Timing and Estimation Plugin mylyn integration [message #70593 is a reply to message #70191] |
Tue, 14 July 2009 15:34 ![Go to previous message Go to previous message](theme/Solstice/images/up.png) ![Go to next message Go to next message](theme/Solstice/images/down.png) |
Eclipse User![Friend of Eclipse Friend](/donate/web-api/friends_decorator.php?email=) |
|
|
|
Originally posted by: seltmann.googlemail.com
Hello Phil,
Really nice, but before implementing this i would like to mention that
times are only counted for activated tasks when eclipse has the
window-focus. so every time you spend working on the activated issue
without using eclipse (i.e. answering mails, making calls, ...) is not
recognised as issue time.
ciao
Ulf
|
|
| |
Re: Trac Timing and Estimation Plugin mylyn integration [message #70745 is a reply to message #70191] |
Wed, 15 July 2009 07:13 ![Go to previous message Go to previous message](theme/Solstice/images/up.png) ![Go to next message Go to next message](theme/Solstice/images/down.png) |
Jörg Thönnes![Friend of Eclipse Friend](/donate/web-api/friends_decorator.php?email=Joerg.Thoennes%40macd.com) Messages: 229 Registered: July 2009 |
Senior Member |
|
|
Hi Phil,
great stuff! Could you post a easy deployable plugin/feature for Eclipse 3.5 + Mylyn 3.2?
Anyway, please file a bug and attach your patch!
Thanks, Jörg
On 07/05/09 01:16, phil wrote:
> Yesterday I installed mylyn and was blown away. However, what I found to
> be lacking was integration with the Trac Timing and Estimation Plugin so
> that time tracking happens automatically when a task is de/activated.
> Today I started working on a patch for the e_3_3_m_3_x branch and to my
> surprise it works. I'm pretty sure my design is ugly as hell, but I
> don't have time to dig in right now and do it properly. What I did:
>
> * added a totalhours field and setters/getters to AbstractTask
> * when the tooltips are generated in TaskListToolTip I just return the
> time stored in AbstractTask if there's any and the standard
> TaskActivityManager().getElapsedTime() otherwise
> * in TaskActivityManager.de/activateTask() I call a chain of functions
> ultimately leading to TracXmlRpcClient.de/activateTask() where a XML-RPC
> request is sent to Trac
> * in TaskActivityMonitor.stop() i deactivate the currently active task
> so that Trac doesn't continue counting the time if somebody forgets to
> deactivate the active task before quitting
>
> It's not perfect but it works. The elapsed time stored in Trac is only
> visible after a refresh and isn't stored locally. Here's the patch:
>
> ### Eclipse Workspace Patch 1.0
> #P org.eclipse.mylyn.trac.core
> Index:
> src/org/eclipse/mylyn/internal/trac/core/client/AbstractTrac Client.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/client/AbstractTra cClient.java,v
>
> retrieving revision 1.9
> diff -u -r1.9 AbstractTracClient.java
> ---
> src/org/eclipse/mylyn/internal/trac/core/client/AbstractTrac Client.java
> 22 Jun 2009 02:00:53 -0000 1.9
> +++
> src/org/eclipse/mylyn/internal/trac/core/client/AbstractTrac Client.java
> 4 Jul 2009 22:49:40 -0000
> @@ -37,6 +37,7 @@
> import org.eclipse.mylyn.internal.trac.core.model.TracTicketStatus;
> import org.eclipse.mylyn.internal.trac.core.model.TracTicketType;
> import org.eclipse.mylyn.internal.trac.core.model.TracVersion;
> +import org.eclipse.mylyn.tasks.core.ITask;
>
> /**
> * @author Steffen Pingel
> @@ -232,4 +233,9 @@
> return null;
> }
>
> + public void activateTask(ITask task) {
> + }
> +
> + public void deactivateTask() {
> + }
> }
> Index:
> src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcCl ient.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcC lient.java,v
>
> retrieving revision 1.20
> diff -u -r1.20 TracXmlRpcClient.java
> ---
> src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcCl ient.java
> 22 Jun 2009 02:00:53 -0000 1.20
> +++
> src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcCl ient.java
> 4 Jul 2009 22:49:40 -0000
> @@ -80,6 +80,7 @@
> import org.eclipse.mylyn.internal.trac.core.util.TracUtil;
> import org.eclipse.mylyn.internal.trac.core.util.TracXmlRpcClientRe quest;
> import
> org.eclipse.mylyn.internal.trac.core.util.TracHttpClientTran sportFactory.TracHttpException;
>
> +import org.eclipse.mylyn.tasks.core.ITask;
> import org.eclipse.osgi.util.NLS;
>
> /**
> @@ -1125,10 +1126,10 @@
> * An InputStream of the content of the attachment
> * @param replace
> * whether to overwrite an existing attachment with the
> same filename
> - * @return The (possibly transformed) filename of the attachment.
> If <code>replace</code> is <code>true</code>, the
> - * returned name is always the same as the argument
> <code>fileName</code>; if <code>replace</code> is
> - * <code>false</code> and an attachment with name
> <code>fileName</code> already exists, a number is appended
> - * to the file name (before suffix) and the generated
> filename of the attachment is returned.
> + * @return The (possibly transformed) filename of the attachment.
> If <code>replace</code> is <code>true</code>,
> + * the returned name is always the same as the argument
> <code>fileName</code>; if <code>replace</code>
> + * is <code>false</code> and an attachment with name
> <code>fileName</code> already exists, a number is
> + * appended to the file name (before suffix) and the
> generated filename of the attachment is returned.
> * @throws TracException
> */
> public String putWikiPageAttachmentData(String pageName, String
> fileName, String description, InputStream in,
> @@ -1146,4 +1147,21 @@
> call(monitor, "ticket.delete", ticketId); //$NON-NLS-1$
> }
>
> + @Override
> + public void activateTask(ITask task) {
> + try {
> + call(null, "worklog.startWork",
> Integer.parseInt(task.getTaskId()));
> + } catch (TracException e) {
> + throw new RuntimeException(e);
> + }
> + }
> +
> + @Override
> + public void deactivateTask() {
> + try {
> + call(null, "worklog.stopWork", "", 0);
> + } catch (TracException e) {
> + throw new RuntimeException(e);
> + }
> + }
> }
> \ No newline at end of file
> Index: src/org/eclipse/mylyn/internal/trac/core/client/ITracClient. java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/client/ITracClient .java,v
>
> retrieving revision 1.10
> diff -u -r1.10 ITracClient.java
> --- src/org/eclipse/mylyn/internal/trac/core/client/ITracClient. java
> 20 Jun 2009 07:32:02 -0000 1.10
> +++ src/org/eclipse/mylyn/internal/trac/core/client/ITracClient. java
> 4 Jul 2009 22:49:40 -0000
> @@ -29,6 +29,7 @@
> import org.eclipse.mylyn.internal.trac.core.model.TracTicketStatus;
> import org.eclipse.mylyn.internal.trac.core.model.TracTicketType;
> import org.eclipse.mylyn.internal.trac.core.model.TracVersion;
> +import org.eclipse.mylyn.tasks.core.ITask;
>
> /**
> * Defines the requirements for classes that provide remote access to
> Trac repositories.
> @@ -209,4 +210,7 @@
>
> void deleteTicket(int ticketId, IProgressMonitor monitor) throws
> TracException;
>
> + public void activateTask(ITask task);
> +
> + public void deactivateTask();
> }
> Index: src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandler .java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandle r.java,v
>
> retrieving revision 1.73
> diff -u -r1.73 TracTaskDataHandler.java
> --- src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandler .java
> 2 Jul 2009 06:52:44 -0000 1.73
> +++ src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandler .java
> 4 Jul 2009 22:49:40 -0000
> @@ -660,4 +660,31 @@
> }
> }
>
> + public void activateTask(TaskRepository repository, ITask task) {
> + if (repository == null) {
> + return;
> + }
> +
> + ITracClient server =
> connector.getClientManager().getTracClient(repository);
> +
> + if (server == null) {
> + return;
> + }
> +
> + server.activateTask(task);
> + }
> +
> + public void deactivateTask(TaskRepository repository) {
> + if (repository == null) {
> + return;
> + }
> +
> + ITracClient server =
> connector.getClientManager().getTracClient(repository);
> +
> + if (server == null) {
> + return;
> + }
> +
> + server.deactivateTask();
> + }
> }
> Index: src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.jav a,v
>
> retrieving revision 1.25
> diff -u -r1.25 TracCorePlugin.java
> --- src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java 13
> Jan 2009 07:05:20 -0000 1.25
> +++ src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java 4
> Jul 2009 22:49:39 -0000
> @@ -106,8 +106,8 @@
> return new RepositoryStatus(IStatus.ERROR, ID_PLUGIN,
> RepositoryStatus.ERROR_IO,
> Messages.TracCorePlugin_Repository_URL_is_invalid, e);
> } else {
> - return new RepositoryStatus(IStatus.ERROR, ID_PLUGIN,
> RepositoryStatus.ERROR_INTERNAL, Messages.TracCorePlugin_Unexpected_error,
> - e);
> + return new RepositoryStatus(IStatus.ERROR, ID_PLUGIN,
> RepositoryStatus.ERROR_INTERNAL,
> + Messages.TracCorePlugin_Unexpected_error, e);
> }
> }
>
> Index:
> src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConne ctor.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConn ector.java,v
>
> retrieving revision 1.106
> diff -u -r1.106 TracRepositoryConnector.java
> ---
> src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConne ctor.java
> 14 Jun 2009 23:38:26 -0000 1.106
> +++
> src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConne ctor.java
> 4 Jul 2009 22:49:39 -0000
> @@ -31,6 +31,7 @@
> import org.eclipse.mylyn.commons.net.AuthenticationCredentials;
> import org.eclipse.mylyn.commons.net.AuthenticationType;
> import org.eclipse.mylyn.commons.net.Policy;
> +import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
> import org.eclipse.mylyn.internal.trac.core.client.AbstractWikiHand ler;
> import org.eclipse.mylyn.internal.trac.core.client.ITracClient;
> import org.eclipse.mylyn.internal.trac.core.client.ITracWikiClient;
> @@ -652,6 +653,18 @@
> task.setCompletionDate(null);
> }
> }
> +
> + if (task instanceof AbstractTask) {
> + Map<String, TaskAttribute> attributes =
> taskData.getRoot().getAttributes();
> +
> + if (attributes.get("totalhours") != null) {
> + AbstractTask atask = (AbstractTask) task;
> + float totalHours =
> Float.parseFloat(attributes.get("totalhours").getValue());
> +
> + atask.setTotalTimeHours(totalHours);
> + }
> + }
> +
> task.setUrl(taskRepository.getRepositoryUrl() +
> ITracClient.TICKET_URL + taskData.getTaskId());
> if (!taskData.isPartial()) {
> task.setAttribute(TASK_KEY_SUPPORTS_SUBTASKS,
> Boolean.toString(taskDataHandler.supportsSubtasks(taskData)) );
> @@ -696,4 +709,13 @@
> return new TracTaskMapper(taskData, client);
> }
>
> + @Override
> + public void activateTask(TaskRepository repository, ITask task) {
> + taskDataHandler.activateTask(repository, task);
> + }
> +
> + @Override
> + public void deactivateTask(TaskRepository repository) {
> + taskDataHandler.deactivateTask(repository);
> + }
> }
> #P org.eclipse.mylyn.tasks.ui
> Index: src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.ui/ src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java,v
>
> retrieving revision 1.69
> diff -u -r1.69 TaskListToolTip.java
> ---
> src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java 22
> Jun 2009 21:19:28 -0000 1.69
> +++
> src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java 4
> Jul 2009 22:49:42 -0000
> @@ -183,15 +183,20 @@
> for (ITask child : container.getChildren()) {
> if (child instanceof AbstractTask) {
> estimateTotal += ((AbstractTask)
> child).getEstimatedTimeHours();
> - elapsedTotal +=
> TasksUiPlugin.getTaskActivityManager().getElapsedTime(child,
> - container.getDateRange());
> +
> + if (((AbstractTask) child).getTotalTimeHours() > 0) {
> + elapsedTotal += (long) (((AbstractTask)
> child).getTotalTimeHours() * 3600 * 1000);
> + } else {
> + elapsedTotal +=
> TasksUiPlugin.getTaskActivityManager().getElapsedTime(child,
> + container.getDateRange());
> + }
> }
> }
> StringBuilder sb = new StringBuilder();
> sb.append(NLS.bind(Messages.TaskListToolTip_Estimate,
> estimateTotal));
> - sb.append("\n"); //$NON-NLS-1$
> + sb.append("\n");
> sb.append(NLS.bind(Messages.TaskListToolTip_Elapsed,
> DateUtil.getFormattedDurationShort(elapsedTotal)));
> - sb.append("\n"); //$NON-NLS-1$
> + sb.append("\n");
> return sb.toString();
> } else if (element instanceof ITask) {
> ITask task = (ITask) element;
> @@ -204,21 +209,21 @@
> }
> String key = task.getTaskKey();
> if (key != null) {
> - sb.append(" "); //$NON-NLS-1$
> + sb.append(" ");
> sb.append(key);
> }
> String taskKind = task.getTaskKind();
> if (taskKind != null && taskKind.length() > 0 &&
> !taskKind.equals(kindLabel)) {
> - sb.append(" ("); //$NON-NLS-1$
> + sb.append(" (");
> sb.append(taskKind);
> - sb.append(") "); //$NON-NLS-1$
> + sb.append(") ");
> }
> - sb.append(", "); //$NON-NLS-1$
> + sb.append(", ");
> sb.append(task.getPriority());
> - sb.append(" ["); //$NON-NLS-1$
> + sb.append(" [");
> sb.append(getRepositoryLabel(task.getConnectorKind(),
> task.getRepositoryUrl()));
> - sb.append("]"); //$NON-NLS-1$
> - sb.append("\n"); //$NON-NLS-1$
> + sb.append("]");
> + sb.append("\n");
> return sb.toString();
> } else {
> return null;
> @@ -281,8 +286,13 @@
> }
>
> long elapsed =
> TasksUiPlugin.getTaskActivityManager().getElapsedTime(task);
> +
> + if (task.getTotalTimeHours() > 0) {
> + elapsed = (int) (task.getTotalTimeHours() * 3600 * 1000);
> + }
> +
> sb.append(NLS.bind(Messages.TaskListToolTip_Elapsed,
> DateUtil.getFormattedDurationShort(elapsed)));
> - sb.append("\n"); //$NON-NLS-1$
> + sb.append("\n");
>
> return sb.toString();
> }
> Index: src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.ui/ src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java,v
>
> retrieving revision 1.22
> diff -u -r1.22 TaskActivityMonitor.java
> --- src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java
> 1 May 2009 22:59:22 -0000 1.22
> +++ src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java
> 4 Jul 2009 22:49:41 -0000
> @@ -115,6 +115,7 @@
> }
>
> public void stop() {
> + taskActivityManager.deactivateActiveTask();
> contextManager.removeActivityMetaContextListener(CONTEXT_LIS TENER);
> }
>
> #P org.eclipse.mylyn.tasks.core
> Index: src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnector .java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.cor e/src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnect or.java,v
>
> retrieving revision 1.114
> diff -u -r1.114 AbstractRepositoryConnector.java
> --- src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnector .java
> 17 Dec 2008 00:34:32 -0000 1.114
> +++ src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnector .java
> 4 Jul 2009 22:49:43 -0000
> @@ -29,9 +29,7 @@
>
> /**
> * Encapsulates common operations that can be performed on a task
> repository. Extend to connect with a Java API or WS
> - * API for accessing the repository.
> - * - * Only methods that take a progress monitor can do network I/O.
> + * API for accessing the repository. Only methods that take a progress
> monitor can do network I/O.
> * * @author Mik Kersten
> * @author Rob Elves
> @@ -215,9 +213,8 @@
> }
>
> /**
> - * Runs <code>query</code> on <code>repository</code>, results are
> passed to <code>collector</code>. If a repository
> - * does not return the full task data for a result, {@link
> TaskData#isPartial()} will return true.
> - * + * Runs <code>query</code> on <code>repository</code>,
> results are passed to <code>collector</code>. If a
> + * repository does not return the full task data for a result,
> {@link TaskData#isPartial()} will return true.
> * <p>
> * Implementors must complete executing <code>query</code> before
> returning from this method.
> * @@ -278,4 +275,9 @@
> */
> public abstract void updateTaskFromTaskData(TaskRepository
> taskRepository, ITask task, TaskData taskData);
>
> + public void activateTask(TaskRepository repository, ITask task) {
> + }
> +
> + public void deactivateTask(TaskRepository repository) {
> + }
> }
> Index: src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManage r.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.cor e/src/org/eclipse/mylyn/internal/tasks/core/TaskActivityMana ger.java,v
>
> retrieving revision 1.72
> diff -u -r1.72 TaskActivityManager.java
> ---
> src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManage r.java 11
> Mar 2009 00:12:44 -0000 1.72
> +++
> src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManage r.java 4
> Jul 2009 22:49:43 -0000
> @@ -29,6 +29,7 @@
> import org.eclipse.core.runtime.IStatus;
> import org.eclipse.core.runtime.Status;
> import org.eclipse.mylyn.commons.core.StatusHandler;
> +import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
> import org.eclipse.mylyn.tasks.core.ITask;
> import org.eclipse.mylyn.tasks.core.ITaskActivationListener;
> import org.eclipse.mylyn.tasks.core.ITaskActivityListener;
> @@ -103,7 +104,6 @@
> * Set the first day of the week (Calendar.SUNDAY | Calendar.MONDAY)
> * * @see
> http://en.wikipedia.org/wiki/Days_of_the_week#First_day_of_t he_week
> - * * @param startDay
> * (Calendar.SUNDAY | Calendar.MONDAY)
> */
> @@ -417,6 +417,12 @@
> activeTask = task;
> ((AbstractTask) activeTask).setActive(true);
>
> + AbstractRepositoryConnector connector =
> repositoryManager.getRepositoryConnector(task.getConnectorKi nd());
> +
> + if (connector != null) {
> +
> connector.activateTask(repositoryManager.getRepository(task. getRepositoryUrl()),
> task);
> + }
> +
> for (ITaskActivationListener listener : new
> ArrayList<ITaskActivationListener>(activationListeners)) {
> try {
> listener.taskActivated(task);
> @@ -452,6 +458,12 @@
> ((AbstractTask) activeTask).setActive(false);
> activeTask = null;
>
> + AbstractRepositoryConnector connector =
> repositoryManager.getRepositoryConnector(task.getConnectorKi nd());
> +
> + if (connector != null) {
> +
> connector.deactivateTask(repositoryManager.getRepository(tas k.getRepositoryUrl()));
>
> + }
> +
> for (ITaskActivationListener listener : new
> ArrayList<ITaskActivationListener>(activationListeners)) {
> try {
> listener.taskDeactivated(task);
> Index: src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.cor e/src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.jav a,v
>
> retrieving revision 1.34
> diff -u -r1.34 AbstractTask.java
> --- src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.java 8 May
> 2009 04:43:30 -0000 1.34
> +++ src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.java 4 Jul
> 2009 22:49:43 -0000
> @@ -84,6 +84,8 @@
>
> private int estimatedTimeHours = 1;
>
> + private float totalTimeHours = -1;
> +
> private boolean markReadPending;
>
> // TODO 4.0 make private
> @@ -303,6 +305,14 @@
> this.estimatedTimeHours = estimated;
> }
>
> + public float getTotalTimeHours() {
> + return totalTimeHours;
> + }
> +
> + public void setTotalTimeHours(float total) {
> + this.totalTimeHours = total;
> + }
> +
> void addParentContainer(AbstractTaskContainer container) {
> containers.add(container);
> }
> Index: .refactorings/2009/7/27/refactorings.index
> ============================================================ =======
> RCS file: .refactorings/2009/7/27/refactorings.index
> diff -N .refactorings/2009/7/27/refactorings.index
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ .refactorings/2009/7/27/refactorings.index 1 Jan 1970 00:00:00 -0000
> @@ -0,0 +1,1 @@
> +1246743674234 Rename method 'acticateTask'
> Index: .refactorings/2009/7/27/refactorings.history
> ============================================================ =======
> RCS file: .refactorings/2009/7/27/refactorings.history
> diff -N .refactorings/2009/7/27/refactorings.history
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ .refactorings/2009/7/27/refactorings.history 1 Jan 1970 00:00:00
> -0000
> @@ -0,0 +1,4 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<session version="1.0">
> +<refactoring comment="Rename method
> 'org.eclipse.mylyn.tasks.core.AbstractRepositoryCon nector.acticateTask(...)'
> to 'activateTask'
- Original project:
> 'org.eclipse.mylyn.tasks.core'
&a mp;#x0A;- Original element:
> 'org.eclipse.mylyn.tasks.core.AbstractRepositoryCon nector.acticateTask(...)'
-
> Renamed element:
> 'org.eclipse.mylyn.tasks.core.AbstractRepositoryCon nector.activateTask(...)'
-
> Update references to refactored element" delegate="false"
> deprecate="false" description="Rename method 'acticateTask'"
> flags="589830" id="org.eclipse.jdt.ui.rename.method"
> input=" /src<org.eclipse.mylyn.tasks.core{AbstractRepositoryC onnector.java[AbstractRepositoryConnector~acticateTask~QITas k; "
> name="activateTask" references="true" stamp="1246743674234" version="1.0"/>
> +</session>
>
>
|
|
| | | | | | |
Re: Trac Timing and Estimation Plugin mylyn integration [message #598168 is a reply to message #70191] |
Wed, 15 July 2009 07:13 ![Go to previous message Go to previous message](theme/Solstice/images/up.png) ![Go to next message Go to next message](theme/Solstice/images/down.png) |
Jörg Thönnes![Friend of Eclipse Friend](/donate/web-api/friends_decorator.php?email=Joerg.Thoennes%40macd.com) Messages: 229 Registered: July 2009 |
Senior Member |
|
|
Hi Phil,
great stuff! Could you post a easy deployable plugin/feature for Eclipse 3.5 + Mylyn 3.2?
Anyway, please file a bug and attach your patch!
Thanks, Jörg
On 07/05/09 01:16, phil wrote:
> Yesterday I installed mylyn and was blown away. However, what I found to
> be lacking was integration with the Trac Timing and Estimation Plugin so
> that time tracking happens automatically when a task is de/activated.
> Today I started working on a patch for the e_3_3_m_3_x branch and to my
> surprise it works. I'm pretty sure my design is ugly as hell, but I
> don't have time to dig in right now and do it properly. What I did:
>
> * added a totalhours field and setters/getters to AbstractTask
> * when the tooltips are generated in TaskListToolTip I just return the
> time stored in AbstractTask if there's any and the standard
> TaskActivityManager().getElapsedTime() otherwise
> * in TaskActivityManager.de/activateTask() I call a chain of functions
> ultimately leading to TracXmlRpcClient.de/activateTask() where a XML-RPC
> request is sent to Trac
> * in TaskActivityMonitor.stop() i deactivate the currently active task
> so that Trac doesn't continue counting the time if somebody forgets to
> deactivate the active task before quitting
>
> It's not perfect but it works. The elapsed time stored in Trac is only
> visible after a refresh and isn't stored locally. Here's the patch:
>
> ### Eclipse Workspace Patch 1.0
> #P org.eclipse.mylyn.trac.core
> Index:
> src/org/eclipse/mylyn/internal/trac/core/client/AbstractTrac Client.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/client/AbstractTra cClient.java,v
>
> retrieving revision 1.9
> diff -u -r1.9 AbstractTracClient.java
> ---
> src/org/eclipse/mylyn/internal/trac/core/client/AbstractTrac Client.java
> 22 Jun 2009 02:00:53 -0000 1.9
> +++
> src/org/eclipse/mylyn/internal/trac/core/client/AbstractTrac Client.java
> 4 Jul 2009 22:49:40 -0000
> @@ -37,6 +37,7 @@
> import org.eclipse.mylyn.internal.trac.core.model.TracTicketStatus;
> import org.eclipse.mylyn.internal.trac.core.model.TracTicketType;
> import org.eclipse.mylyn.internal.trac.core.model.TracVersion;
> +import org.eclipse.mylyn.tasks.core.ITask;
>
> /**
> * @author Steffen Pingel
> @@ -232,4 +233,9 @@
> return null;
> }
>
> + public void activateTask(ITask task) {
> + }
> +
> + public void deactivateTask() {
> + }
> }
> Index:
> src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcCl ient.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcC lient.java,v
>
> retrieving revision 1.20
> diff -u -r1.20 TracXmlRpcClient.java
> ---
> src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcCl ient.java
> 22 Jun 2009 02:00:53 -0000 1.20
> +++
> src/org/eclipse/mylyn/internal/trac/core/client/TracXmlRpcCl ient.java
> 4 Jul 2009 22:49:40 -0000
> @@ -80,6 +80,7 @@
> import org.eclipse.mylyn.internal.trac.core.util.TracUtil;
> import org.eclipse.mylyn.internal.trac.core.util.TracXmlRpcClientRe quest;
> import
> org.eclipse.mylyn.internal.trac.core.util.TracHttpClientTran sportFactory.TracHttpException;
>
> +import org.eclipse.mylyn.tasks.core.ITask;
> import org.eclipse.osgi.util.NLS;
>
> /**
> @@ -1125,10 +1126,10 @@
> * An InputStream of the content of the attachment
> * @param replace
> * whether to overwrite an existing attachment with the
> same filename
> - * @return The (possibly transformed) filename of the attachment.
> If <code>replace</code> is <code>true</code>, the
> - * returned name is always the same as the argument
> <code>fileName</code>; if <code>replace</code> is
> - * <code>false</code> and an attachment with name
> <code>fileName</code> already exists, a number is appended
> - * to the file name (before suffix) and the generated
> filename of the attachment is returned.
> + * @return The (possibly transformed) filename of the attachment.
> If <code>replace</code> is <code>true</code>,
> + * the returned name is always the same as the argument
> <code>fileName</code>; if <code>replace</code>
> + * is <code>false</code> and an attachment with name
> <code>fileName</code> already exists, a number is
> + * appended to the file name (before suffix) and the
> generated filename of the attachment is returned.
> * @throws TracException
> */
> public String putWikiPageAttachmentData(String pageName, String
> fileName, String description, InputStream in,
> @@ -1146,4 +1147,21 @@
> call(monitor, "ticket.delete", ticketId); //$NON-NLS-1$
> }
>
> + @Override
> + public void activateTask(ITask task) {
> + try {
> + call(null, "worklog.startWork",
> Integer.parseInt(task.getTaskId()));
> + } catch (TracException e) {
> + throw new RuntimeException(e);
> + }
> + }
> +
> + @Override
> + public void deactivateTask() {
> + try {
> + call(null, "worklog.stopWork", "", 0);
> + } catch (TracException e) {
> + throw new RuntimeException(e);
> + }
> + }
> }
> \ No newline at end of file
> Index: src/org/eclipse/mylyn/internal/trac/core/client/ITracClient. java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/client/ITracClient .java,v
>
> retrieving revision 1.10
> diff -u -r1.10 ITracClient.java
> --- src/org/eclipse/mylyn/internal/trac/core/client/ITracClient. java
> 20 Jun 2009 07:32:02 -0000 1.10
> +++ src/org/eclipse/mylyn/internal/trac/core/client/ITracClient. java
> 4 Jul 2009 22:49:40 -0000
> @@ -29,6 +29,7 @@
> import org.eclipse.mylyn.internal.trac.core.model.TracTicketStatus;
> import org.eclipse.mylyn.internal.trac.core.model.TracTicketType;
> import org.eclipse.mylyn.internal.trac.core.model.TracVersion;
> +import org.eclipse.mylyn.tasks.core.ITask;
>
> /**
> * Defines the requirements for classes that provide remote access to
> Trac repositories.
> @@ -209,4 +210,7 @@
>
> void deleteTicket(int ticketId, IProgressMonitor monitor) throws
> TracException;
>
> + public void activateTask(ITask task);
> +
> + public void deactivateTask();
> }
> Index: src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandler .java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandle r.java,v
>
> retrieving revision 1.73
> diff -u -r1.73 TracTaskDataHandler.java
> --- src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandler .java
> 2 Jul 2009 06:52:44 -0000 1.73
> +++ src/org/eclipse/mylyn/internal/trac/core/TracTaskDataHandler .java
> 4 Jul 2009 22:49:40 -0000
> @@ -660,4 +660,31 @@
> }
> }
>
> + public void activateTask(TaskRepository repository, ITask task) {
> + if (repository == null) {
> + return;
> + }
> +
> + ITracClient server =
> connector.getClientManager().getTracClient(repository);
> +
> + if (server == null) {
> + return;
> + }
> +
> + server.activateTask(task);
> + }
> +
> + public void deactivateTask(TaskRepository repository) {
> + if (repository == null) {
> + return;
> + }
> +
> + ITracClient server =
> connector.getClientManager().getTracClient(repository);
> +
> + if (server == null) {
> + return;
> + }
> +
> + server.deactivateTask();
> + }
> }
> Index: src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.jav a,v
>
> retrieving revision 1.25
> diff -u -r1.25 TracCorePlugin.java
> --- src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java 13
> Jan 2009 07:05:20 -0000 1.25
> +++ src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java 4
> Jul 2009 22:49:39 -0000
> @@ -106,8 +106,8 @@
> return new RepositoryStatus(IStatus.ERROR, ID_PLUGIN,
> RepositoryStatus.ERROR_IO,
> Messages.TracCorePlugin_Repository_URL_is_invalid, e);
> } else {
> - return new RepositoryStatus(IStatus.ERROR, ID_PLUGIN,
> RepositoryStatus.ERROR_INTERNAL, Messages.TracCorePlugin_Unexpected_error,
> - e);
> + return new RepositoryStatus(IStatus.ERROR, ID_PLUGIN,
> RepositoryStatus.ERROR_INTERNAL,
> + Messages.TracCorePlugin_Unexpected_error, e);
> }
> }
>
> Index:
> src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConne ctor.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.trac.core /src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConn ector.java,v
>
> retrieving revision 1.106
> diff -u -r1.106 TracRepositoryConnector.java
> ---
> src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConne ctor.java
> 14 Jun 2009 23:38:26 -0000 1.106
> +++
> src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConne ctor.java
> 4 Jul 2009 22:49:39 -0000
> @@ -31,6 +31,7 @@
> import org.eclipse.mylyn.commons.net.AuthenticationCredentials;
> import org.eclipse.mylyn.commons.net.AuthenticationType;
> import org.eclipse.mylyn.commons.net.Policy;
> +import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
> import org.eclipse.mylyn.internal.trac.core.client.AbstractWikiHand ler;
> import org.eclipse.mylyn.internal.trac.core.client.ITracClient;
> import org.eclipse.mylyn.internal.trac.core.client.ITracWikiClient;
> @@ -652,6 +653,18 @@
> task.setCompletionDate(null);
> }
> }
> +
> + if (task instanceof AbstractTask) {
> + Map<String, TaskAttribute> attributes =
> taskData.getRoot().getAttributes();
> +
> + if (attributes.get("totalhours") != null) {
> + AbstractTask atask = (AbstractTask) task;
> + float totalHours =
> Float.parseFloat(attributes.get("totalhours").getValue());
> +
> + atask.setTotalTimeHours(totalHours);
> + }
> + }
> +
> task.setUrl(taskRepository.getRepositoryUrl() +
> ITracClient.TICKET_URL + taskData.getTaskId());
> if (!taskData.isPartial()) {
> task.setAttribute(TASK_KEY_SUPPORTS_SUBTASKS,
> Boolean.toString(taskDataHandler.supportsSubtasks(taskData)) );
> @@ -696,4 +709,13 @@
> return new TracTaskMapper(taskData, client);
> }
>
> + @Override
> + public void activateTask(TaskRepository repository, ITask task) {
> + taskDataHandler.activateTask(repository, task);
> + }
> +
> + @Override
> + public void deactivateTask(TaskRepository repository) {
> + taskDataHandler.deactivateTask(repository);
> + }
> }
> #P org.eclipse.mylyn.tasks.ui
> Index: src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.ui/ src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java,v
>
> retrieving revision 1.69
> diff -u -r1.69 TaskListToolTip.java
> ---
> src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java 22
> Jun 2009 21:19:28 -0000 1.69
> +++
> src/org/eclipse/mylyn/internal/tasks/ui/views/TaskListToolTi p.java 4
> Jul 2009 22:49:42 -0000
> @@ -183,15 +183,20 @@
> for (ITask child : container.getChildren()) {
> if (child instanceof AbstractTask) {
> estimateTotal += ((AbstractTask)
> child).getEstimatedTimeHours();
> - elapsedTotal +=
> TasksUiPlugin.getTaskActivityManager().getElapsedTime(child,
> - container.getDateRange());
> +
> + if (((AbstractTask) child).getTotalTimeHours() > 0) {
> + elapsedTotal += (long) (((AbstractTask)
> child).getTotalTimeHours() * 3600 * 1000);
> + } else {
> + elapsedTotal +=
> TasksUiPlugin.getTaskActivityManager().getElapsedTime(child,
> + container.getDateRange());
> + }
> }
> }
> StringBuilder sb = new StringBuilder();
> sb.append(NLS.bind(Messages.TaskListToolTip_Estimate,
> estimateTotal));
> - sb.append("\n"); //$NON-NLS-1$
> + sb.append("\n");
> sb.append(NLS.bind(Messages.TaskListToolTip_Elapsed,
> DateUtil.getFormattedDurationShort(elapsedTotal)));
> - sb.append("\n"); //$NON-NLS-1$
> + sb.append("\n");
> return sb.toString();
> } else if (element instanceof ITask) {
> ITask task = (ITask) element;
> @@ -204,21 +209,21 @@
> }
> String key = task.getTaskKey();
> if (key != null) {
> - sb.append(" "); //$NON-NLS-1$
> + sb.append(" ");
> sb.append(key);
> }
> String taskKind = task.getTaskKind();
> if (taskKind != null && taskKind.length() > 0 &&
> !taskKind.equals(kindLabel)) {
> - sb.append(" ("); //$NON-NLS-1$
> + sb.append(" (");
> sb.append(taskKind);
> - sb.append(") "); //$NON-NLS-1$
> + sb.append(") ");
> }
> - sb.append(", "); //$NON-NLS-1$
> + sb.append(", ");
> sb.append(task.getPriority());
> - sb.append(" ["); //$NON-NLS-1$
> + sb.append(" [");
> sb.append(getRepositoryLabel(task.getConnectorKind(),
> task.getRepositoryUrl()));
> - sb.append("]"); //$NON-NLS-1$
> - sb.append("\n"); //$NON-NLS-1$
> + sb.append("]");
> + sb.append("\n");
> return sb.toString();
> } else {
> return null;
> @@ -281,8 +286,13 @@
> }
>
> long elapsed =
> TasksUiPlugin.getTaskActivityManager().getElapsedTime(task);
> +
> + if (task.getTotalTimeHours() > 0) {
> + elapsed = (int) (task.getTotalTimeHours() * 3600 * 1000);
> + }
> +
> sb.append(NLS.bind(Messages.TaskListToolTip_Elapsed,
> DateUtil.getFormattedDurationShort(elapsed)));
> - sb.append("\n"); //$NON-NLS-1$
> + sb.append("\n");
>
> return sb.toString();
> }
> Index: src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.ui/ src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java,v
>
> retrieving revision 1.22
> diff -u -r1.22 TaskActivityMonitor.java
> --- src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java
> 1 May 2009 22:59:22 -0000 1.22
> +++ src/org/eclipse/mylyn/internal/tasks/ui/TaskActivityMonitor. java
> 4 Jul 2009 22:49:41 -0000
> @@ -115,6 +115,7 @@
> }
>
> public void stop() {
> + taskActivityManager.deactivateActiveTask();
> contextManager.removeActivityMetaContextListener(CONTEXT_LIS TENER);
> }
>
> #P org.eclipse.mylyn.tasks.core
> Index: src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnector .java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.cor e/src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnect or.java,v
>
> retrieving revision 1.114
> diff -u -r1.114 AbstractRepositoryConnector.java
> --- src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnector .java
> 17 Dec 2008 00:34:32 -0000 1.114
> +++ src/org/eclipse/mylyn/tasks/core/AbstractRepositoryConnector .java
> 4 Jul 2009 22:49:43 -0000
> @@ -29,9 +29,7 @@
>
> /**
> * Encapsulates common operations that can be performed on a task
> repository. Extend to connect with a Java API or WS
> - * API for accessing the repository.
> - * - * Only methods that take a progress monitor can do network I/O.
> + * API for accessing the repository. Only methods that take a progress
> monitor can do network I/O.
> * * @author Mik Kersten
> * @author Rob Elves
> @@ -215,9 +213,8 @@
> }
>
> /**
> - * Runs <code>query</code> on <code>repository</code>, results are
> passed to <code>collector</code>. If a repository
> - * does not return the full task data for a result, {@link
> TaskData#isPartial()} will return true.
> - * + * Runs <code>query</code> on <code>repository</code>,
> results are passed to <code>collector</code>. If a
> + * repository does not return the full task data for a result,
> {@link TaskData#isPartial()} will return true.
> * <p>
> * Implementors must complete executing <code>query</code> before
> returning from this method.
> * @@ -278,4 +275,9 @@
> */
> public abstract void updateTaskFromTaskData(TaskRepository
> taskRepository, ITask task, TaskData taskData);
>
> + public void activateTask(TaskRepository repository, ITask task) {
> + }
> +
> + public void deactivateTask(TaskRepository repository) {
> + }
> }
> Index: src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManage r.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.cor e/src/org/eclipse/mylyn/internal/tasks/core/TaskActivityMana ger.java,v
>
> retrieving revision 1.72
> diff -u -r1.72 TaskActivityManager.java
> ---
> src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManage r.java 11
> Mar 2009 00:12:44 -0000 1.72
> +++
> src/org/eclipse/mylyn/internal/tasks/core/TaskActivityManage r.java 4
> Jul 2009 22:49:43 -0000
> @@ -29,6 +29,7 @@
> import org.eclipse.core.runtime.IStatus;
> import org.eclipse.core.runtime.Status;
> import org.eclipse.mylyn.commons.core.StatusHandler;
> +import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
> import org.eclipse.mylyn.tasks.core.ITask;
> import org.eclipse.mylyn.tasks.core.ITaskActivationListener;
> import org.eclipse.mylyn.tasks.core.ITaskActivityListener;
> @@ -103,7 +104,6 @@
> * Set the first day of the week (Calendar.SUNDAY | Calendar.MONDAY)
> * * @see
> http://en.wikipedia.org/wiki/Days_of_the_week#First_day_of_t he_week
> - * * @param startDay
> * (Calendar.SUNDAY | Calendar.MONDAY)
> */
> @@ -417,6 +417,12 @@
> activeTask = task;
> ((AbstractTask) activeTask).setActive(true);
>
> + AbstractRepositoryConnector connector =
> repositoryManager.getRepositoryConnector(task.getConnectorKi nd());
> +
> + if (connector != null) {
> +
> connector.activateTask(repositoryManager.getRepository(task. getRepositoryUrl()),
> task);
> + }
> +
> for (ITaskActivationListener listener : new
> ArrayList<ITaskActivationListener>(activationListeners)) {
> try {
> listener.taskActivated(task);
> @@ -452,6 +458,12 @@
> ((AbstractTask) activeTask).setActive(false);
> activeTask = null;
>
> + AbstractRepositoryConnector connector =
> repositoryManager.getRepositoryConnector(task.getConnectorKi nd());
> +
> + if (connector != null) {
> +
> connector.deactivateTask(repositoryManager.getRepository(tas k.getRepositoryUrl()));
>
> + }
> +
> for (ITaskActivationListener listener : new
> ArrayList<ITaskActivationListener>(activationListeners)) {
> try {
> listener.taskDeactivated(task);
> Index: src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.java
> ============================================================ =======
> RCS file:
> /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.cor e/src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.jav a,v
>
> retrieving revision 1.34
> diff -u -r1.34 AbstractTask.java
> --- src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.java 8 May
> 2009 04:43:30 -0000 1.34
> +++ src/org/eclipse/mylyn/internal/tasks/core/AbstractTask.java 4 Jul
> 2009 22:49:43 -0000
> @@ -84,6 +84,8 @@
>
> private int estimatedTimeHours = 1;
>
> + private float totalTimeHours = -1;
> +
> private boolean markReadPending;
>
> // TODO 4.0 make private
> @@ -303,6 +305,14 @@
> this.estimatedTimeHours = estimated;
> }
>
> + public float getTotalTimeHours() {
> + return totalTimeHours;
> + }
> +
> + public void setTotalTimeHours(float total) {
> + this.totalTimeHours = total;
> + }
> +
> void addParentContainer(AbstractTaskContainer container) {
> containers.add(container);
> }
> Index: .refactorings/2009/7/27/refactorings.index
> ============================================================ =======
> RCS file: .refactorings/2009/7/27/refactorings.index
> diff -N .refactorings/2009/7/27/refactorings.index
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ .refactorings/2009/7/27/refactorings.index 1 Jan 1970 00:00:00 -0000
> @@ -0,0 +1,1 @@
> +1246743674234 Rename method 'acticateTask'
> Index: .refactorings/2009/7/27/refactorings.history
> ============================================================ =======
> RCS file: .refactorings/2009/7/27/refactorings.history
> diff -N .refactorings/2009/7/27/refactorings.history
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ .refactorings/2009/7/27/refactorings.history 1 Jan 1970 00:00:00
> -0000
> @@ -0,0 +1,4 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<session version="1.0">
> +<refactoring comment="Rename method
> 'org.eclipse.mylyn.tasks.core.AbstractRepositoryCon nector.acticateTask(...)'
> to 'activateTask'
- Original project:
> 'org.eclipse.mylyn.tasks.core'
&a mp;#x0A;- Original element:
> 'org.eclipse.mylyn.tasks.core.AbstractRepositoryCon nector.acticateTask(...)'
-
> Renamed element:
> 'org.eclipse.mylyn.tasks.core.AbstractRepositoryCon nector.activateTask(...)'
-
> Update references to refactored element" delegate="false"
> deprecate="false" description="Rename method 'acticateTask'"
> flags="589830" id="org.eclipse.jdt.ui.rename.method"
> input=" /src<org.eclipse.mylyn.tasks.core{AbstractRepositoryC onnector.java[AbstractRepositoryConnector~acticateTask~QITas k; "
> name="activateTask" references="true" stamp="1246743674234" version="1.0"/>
> +</session>
>
>
|
|
| | | | | | |
Goto Forum:
Current Time: Sat Aug 31 23:30:24 GMT 2024
Powered by FUDForum. Page generated in 0.05233 seconds
|