From 77fc4c562681a272ee024b48b86ec44e0f29e2e6 Mon Sep 17 00:00:00 2001 From: Evgeny Zakrevsky Date: Thu, 27 Dec 2012 16:12:12 +0400 Subject: [PATCH] IDEA-74652 Time tracking features. Integration with youtrack and jira. --- .../github/tasks/GitHubRepository.java | 5 + .../github/tasks/GitHubRepositoryType.java | 5 - .../intellij/tasks/jira/JiraRepository.java | 28 ++++ .../tasks/jira/JiraRepositoryType.java | 5 - .../com/intellij/tasks/TaskRepository.java | 15 +++ .../intellij/tasks/TaskRepositoryType.java | 13 -- .../tasks/config/BaseRepositoryEditor.java | 6 +- .../tasks/generic/GenericRepository.java | 5 + .../tasks/generic/GenericRepositoryType.java | 5 - .../generic/assembla/AssemblaRepository.java | 5 + .../assembla/AssemblaRepositoryType.java | 5 - .../pivotal/PivotalTrackerRepository.java | 5 + .../pivotal/PivotalTrackerRepositoryType.java | 5 - .../tasks/redmine/RedmineRepository.java | 5 + .../tasks/redmine/RedmineRepositoryType.java | 5 - .../intellij/tasks/trac/TracRepository.java | 5 + .../tasks/trac/TracRepositoryType.java | 5 - .../tasks/youtrack/YouTrackRepository.java | 31 ++++- .../SendTimeTrackingInformationDialog.java | 121 ++++++++++++++++++ .../timeTracking/TasksToolWindowPanel.java | 106 +++++++++------ 20 files changed, 293 insertions(+), 92 deletions(-) create mode 100644 plugins/tasks/tasks-time-tracking/src/com/intellij/tasks/timeTracking/SendTimeTrackingInformationDialog.java diff --git a/plugins/github/src/org/jetbrains/plugins/github/tasks/GitHubRepository.java b/plugins/github/src/org/jetbrains/plugins/github/tasks/GitHubRepository.java index fe162a4d5c0d..6059d36c91d3 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/tasks/GitHubRepository.java +++ b/plugins/github/src/org/jetbrains/plugins/github/tasks/GitHubRepository.java @@ -362,4 +362,9 @@ public class GitHubRepository extends BaseRepositoryImpl { if (getRepoName() != null ? !getRepoName().equals(that.getRepoName()) : that.getRepoName() != null) return false; return true; } + + @Override + protected int getFeatures() { + return BASIC_HTTP_AUTHORIZATION; + } } diff --git a/plugins/github/src/org/jetbrains/plugins/github/tasks/GitHubRepositoryType.java b/plugins/github/src/org/jetbrains/plugins/github/tasks/GitHubRepositoryType.java index f4befc957044..91d93ce0d771 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/tasks/GitHubRepositoryType.java +++ b/plugins/github/src/org/jetbrains/plugins/github/tasks/GitHubRepositoryType.java @@ -52,9 +52,4 @@ public class GitHubRepositoryType extends BaseRepositoryType { Consumer changeListener) { return new GitHubRepositoryEditor(project, repository, changeListener); } - - @Override - protected int getFeatures() { - return BASIC_HTTP_AUTHORIZATION; - } } diff --git a/plugins/tasks/jira-connector/src/main/java/com/intellij/tasks/jira/JiraRepository.java b/plugins/tasks/jira-connector/src/main/java/com/intellij/tasks/jira/JiraRepository.java index 551178a246fe..1fc4db036117 100644 --- a/plugins/tasks/jira-connector/src/main/java/com/intellij/tasks/jira/JiraRepository.java +++ b/plugins/tasks/jira-connector/src/main/java/com/intellij/tasks/jira/JiraRepository.java @@ -4,6 +4,7 @@ import com.atlassian.connector.commons.jira.soap.axis.JiraSoapService; import com.atlassian.connector.commons.jira.soap.axis.JiraSoapServiceServiceLocator; import com.atlassian.theplugin.jira.api.JIRAIssueBean; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.tasks.LocalTask; import com.intellij.tasks.Task; import com.intellij.tasks.TaskRepository; import com.intellij.tasks.TaskState; @@ -17,6 +18,7 @@ import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.StringRequestEntity; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; @@ -211,4 +213,30 @@ public class JiraRepository extends BaseRepositoryImpl { return null; } } + + @Override + public void updateTimeSpent(final LocalTask task, final int timeSpent, final String comment) throws Exception { + final HttpClient client = login(); + checkVersion(client); + PostMethod method = new PostMethod(getUrl() + "/rest/api/2/issue/" + task.getId() + "/worklog"); + method.setRequestEntity(new StringRequestEntity("{\"timeSpentSeconds\" : " + String.valueOf(timeSpent * 60) + + ", \"comment\" : " + comment + "}", "application/json", "UTF-8")); + client.executeMethod(method); + if (method.getStatusCode() != 201) { + throw new Exception(method.getResponseBodyAsString()); + } + } + + private void checkVersion(final HttpClient client) throws Exception { + GetMethod method = new GetMethod(getUrl() + "/rest/api/2/project"); + client.executeMethod(method); + if (method.getStatusCode() != 200) { + throw new Exception("This version of JIRA doesn't have support REST API for working with worklog items."); + } + } + + @Override + protected int getFeatures() { + return BASIC_HTTP_AUTHORIZATION | TIME_MANAGEMENT; + } } diff --git a/plugins/tasks/jira-connector/src/main/java/com/intellij/tasks/jira/JiraRepositoryType.java b/plugins/tasks/jira-connector/src/main/java/com/intellij/tasks/jira/JiraRepositoryType.java index 5eb11a6be7f7..f34ceffb794a 100644 --- a/plugins/tasks/jira-connector/src/main/java/com/intellij/tasks/jira/JiraRepositoryType.java +++ b/plugins/tasks/jira-connector/src/main/java/com/intellij/tasks/jira/JiraRepositoryType.java @@ -34,11 +34,6 @@ public class JiraRepositoryType extends BaseRepositoryType { public Class getRepositoryClass() { return JiraRepository.class; } - - @Override - protected int getFeatures() { - return BASIC_HTTP_AUTHORIZATION; - } // //@Override //public EnumSet getPossibleTaskStates() { diff --git a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepository.java b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepository.java index 1dda696da52f..20ca2c260b75 100644 --- a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepository.java +++ b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepository.java @@ -36,6 +36,10 @@ import java.util.concurrent.Callable; */ @Tag("server") public abstract class TaskRepository { + protected static final int NO_FEATURES = 0; + public static final int BASIC_HTTP_AUTHORIZATION = 0x0001; + public static final int LOGIN_ANONYMOUSLY = 0x0002; + public static final int TIME_MANAGEMENT = 0x0004; @Attribute("url") public String getUrl() { @@ -190,6 +194,10 @@ public abstract class TaskRepository { return "{id} (e.g. FOO-001), {summary}, {number} (e.g. 001), {project} (e.g. FOO)"; } + public void updateTimeSpent(final LocalTask task, final int timeSpent, final String comment) throws Exception { + throw new UnsupportedOperationException(); + } + public abstract static class CancellableConnection implements Callable { @Nullable @@ -209,4 +217,11 @@ public abstract class TaskRepository { public abstract void cancel(); } + public boolean isSupported(int feature) { + return (getFeatures() & feature) != 0; + } + + protected int getFeatures() { + return NO_FEATURES; + } } diff --git a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepositoryType.java b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepositoryType.java index 64285a69d3ca..9aaa0319ca64 100644 --- a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepositoryType.java +++ b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskRepositoryType.java @@ -33,11 +33,6 @@ public abstract class TaskRepositoryType { public static final ExtensionPointName EP_NAME = new ExtensionPointName("com.intellij.tasks.repositoryType"); - protected static final int NO_FEATURES = 0; - - public static final int BASIC_HTTP_AUTHORIZATION = 0x0001; - public static final int LOGIN_ANONYMOUSLY = 0x0002; - @NotNull public abstract String getName(); @@ -52,15 +47,7 @@ public abstract class TaskRepositoryType { public abstract Class getRepositoryClass(); - public boolean isSupported(int feature) { - return (getFeatures() & feature) != 0; - } - public EnumSet getPossibleTaskStates() { return EnumSet.noneOf(TaskState.class); } - - protected int getFeatures() { - return NO_FEATURES; - } } diff --git a/plugins/tasks/tasks-api/src/com/intellij/tasks/config/BaseRepositoryEditor.java b/plugins/tasks/tasks-api/src/com/intellij/tasks/config/BaseRepositoryEditor.java index 0395fe96019c..7745a72f0c98 100644 --- a/plugins/tasks/tasks-api/src/com/intellij/tasks/config/BaseRepositoryEditor.java +++ b/plugins/tasks/tasks-api/src/com/intellij/tasks/config/BaseRepositoryEditor.java @@ -21,7 +21,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.project.Project; import com.intellij.tasks.TaskManager; -import com.intellij.tasks.TaskRepositoryType; +import com.intellij.tasks.TaskRepository; import com.intellij.tasks.impl.BaseRepository; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.PanelWithAnchor; @@ -99,9 +99,9 @@ public class BaseRepositoryEditor extends TaskReposito myUseProxy.setSelected(repository.isUseProxy()); myUseHttpAuthenticationCheckBox.setSelected(repository.isUseHttpAuthentication()); - myUseHttpAuthenticationCheckBox.setVisible(repository.getRepositoryType().isSupported(TaskRepositoryType.BASIC_HTTP_AUTHORIZATION)); + myUseHttpAuthenticationCheckBox.setVisible(repository.isSupported(TaskRepository.BASIC_HTTP_AUTHORIZATION)); - myLoginAnonymouslyJBCheckBox.setVisible(repository.getRepositoryType().isSupported(TaskRepositoryType.LOGIN_ANONYMOUSLY)); + myLoginAnonymouslyJBCheckBox.setVisible(repository.isSupported(TaskRepository.LOGIN_ANONYMOUSLY)); myLoginAnonymouslyJBCheckBox.setSelected(repository.isLoginAnonymously()); myLoginAnonymouslyJBCheckBox.addActionListener(new ActionListener() { @Override diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepository.java index 1111985096c7..ff823a60e1e9 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepository.java @@ -331,4 +331,9 @@ public class GenericRepository extends BaseRepositoryImpl { protected String getTasksListURLDefault() { return ""; } + + @Override + protected int getFeatures() { + return LOGIN_ANONYMOUSLY | BASIC_HTTP_AUTHORIZATION; + } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryType.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryType.java index 3f5fa6988ba7..720afdb754cf 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryType.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/GenericRepositoryType.java @@ -45,9 +45,4 @@ public class GenericRepositoryType extends BaseRepositoryType final Consumer changeListener) { return new GenericRepositoryEditor(project, repository, changeListener); } - - @Override - protected int getFeatures() { - return LOGIN_ANONYMOUSLY | BASIC_HTTP_AUTHORIZATION; - } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/assembla/AssemblaRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/assembla/AssemblaRepository.java index ec0e4b1a676b..dfe20bf8bf0e 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/assembla/AssemblaRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/assembla/AssemblaRepository.java @@ -79,4 +79,9 @@ public class AssemblaRepository extends GenericRepository { public boolean isConfigured() { return super.isConfigured() && StringUtil.isNotEmpty(getUsername()) && StringUtil.isNotEmpty(getPassword()); } + + @Override + protected int getFeatures() { + return BASIC_HTTP_AUTHORIZATION; + } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/assembla/AssemblaRepositoryType.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/assembla/AssemblaRepositoryType.java index 61cdff48bdbc..c25b3c8527c9 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/assembla/AssemblaRepositoryType.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/generic/assembla/AssemblaRepositoryType.java @@ -45,9 +45,4 @@ public class AssemblaRepositoryType extends TaskRepositoryType getRepositoryClass() { return AssemblaRepository.class; } - - @Override - protected int getFeatures() { - return BASIC_HTTP_AUTHORIZATION; - } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/pivotal/PivotalTrackerRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/pivotal/PivotalTrackerRepository.java index fdfe19c89182..340a9deadb0e 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/pivotal/PivotalTrackerRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/pivotal/PivotalTrackerRepository.java @@ -361,4 +361,9 @@ public class PivotalTrackerRepository extends BaseRepositoryImpl { if (getCommitMessageFormat() != null ? !getCommitMessageFormat().equals(that.getCommitMessageFormat()) : that.getCommitMessageFormat() != null) return false; return isShouldFormatCommitMessage() == that.isShouldFormatCommitMessage(); } + + @Override + protected int getFeatures() { + return BASIC_HTTP_AUTHORIZATION; + } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/pivotal/PivotalTrackerRepositoryType.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/pivotal/PivotalTrackerRepositoryType.java index 3cd886ed42b2..8edecd9e6f16 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/pivotal/PivotalTrackerRepositoryType.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/pivotal/PivotalTrackerRepositoryType.java @@ -52,9 +52,4 @@ public class PivotalTrackerRepositoryType extends BaseRepositoryType changeListener) { return new PivotalTrackerRepositoryEditor(project, repository, changeListener); } - - @Override - protected int getFeatures() { - return BASIC_HTTP_AUTHORIZATION; - } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java index e468a934686c..f50fe62f92af 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepository.java @@ -306,4 +306,9 @@ public class RedmineRepository extends BaseRepositoryImpl { "/projects" + (!StringUtil.isEmpty(getProjectId()) ? "/" + getProjectId() : ""); } + + @Override + protected int getFeatures() { + return BASIC_HTTP_AUTHORIZATION; + } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepositoryType.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepositoryType.java index 3a94736f3bcf..857063605c45 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepositoryType.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/redmine/RedmineRepositoryType.java @@ -45,9 +45,4 @@ public class RedmineRepositoryType extends BaseRepositoryType Consumer changeListener) { return new RedmineRepositoryEditor(project, repository, changeListener); } - - @Override - protected int getFeatures() { - return BASIC_HTTP_AUTHORIZATION; - } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepository.java index 6f9224b4f3eb..92707d861fcd 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepository.java @@ -258,4 +258,9 @@ public class TracRepository extends BaseRepositoryImpl { public boolean equals(Object o) { return super.equals(o) && Comparing.equal(((TracRepository)o).getDefaultSearch(), getDefaultSearch()); } + + @Override + protected int getFeatures() { + return BASIC_HTTP_AUTHORIZATION; + } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepositoryType.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepositoryType.java index d4090cb23c38..d3e02f6f71f4 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepositoryType.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepositoryType.java @@ -53,11 +53,6 @@ public class TracRepositoryType extends BaseRepositoryType { return TracRepository.class; } - @Override - protected int getFeatures() { - return BASIC_HTTP_AUTHORIZATION; - } - @NotNull @Override public TaskRepositoryEditor createEditor(TracRepository repository, diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/youtrack/YouTrackRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/youtrack/YouTrackRepository.java index 94e84226bc6e..f8d30fda4874 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/youtrack/YouTrackRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/youtrack/YouTrackRepository.java @@ -8,6 +8,7 @@ import com.intellij.tasks.impl.BaseRepository; import com.intellij.tasks.impl.BaseRepositoryImpl; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.text.VersionComparatorUtil; import com.intellij.util.xmlb.annotations.Tag; import icons.TasksIcons; import org.apache.axis.utils.XMLChar; @@ -37,7 +38,9 @@ public class YouTrackRepository extends BaseRepositoryImpl { private String myDefaultSearch = "for: me sort by: updated #Unresolved"; - /** for serialization */ + /** + * for serialization + */ @SuppressWarnings({"UnusedDeclaration"}) public YouTrackRepository() { } @@ -277,4 +280,30 @@ public class YouTrackRepository extends BaseRepositoryImpl { } private static final Logger LOG = Logger.getInstance("#com.intellij.tasks.youtrack.YouTrackRepository"); + + @Override + public void updateTimeSpent(final LocalTask task, final int timeSpent, final String comment) throws Exception { + checkVersion(); + final HttpMethod method = doREST("/rest/issue/execute/" + task.getId() + "?command=work+Today+" + timeSpent + "m+" + comment, true); + if (method.getStatusCode() != 200) { + InputStream stream = method.getResponseBodyAsStream(); + String message = new SAXBuilder(false).build(stream).getRootElement().getText(); + throw new Exception(message); + } + } + + private void checkVersion() throws Exception { + HttpMethod method = doREST("/rest/workflow/version", false); + InputStream stream = method.getResponseBodyAsStream(); + Element element = new SAXBuilder(false).build(stream).getRootElement(); + final boolean timeTrackingAvailable = element.getName().equals("version") && VersionComparatorUtil.compare(element.getChildText("version"), "4.1") >= 0; + if (!timeTrackingAvailable) { + throw new Exception("This version of Youtrack the time tracking is not supported"); + } + } + + @Override + protected int getFeatures() { + return TIME_MANAGEMENT; + } } diff --git a/plugins/tasks/tasks-time-tracking/src/com/intellij/tasks/timeTracking/SendTimeTrackingInformationDialog.java b/plugins/tasks/tasks-time-tracking/src/com/intellij/tasks/timeTracking/SendTimeTrackingInformationDialog.java new file mode 100644 index 000000000000..209916bf3c0e --- /dev/null +++ b/plugins/tasks/tasks-time-tracking/src/com/intellij/tasks/timeTracking/SendTimeTrackingInformationDialog.java @@ -0,0 +1,121 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.tasks.timeTracking; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.ValidationInfo; +import com.intellij.tasks.LocalTask; +import com.intellij.tasks.TaskRepository; +import com.intellij.ui.ScrollPaneFactory; +import com.intellij.util.ui.FormBuilder; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * User: evgeny.zakrevsky + * Date: 12/26/12 + */ +public class SendTimeTrackingInformationDialog extends DialogWrapper { + private final static Logger LOG = Logger.getInstance("#com.intellij.tasks.timeTracking.TasksToolWindowPanel"); + public static final Pattern PATTERN = Pattern.compile("([0-9]+)d ([0-9]+)h ([0-9]+)m"); + + @Nullable private final Project myProject; + private final LocalTask myTask; + private JTextField myTimeSpentField; + private JTextArea myCommentField; + + protected SendTimeTrackingInformationDialog(@Nullable final Project project, final LocalTask localTask) { + super(project); + myProject = project; + myTask = localTask; + setTitle("Time Tracking"); + init(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + myTimeSpentField = new JTextField(String.valueOf(formatDuration(myTask.getTimeSpent()))); + myCommentField = new JTextArea(); + return FormBuilder.createFormBuilder() + .addComponent(new JLabel("Send information about activity on " + myTask.getPresentableName())) + .addLabeledComponent("Time spent:", myTimeSpentField, UIUtil.LARGE_VGAP) + .addLabeledComponent("Comment", ScrollPaneFactory.createScrollPane(myCommentField)).getPanel(); + } + + private static String formatDuration(final long milliseconds) { + final int second = 1000; + final int minute = 60 * second; + final int hour = 60 * minute; + final int day = 24 * hour; + + final int days = (int)(milliseconds / day); + final int hours = (int)(milliseconds % day / hour); + final int minutes = (int)(milliseconds % hour / minute); + + String daysString = days + "d "; + String hoursString = hours + "h "; + String minutesString = minutes + "m"; + + return daysString + hoursString + minutesString; + } + + @Override + protected void doOKAction() { + final Matcher matcher = PATTERN.matcher(myTimeSpentField.getText()); + if (matcher.matches()) { + final int timeSpent = Integer.valueOf(matcher.group(1)) * 24 * 60 + Integer.valueOf(matcher.group(2)) * 60 + Integer.valueOf( + matcher.group(3)); + + final TaskRepository repository = myTask.getRepository(); + if (repository != null && + repository.isSupported(TaskRepository.TIME_MANAGEMENT)) { + try { + repository.updateTimeSpent(myTask, timeSpent, myCommentField.getText()); + } + catch (Exception e1) { + Messages + .showErrorDialog(myProject, "Could not send information for " + myTask.getPresentableName() + "
" + e1.getMessage(), + "Error"); + LOG.warn(e1); + } + } + } + + + super.doOKAction(); + } + + @Nullable + @Override + protected ValidationInfo doValidate() { + if (!PATTERN.matcher(myTimeSpentField.getText()).matches()) return new ValidationInfo("Time Spent has broken format"); + return null; + } + + @Nullable + @Override + protected String getDimensionServiceKey() { + return "com.intellij.tasks.timeTracking.TasksToolWindowPanel"; + } +} diff --git a/plugins/tasks/tasks-time-tracking/src/com/intellij/tasks/timeTracking/TasksToolWindowPanel.java b/plugins/tasks/tasks-time-tracking/src/com/intellij/tasks/timeTracking/TasksToolWindowPanel.java index d10fdd26641f..dc6e2d9edada 100644 --- a/plugins/tasks/tasks-time-tracking/src/com/intellij/tasks/timeTracking/TasksToolWindowPanel.java +++ b/plugins/tasks/tasks-time-tracking/src/com/intellij/tasks/timeTracking/TasksToolWindowPanel.java @@ -8,9 +8,7 @@ import com.intellij.openapi.ui.SimpleToolWindowPanel; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.IconLoader; -import com.intellij.tasks.LocalTask; -import com.intellij.tasks.TaskListenerAdapter; -import com.intellij.tasks.TaskManager; +import com.intellij.tasks.*; import com.intellij.tasks.actions.GotoTaskAction; import com.intellij.tasks.actions.SwitchTaskAction; import com.intellij.ui.LayeredIcon; @@ -136,47 +134,25 @@ public class TasksToolWindowPanel extends SimpleToolWindowPanel implements Dispo updateTable(); } }); - group.add(new ToggleAction("Auto mode", "Automatic starting and stopping of timer", TasksIcons.AutoMode) { - @Override - public boolean isSelected(final AnActionEvent e) { - return myTimeTrackingManager.getState().autoMode; - } - - @Override - public void setSelected(final AnActionEvent e, final boolean state) { - myTimeTrackingManager.setAutoMode(state); - updateTable(); - } - }); - group.add(new AnAction() { - @Override - public void update(final AnActionEvent e) { - if (myTimeTrackingManager.getState().autoMode) { - e.getPresentation().setEnabled(false); - e.getPresentation().setIcon(TasksIcons.StartTimer); - e.getPresentation().setText("Start timer for active task"); - } - else { - e.getPresentation().setEnabled(true); - if (myTaskManager.getActiveTask().isRunning()) { - e.getPresentation().setIcon(TasksIcons.StopTimer); - e.getPresentation().setText("Stop timer for active task"); - } - else { - e.getPresentation().setIcon(TasksIcons.StartTimer); - e.getPresentation().setText("Start timer for active task"); - } - } - } - + group.add(new ModeToggleAction()); + group.add(new StartStopAction()); + group.add(new AnAction("Post work item to bugtracker", "Post work item to bugtracker", AllIcons.Actions.Export) { @Override public void actionPerformed(final AnActionEvent e) { - final LocalTask activeTask = myTaskManager.getActiveTask(); - if (activeTask.isRunning()) { - activeTask.setRunning(false); + final LocalTask localTask = myTable.getSelectedObject(); + if (localTask == null) return; + new SendTimeTrackingInformationDialog(myProject, localTask).show(); + } + + @Override + public void update(final AnActionEvent e) { + final LocalTask localTask = myTable.getSelectedObject(); + if (localTask == null) { + e.getPresentation().setEnabled(false); } else { - activeTask.setRunning(true); + final TaskRepository repository = localTask.getRepository(); + e.getPresentation().setEnabled(repository != null && repository.isSupported(TaskRepository.TIME_MANAGEMENT)); } } }); @@ -303,4 +279,54 @@ public class TasksToolWindowPanel extends SimpleToolWindowPanel implements Dispo myTimer.stop(); myTimer = null; } + + private class StartStopAction extends AnAction { + @Override + public void update(final AnActionEvent e) { + if (myTimeTrackingManager.getState().autoMode) { + e.getPresentation().setEnabled(false); + e.getPresentation().setIcon(TasksIcons.StartTimer); + e.getPresentation().setText("Start timer for active task"); + } + else { + e.getPresentation().setEnabled(true); + if (myTaskManager.getActiveTask().isRunning()) { + e.getPresentation().setIcon(TasksIcons.StopTimer); + e.getPresentation().setText("Stop timer for active task"); + } + else { + e.getPresentation().setIcon(TasksIcons.StartTimer); + e.getPresentation().setText("Start timer for active task"); + } + } + } + + @Override + public void actionPerformed(final AnActionEvent e) { + final LocalTask activeTask = myTaskManager.getActiveTask(); + if (activeTask.isRunning()) { + activeTask.setRunning(false); + } + else { + activeTask.setRunning(true); + } + } + } + + private class ModeToggleAction extends ToggleAction { + public ModeToggleAction() { + super("Auto mode", "Automatic starting and stopping of timer", TasksIcons.AutoMode); + } + + @Override + public boolean isSelected(final AnActionEvent e) { + return myTimeTrackingManager.getState().autoMode; + } + + @Override + public void setSelected(final AnActionEvent e, final boolean state) { + myTimeTrackingManager.setAutoMode(state); + updateTable(); + } + } }