diff options
Diffstat (limited to 'org.eclipse.mylyn.trac.core/src')
7 files changed, 1016 insertions, 0 deletions
diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracAttachmentHandler.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracAttachmentHandler.java new file mode 100644 index 000000000..895d2c5db --- /dev/null +++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracAttachmentHandler.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2004 - 2006 University Of British Columbia and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.trac.core; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Proxy; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.mylar.internal.trac.core.model.TracTicket; +import org.eclipse.mylar.tasks.core.AbstractRepositoryTask; +import org.eclipse.mylar.tasks.core.IAttachmentHandler; +import org.eclipse.mylar.tasks.core.RepositoryAttachment; +import org.eclipse.mylar.tasks.core.RepositoryTaskAttribute; +import org.eclipse.mylar.tasks.core.TaskRepository; + +/** + * @author Steffen Pingel + */ +public class TracAttachmentHandler implements IAttachmentHandler { + + private TracRepositoryConnector connector; + + public TracAttachmentHandler(TracRepositoryConnector connector) { + this.connector = connector; + } + + public void downloadAttachment(TaskRepository repository, AbstractRepositoryTask task, RepositoryAttachment attachment, File file, Proxy proxySettings) throws CoreException { + String filename = attachment.getAttributeValue(RepositoryTaskAttribute.ATTACHMENT_FILENAME); + if (filename == null) { + throw new CoreException(new Status(IStatus.ERROR, TracCorePlugin.PLUGIN_ID, IStatus.OK, "Attachment download from " + task.getRepositoryUrl() + " failed, missing attachment filename.", null)); + } + + try { + ITracClient client = connector.getClientManager().getRepository(repository); + int id = Integer.parseInt(AbstractRepositoryTask.getTaskId(task.getHandleIdentifier())); + byte[] data = client.getAttachmentData(id, filename); + writeData(file, data); + } catch (Exception e) { + throw new CoreException(new Status(IStatus.ERROR, TracCorePlugin.PLUGIN_ID, 0, "Attachment download from " + task.getRepositoryUrl() + " failed, please see details.", e )); + } + } + + private void writeData(File file, byte[] data) throws IOException { + OutputStream out = new FileOutputStream(file); + try { + out.write(data); + } finally { + out.close(); + } + } + + public void uploadAttachment(TaskRepository repository, AbstractRepositoryTask task, String comment, String description, File file, String contentType, boolean isPatch, Proxy proxySettings) throws CoreException { + if (!connector.hasAttachmentSupport(repository, task)) { + throw new CoreException(new Status(IStatus.INFO, TracCorePlugin.PLUGIN_ID, IStatus.OK, "Attachments are not supported by this repository access type.", null)); + } + + try { + ITracClient client = connector.getClientManager().getRepository(repository); + int id = Integer.parseInt(AbstractRepositoryTask.getTaskId(task.getHandleIdentifier())); + byte[] data = readData(file); + client.putAttachmentData(id, file.getName(), description, data); + if (comment != null && comment.length() > 0) { + TracTicket ticket = new TracTicket(id); + client.updateTicket(ticket, comment); + } + } catch (Exception e) { + throw new CoreException(new Status(IStatus.ERROR, TracCorePlugin.PLUGIN_ID, 0, "Attachment upload to " + task.getRepositoryUrl() + " failed, please see details.", e )); + } + } + + private byte[] readData(File file) throws IOException { + if (file.length() > Integer.MAX_VALUE) { + throw new IOException("Can not upload files larger than " + Integer.MAX_VALUE + " bytes"); + } + + InputStream in = new FileInputStream(file); + try { + byte[] data = new byte[(int) file.length()]; + in.read(data, 0, (int) file.length()); + return data; + } finally { + in.close(); + } + } + + public boolean canDownloadAttachment(TaskRepository repository, AbstractRepositoryTask task) { + return connector.hasAttachmentSupport(repository, task); + } + + public boolean canUploadAttachment(TaskRepository repository, AbstractRepositoryTask task) { + return connector.hasAttachmentSupport(repository, task); + } + +} diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java index a9a2389df..20f14607b 100644 --- a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java +++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracCorePlugin.java @@ -10,7 +10,9 @@ *******************************************************************************/ package org.eclipse.mylar.internal.trac.core; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Plugin; import org.eclipse.core.runtime.Status; import org.osgi.framework.BundleContext; @@ -29,6 +31,8 @@ public class TracCorePlugin extends Plugin { private static TracCorePlugin plugin; public final static String REPOSITORY_KIND = "trac"; + + private TracRepositoryConnector connector; public TracCorePlugin() { } @@ -45,10 +49,45 @@ public class TracCorePlugin extends Plugin { @Override public void stop(BundleContext context) throws Exception { + if (connector != null) { + connector.stop(); + connector = null; + } + plugin = null; super.stop(context); } + public TracRepositoryConnector getConnector() { + return connector; + } + + void setConnector(TracRepositoryConnector connector) { + this.connector = connector; + } + + /** + * Returns the path to the file caching repository attributes. + */ + protected IPath getRepostioryAttributeCachePath() { + IPath stateLocation = Platform.getStateLocation(TracCorePlugin.getDefault().getBundle()); + IPath cacheFile = stateLocation.append("repositoryConfigurations"); + return cacheFile; + } + + public static IStatus toStatus(Throwable e) { + if (e instanceof TracLoginException) { + return new Status(Status.ERROR, PLUGIN_ID, IStatus.INFO, + "Your login name or password is incorrect. Ensure proper repository configuration in Task Repositories View.", null); + } else if (e instanceof TracException) { + return new Status(Status.ERROR, PLUGIN_ID, IStatus.INFO, "Connection Error: " + e.getMessage(), e); + } else if (e instanceof ClassCastException) { + return new Status(Status.ERROR, PLUGIN_ID, IStatus.INFO, "Error parsing server response", e); + } else { + return new Status(Status.ERROR, PLUGIN_ID, IStatus.ERROR, "Unexpected error", e); + } + } + /** * Convenience method for logging statuses to the plug-in log * diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracOfflineTaskHandler.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracOfflineTaskHandler.java new file mode 100644 index 000000000..9c18d60e3 --- /dev/null +++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracOfflineTaskHandler.java @@ -0,0 +1,275 @@ +/******************************************************************************* + * Copyright (c) 2004 - 2006 University Of British Columbia and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * University Of British Columbia - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.trac.core; + +import java.io.UnsupportedEncodingException; +import java.net.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.mylar.internal.trac.core.TracAttributeFactory.Attribute; +import org.eclipse.mylar.internal.trac.core.model.TracAttachment; +import org.eclipse.mylar.internal.trac.core.model.TracComment; +import org.eclipse.mylar.internal.trac.core.model.TracTicket; +import org.eclipse.mylar.internal.trac.core.util.TracUtils; +import org.eclipse.mylar.tasks.core.AbstractAttributeFactory; +import org.eclipse.mylar.tasks.core.AbstractRepositoryTask; +import org.eclipse.mylar.tasks.core.IOfflineTaskHandler; +import org.eclipse.mylar.tasks.core.RepositoryAttachment; +import org.eclipse.mylar.tasks.core.RepositoryOperation; +import org.eclipse.mylar.tasks.core.RepositoryTaskAttribute; +import org.eclipse.mylar.tasks.core.RepositoryTaskData; +import org.eclipse.mylar.tasks.core.TaskComment; +import org.eclipse.mylar.tasks.core.TaskRepository; + +/** + * @author Steffen Pingel + */ +public class TracOfflineTaskHandler implements IOfflineTaskHandler { + + private AbstractAttributeFactory attributeFactory = new TracAttributeFactory(); + + private TracRepositoryConnector connector; + + public TracOfflineTaskHandler(TracRepositoryConnector connector) { + this.connector = connector; + } + + public RepositoryTaskData downloadTaskData(final AbstractRepositoryTask task, TaskRepository repository, Proxy proxySettings) throws CoreException { + if (!connector.hasRichEditor(repository, task)) { + // offline mode is only supported for XML-RPC + return null; + } + + try { + int id = Integer.parseInt(AbstractRepositoryTask.getTaskId(task.getHandleIdentifier())); + RepositoryTaskData data = new RepositoryTaskData(attributeFactory, TracCorePlugin.REPOSITORY_KIND, + repository.getUrl(), id + ""); + ITracClient client = connector.getClientManager().getRepository(repository); + client.updateAttributes(new NullProgressMonitor(), false); + TracTicket ticket = client.getTicket(id); + createDefaultAttributes(attributeFactory, data, client); + updateTaskData(repository, attributeFactory, data, ticket); + return data; + } catch (Exception e) { + throw new CoreException(new Status(IStatus.ERROR, TracCorePlugin.PLUGIN_ID, 0, "Ticket download from " + + task.getRepositoryUrl() + " failed, please see details.", e)); + } + } + + public AbstractAttributeFactory getAttributeFactory() { + return attributeFactory; + } + + public Date getDateForAttributeType(String attributeKey, String dateString) { + if (dateString == null || dateString.length() == 0) { + return null; + } + + try { + String mappedKey = attributeFactory.mapCommonAttributeKey(attributeKey); + if (mappedKey.equals(Attribute.TIME.getTracKey()) || mappedKey.equals(Attribute.CHANGE_TIME.getTracKey())) { + return TracUtils.parseDate(Integer.valueOf(dateString)); + } + } catch (Exception e) { + } + return null; + } + + public static void updateTaskData(TaskRepository repository, AbstractAttributeFactory factory, + RepositoryTaskData data, TracTicket ticket) { + if (ticket.getCreated() != null) { + data.setAttributeValue(Attribute.TIME.getTracKey(), TracUtils.toTracTime(ticket.getCreated()) + ""); + } + if (ticket.getLastChanged() != null) { + data.setAttributeValue(Attribute.CHANGE_TIME.getTracKey(), TracUtils.toTracTime(ticket.getLastChanged()) + + ""); + } + Map<String, String> valueByKey = ticket.getValues(); + for (String key : valueByKey.keySet()) { + data.setAttributeValue(key, valueByKey.get(key)); + } + + TracComment[] comments = ticket.getComments(); + if (comments != null) { + for (int i = 0; i < comments.length; i++) { + if (!"comment".equals(comments[i].getField()) || "".equals(comments[i].getNewValue())) { + continue; + } + + TaskComment taskComment = new TaskComment(factory, data, data.getComments().size() + 1); + taskComment.setAttributeValue(RepositoryTaskAttribute.USER_OWNER, comments[i].getAuthor()); + taskComment + .setAttributeValue(RepositoryTaskAttribute.COMMENT_DATE, comments[i].getCreated().toString()); + taskComment.setAttributeValue(RepositoryTaskAttribute.COMMENT_TEXT, comments[i].getNewValue()); + data.addComment(taskComment); + } + } + + TracAttachment[] attachments = ticket.getAttachments(); + if (attachments != null) { + for (int i = 0; i < attachments.length; i++) { + RepositoryAttachment taskAttachment = new RepositoryAttachment(factory); + taskAttachment.setCreator(attachments[i].getAuthor()); + taskAttachment.setAttributeValue(Attribute.DESCRIPTION.getTracKey(), attachments[i].getDescription()); + taskAttachment.setAttributeValue(RepositoryTaskAttribute.ATTACHMENT_FILENAME, attachments[i] + .getFilename()); + taskAttachment.setAttributeValue(RepositoryTaskAttribute.USER_OWNER, attachments[i].getAuthor()); + taskAttachment.setAttributeValue(RepositoryTaskAttribute.ATTACHMENT_DATE, attachments[i].getCreated() + .toString()); + taskAttachment.setAttributeValue(RepositoryTaskAttribute.ATTACHMENT_URL, repository.getUrl() + + ITracClient.TICKET_ATTACHMENT_URL + ticket.getId() + "/" + attachments[i].getFilename()); + taskAttachment.setAttributeValue(RepositoryTaskAttribute.ATTACHMENT_ID, i + ""); + data.addAttachment(taskAttachment); + } + } + + String[] actions = ticket.getActions(); + if (actions != null) { + // add operations in a defined order + List<String> actionList = new ArrayList<String>(Arrays.asList(actions)); + addOperation(repository, data, ticket, actionList, "leave"); + addOperation(repository, data, ticket, actionList, "accept"); + addOperation(repository, data, ticket, actionList, "resolve"); + addOperation(repository, data, ticket, actionList, "reassign"); + addOperation(repository, data, ticket, actionList, "reopen"); + } + } + + // TODO Reuse Labels from BugzillaServerFacade + private static void addOperation(TaskRepository repository, RepositoryTaskData data, TracTicket ticket, + List<String> actions, String action) { + if (!actions.remove(action)) { + return; + } + + RepositoryOperation operation = null; + if ("leave".equals(action)) { + operation = new RepositoryOperation(action, "Leave as " + data.getStatus() + " " + data.getResolution()); + operation.setChecked(true); + } else if ("accept".equals(action)) { + operation = new RepositoryOperation(action, "Accept"); + } else if ("resolve".equals(action)) { + operation = new RepositoryOperation(action, "Resolve bug, changing resolution to"); + operation.setUpOptions("resolution"); + for (String resolution : ticket.getResolutions()) { + operation.addOption(resolution, resolution); + } + } else if ("reassign".equals(action)) { + operation = new RepositoryOperation(action, "Reassing bug to"); + operation.setInputName("owner"); + operation.setInputValue(TracRepositoryConnector.getDisplayUsername(repository)); + } else if ("reopen".equals(action)) { + operation = new RepositoryOperation(action, "Reopen"); + } + + if (operation != null) { + data.addOperation(operation); + } + } + + public static void createDefaultAttributes(AbstractAttributeFactory factory, RepositoryTaskData data, + ITracClient client) { + createAttribute(factory, data, Attribute.STATUS, client.getTicketStatus()); + createAttribute(factory, data, Attribute.RESOLUTION, client.getTicketResolutions()); + + createAttribute(factory, data, Attribute.COMPONENT, client.getComponents()); + createAttribute(factory, data, Attribute.VERSION, client.getVersions(), true); + createAttribute(factory, data, Attribute.PRIORITY, client.getPriorities()); + createAttribute(factory, data, Attribute.SEVERITY, client.getSeverities()); + + createAttribute(factory, data, Attribute.TYPE, client.getTicketTypes()); + createAttribute(factory, data, Attribute.OWNER); + createAttribute(factory, data, Attribute.MILESTONE, client.getMilestones(), true); + createAttribute(factory, data, Attribute.REPORTER); + + createAttribute(factory, data, Attribute.CC); + createAttribute(factory, data, Attribute.KEYWORDS); + } + + private static RepositoryTaskAttribute createAttribute(AbstractAttributeFactory factory, RepositoryTaskData data, + Attribute attribute, Object[] values, boolean allowEmtpy) { + RepositoryTaskAttribute attr = factory.createAttribute(attribute.getTracKey()); + if (values != null && values.length > 0) { + if (allowEmtpy) { + attr.addOptionValue("", ""); + } + for (int i = 0; i < values.length; i++) { + attr.addOptionValue(values[i].toString(), values[i].toString()); + } + } else { + // attr.setHidden(true); + attr.setReadOnly(true); + } + data.addAttribute(attribute.getTracKey(), attr); + return attr; + } + + private static RepositoryTaskAttribute createAttribute(AbstractAttributeFactory factory, RepositoryTaskData data, + Attribute attribute) { + RepositoryTaskAttribute attr = factory.createAttribute(attribute.getTracKey()); + data.addAttribute(attribute.getTracKey(), attr); + return attr; + } + + private static RepositoryTaskAttribute createAttribute(AbstractAttributeFactory factory, RepositoryTaskData data, + Attribute attribute, Object[] values) { + return createAttribute(factory, data, attribute, values, false); + } + + public Set<AbstractRepositoryTask> getChangedSinceLastSync(TaskRepository repository, + Set<AbstractRepositoryTask> tasks, Proxy proxySettings) throws CoreException, UnsupportedEncodingException { + if (repository.getSyncTimeStamp() == null) { + return tasks; + } + + if (!connector.hasChangedSince(repository)) { + // return an empty list to avoid causing all tasks to synchronized + return Collections.emptySet(); + } + + Date since = new Date(0); + try { + since = TracUtils.parseDate(Integer.parseInt(repository.getSyncTimeStamp())); + } catch (NumberFormatException e) { + } + + ITracClient client; + try { + client = connector.getClientManager().getRepository(repository); + Set<Integer> ids = client.getChangedTickets(since); + + Set<AbstractRepositoryTask> result = new HashSet<AbstractRepositoryTask>(); + if (!ids.isEmpty()) { + for (AbstractRepositoryTask task : tasks) { + Integer id = Integer.parseInt(AbstractRepositoryTask.getTaskId(task.getHandleIdentifier())); + if (ids.contains(id)) { + result.add(task); + } + } + } + return result; + } catch (Exception e) { + throw new CoreException(new Status(IStatus.ERROR, TracCorePlugin.PLUGIN_ID, IStatus.OK, "could not determine changed tasks", e)); + } + } +} diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracQueryHit.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracQueryHit.java new file mode 100644 index 000000000..261312ed8 --- /dev/null +++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracQueryHit.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2006 - 2006 Mylar eclipse.org project and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mylar project committers - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.trac.core; + +import org.eclipse.mylar.tasks.core.AbstractQueryHit; +import org.eclipse.mylar.tasks.core.AbstractRepositoryTask; +import org.eclipse.mylar.tasks.core.ITask; +import org.eclipse.mylar.tasks.core.TaskList; + +/** + * @author Steffen Pingel + */ +public class TracQueryHit extends AbstractQueryHit { + + private TracTask task; + + private boolean completed; + + private TaskList taskList; + + public TracQueryHit(TaskList taskList, String repositoryUrl, String description, String id) { + super(repositoryUrl, description, id); + + this.taskList = taskList; + } + + public TracQueryHit(TaskList taskList, String handle) { + super(AbstractRepositoryTask.getRepositoryUrl(handle), "", AbstractRepositoryTask.getTaskId(handle)); + + this.taskList = taskList; + } + + @Override + public AbstractRepositoryTask getCorrespondingTask() { + return task; + } + + @Override + public AbstractRepositoryTask getOrCreateCorrespondingTask() { + ITask existingTask = taskList.getTask(getHandleIdentifier()); + if (existingTask instanceof TracTask) { + this.task = (TracTask) existingTask; + } else { + this.task = new TracTask(getHandleIdentifier(), getDescription(), true); + task.setCompleted(completed); + task.setPriority(priority); + taskList.addTask(task); + } + return task; + } + + @Override + public boolean isCompleted() { + return (task != null) ? task.isCompleted() : completed; + } + + @Override + public void setCorrespondingTask(AbstractRepositoryTask task) { + if (task instanceof TracTask) { + this.task = (TracTask) task; + } + } + + public String getUrl() { + return getRepositoryUrl() + ITracClient.TICKET_URL + getId(); + } + + public void setCompleted(boolean completed) { + this.completed = completed; + } + +} diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConnector.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConnector.java new file mode 100644 index 000000000..7c589748e --- /dev/null +++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryConnector.java @@ -0,0 +1,240 @@ +/******************************************************************************* + * Copyright (c) 2006 - 2006 Mylar eclipse.org project and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mylar project committers - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.trac.core; + +import java.io.File; +import java.net.Proxy; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.mylar.context.core.MylarStatusHandler; +import org.eclipse.mylar.internal.trac.core.ITracClient.Version; +import org.eclipse.mylar.internal.trac.core.TracTask.Kind; +import org.eclipse.mylar.internal.trac.core.model.TracTicket; +import org.eclipse.mylar.internal.trac.core.model.TracTicket.Key; +import org.eclipse.mylar.tasks.core.AbstractRepositoryConnector; +import org.eclipse.mylar.tasks.core.AbstractRepositoryQuery; +import org.eclipse.mylar.tasks.core.AbstractRepositoryTask; +import org.eclipse.mylar.tasks.core.IAttachmentHandler; +import org.eclipse.mylar.tasks.core.IOfflineTaskHandler; +import org.eclipse.mylar.tasks.core.IQueryHitCollector; +import org.eclipse.mylar.tasks.core.ITask; +import org.eclipse.mylar.tasks.core.TaskRepository; + +/** + * @author Steffen Pingel + */ +public class TracRepositoryConnector extends AbstractRepositoryConnector { + + private final static String CLIENT_LABEL = "Trac (supports 0.9 and later or XML-RPC)"; + + private List<String> supportedVersions; + + private TracClientManager clientManager; + + private TracOfflineTaskHandler offlineTaskHandler = new TracOfflineTaskHandler(this); + + private TracAttachmentHandler attachmentHandler = new TracAttachmentHandler(this); + + public TracRepositoryConnector() { + TracCorePlugin.getDefault().setConnector(this); + } + + @Override + public boolean canCreateNewTask(TaskRepository repository) { + return true; + } + + @Override + public boolean canCreateTaskFromKey(TaskRepository repository) { + return true; + } + + @Override + public String getLabel() { + return CLIENT_LABEL; + } + + @Override + public String getRepositoryType() { + return TracCorePlugin.REPOSITORY_KIND; + } + + @Override + public String getRepositoryUrlFromTaskUrl(String url) { + if (url == null) { + return null; + } + int i = url.lastIndexOf(ITracClient.TICKET_URL); + return (i != -1) ? url.substring(0, i) : null; + } + + @Override + public List<String> getSupportedVersions() { + if (supportedVersions == null) { + supportedVersions = new ArrayList<String>(); + for (Version version : Version.values()) { + supportedVersions.add(version.toString()); + } + } + return supportedVersions; + } + + @Override + public IAttachmentHandler getAttachmentHandler() { + return attachmentHandler; + } + + @Override + public IOfflineTaskHandler getOfflineTaskHandler() { + return offlineTaskHandler; + } + + @Override + public void updateTaskState(AbstractRepositoryTask repositoryTask) { + // TODO Auto-generated method stub + } + + @Override + public IStatus performQuery(AbstractRepositoryQuery query, TaskRepository repository, + Proxy proxySettings, IProgressMonitor monitor, IQueryHitCollector resultCollector) { + + final List<TracTicket> tickets = new ArrayList<TracTicket>(); + + ITracClient tracClient; + try { + tracClient = getClientManager().getRepository(repository); + if (query instanceof TracRepositoryQuery) { + tracClient.search(((TracRepositoryQuery) query).getTracSearch(), tickets); + } + + for (TracTicket ticket : tickets) { + TracQueryHit hit = new TracQueryHit(taskList, query.getRepositoryUrl(), getTicketDescription(ticket), ticket + .getId() + + ""); + hit.setCompleted(TracTask.isCompleted(ticket.getValue(Key.STATUS))); + hit.setPriority(TracTask.getMylarPriority(ticket.getValue(Key.PRIORITY))); + resultCollector.accept(hit); + } + } catch (Throwable e) { + return TracCorePlugin.toStatus(e); + } + + return Status.OK_STATUS; + } + + @Override + public ITask createTaskFromExistingKey(TaskRepository repository, String id, Proxy proxySettings) throws CoreException { + try { + ITracClient connection = getClientManager().getRepository(repository); + TracTicket ticket = connection.getTicket(Integer.parseInt(id)); + + String handleIdentifier = AbstractRepositoryTask.getHandle(repository.getUrl(), ticket.getId()); + TracTask task = createTask(ticket, handleIdentifier); + updateTaskDetails(task, ticket, true); + + return task; + } catch (Exception e) { + MylarStatusHandler.log(e, "Error creating task from key " + id); + } + return null; + } + + public synchronized TracClientManager getClientManager() { + if (clientManager == null) { + File cacheFile = null; + if (TracCorePlugin.getDefault().getRepostioryAttributeCachePath() != null) { + cacheFile = TracCorePlugin.getDefault().getRepostioryAttributeCachePath().toFile(); + } + clientManager = new TracClientManager(cacheFile); + } + return clientManager; + } + + public TracTask createTask(TracTicket ticket, String handleIdentifier) { + TracTask task; + ITask existingTask = taskList.getTask(handleIdentifier); + if (existingTask instanceof TracTask) { + task = (TracTask) existingTask; + } else { + task = new TracTask(handleIdentifier, getTicketDescription(ticket), true); + taskList.addTask(task); + } + return task; + } + + /** + * Updates fields of <code>task</code> from <code>ticket</code>. + */ + public void updateTaskDetails(TracTask task, TracTicket ticket, boolean notify) { + if (ticket.getValue(Key.SUMMARY) != null) { + task.setDescription(getTicketDescription(ticket)); + } + task.setCompleted(TracTask.isCompleted(ticket.getValue(Key.STATUS))); + task.setPriority(TracTask.getMylarPriority(ticket.getValue(Key.PRIORITY))); + if (ticket.getValue(Key.TYPE) != null) { + Kind kind = TracTask.Kind.fromType(ticket.getValue(Key.TYPE)); + task.setKind((kind != null) ? kind.toString() : ticket.getValue(Key.TYPE)); + } + if (ticket.getCreated() != null) { + task.setCreationDate(ticket.getCreated()); + } + + if (notify) { + taskList.notifyLocalInfoChanged(task); + } + } + + private static String getTicketDescription(TracTicket ticket) { + return ticket.getId() + ": " + ticket.getValue(Key.SUMMARY); + } + + public boolean hasChangedSince(TaskRepository repository) { + return Version.XML_RPC.name().equals(repository.getVersion()); + } + + public boolean hasRichEditor(TaskRepository repository, AbstractRepositoryTask task) { + return Version.XML_RPC.name().equals(repository.getVersion()); + } + + public boolean hasAttachmentSupport(TaskRepository repository, AbstractRepositoryTask task) { + return Version.XML_RPC.name().equals(repository.getVersion()); + } + + public void stop() { + if (clientManager != null) { + clientManager.writeCache(); + } + } + + @Override + public void updateAttributes(TaskRepository repository, Proxy proxySettings, IProgressMonitor monitor) throws CoreException { + try { + ITracClient client = getClientManager().getRepository(repository); + client.updateAttributes(monitor, true); + } catch (Exception e) { + MylarStatusHandler.fail(e, "Could not update attributes", false); + } + } + + public static String getDisplayUsername(TaskRepository repository) { + if (!repository.hasCredentials()) { + return ITracClient.DEFAULT_USERNAME; + } + return repository.getUserName(); + } + +}
\ No newline at end of file diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryQuery.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryQuery.java new file mode 100644 index 000000000..03daac842 --- /dev/null +++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracRepositoryQuery.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2006 - 2006 Mylar eclipse.org project and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mylar project committers - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.trac.core; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.StringTokenizer; + +import org.eclipse.mylar.context.core.MylarStatusHandler; +import org.eclipse.mylar.internal.trac.core.model.TracSearch; +import org.eclipse.mylar.tasks.core.AbstractRepositoryQuery; +import org.eclipse.mylar.tasks.core.TaskList; + +/** + * @author Steffen Pingel + */ +public class TracRepositoryQuery extends AbstractRepositoryQuery { + + public TracRepositoryQuery(String repositoryUrl, String queryUrl, String description, TaskList taskList) { + super(description, taskList); + + assert queryUrl.startsWith(repositoryUrl + ITracClient.QUERY_URL); + + setRepositoryUrl(repositoryUrl); + setUrl(queryUrl); + } + + @Override + public String getRepositoryKind() { + return TracCorePlugin.REPOSITORY_KIND; + } + + public String getQueryParameter() { + String url = getUrl(); + int i = url.indexOf(ITracClient.QUERY_URL); + if (i == -1) { + return null; + } + return url.substring(i + ITracClient.QUERY_URL.length()); + } + + /** + * Creates a <code>TracSearch</code> object from this query. + */ + public TracSearch getTracSearch() { + TracSearch list = new TracSearch(); + String url = getQueryParameter(); + if (url == null) { + return list; + } + + StringTokenizer t = new StringTokenizer(url, "&"); + while (t.hasMoreTokens()) { + String token = t.nextToken(); + int i = token.indexOf("="); + if (i != -1) { + try { + String key = URLDecoder.decode(token.substring(0, i), ITracClient.CHARSET); + String value = URLDecoder.decode(token.substring(i + 1), ITracClient.CHARSET); + + if ("order".equals(key)) { + list.setOrderBy(value); + } else if ("desc".equals(key)) { + list.setAscending(!"1".equals(value)); + } else if ("group".equals(key) || "groupdesc".equals(key) || "verbose".equals(key)) { + // ignore these parameters + } else { + list.addFilter(key, value); + } + } catch (UnsupportedEncodingException e) { + MylarStatusHandler.log(e, "Unexpected exception while decoding URL"); + } + } + } + + return list; + } + +} diff --git a/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracTask.java b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracTask.java new file mode 100644 index 000000000..fabd76cd2 --- /dev/null +++ b/org.eclipse.mylyn.trac.core/src/org/eclipse/mylyn/internal/trac/core/TracTask.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * Copyright (c) 2006 - 2006 Mylar eclipse.org project and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mylar project committers - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylar.internal.trac.core; + +import org.eclipse.mylar.internal.trac.core.TracAttributeFactory.Attribute; +import org.eclipse.mylar.tasks.core.AbstractRepositoryTask; +import org.eclipse.mylar.tasks.core.RepositoryTaskAttribute; +import org.eclipse.mylar.tasks.core.Task; + +/** + * @author Steffen Pingel + */ +public class TracTask extends AbstractRepositoryTask { + + public enum PriorityLevel { + BLOCKER, CRITICAL, MAJOR, MINOR, TRIVIAL; + + @Override + public String toString() { + switch (this) { + case BLOCKER: + return "P1"; + case CRITICAL: + return "P2"; + case MAJOR: + return "P3"; + case MINOR: + return "P4"; + case TRIVIAL: + return "P5"; + default: + return "P5"; + } + } + + public static PriorityLevel fromPriority(String priority) { + if (priority == null) + return null; + if (priority.equals("blocker")) + return BLOCKER; + if (priority.equals("critical")) + return CRITICAL; + if (priority.equals("major")) + return MAJOR; + if (priority.equals("minor")) + return MINOR; + if (priority.equals("trivial")) + return TRIVIAL; + return null; + } + } + + public enum Kind { + DEFECT, ENHANCEMENT, TASK; + + @Override + public String toString() { + switch (this) { + case DEFECT: + return "Defect"; + case ENHANCEMENT: + return "Enhancement"; + case TASK: + return "Task"; + default: + return ""; + } + } + + public static Kind fromType(String type) { + if (type == null) + return null; + if (type.equals("defect")) + return DEFECT; + if (type.equals("enhancement")) + return ENHANCEMENT; + if (type.equals("task")) + return TASK; + return null; + } + + } + + public enum Status { + NEW, ASSIGNED, REOPENED, CLOSED; + + @Override + public String toString() { + switch (this) { + case NEW: + return "New"; + case ASSIGNED: + return "Assigned"; + case REOPENED: + return "Reopened"; + case CLOSED: + return "Closed"; + default: + return ""; + } + } + + public static Status fromStatus(String status) { + if (status == null) + return null; + if (status.equals("new")) + return NEW; + if (status.equals("assigned")) + return ASSIGNED; + if (status.equals("reopened")) + return REOPENED; + if (status.equals("closed")) + return CLOSED; + return null; + } + + } + + public TracTask(String handle, String label, boolean newTask) { + super(handle, label, newTask); + + setUrl(AbstractRepositoryTask.getRepositoryUrl(handle) + ITracClient.TICKET_URL + + AbstractRepositoryTask.getTaskId(handle)); + } + + @Override + public boolean isCompleted() { + if (taskData != null) { + return isCompleted(taskData.getStatus()); + } else { + return super.isCompleted(); + } + } + + @Override + public String getRepositoryKind() { + return TracCorePlugin.REPOSITORY_KIND; + } + + @Override + public String getPriority() { + if (taskData != null && taskData.getAttribute(Attribute.PRIORITY.getTracKey()) != null) { + return getMylarPriority(taskData.getAttributeValue(Attribute.PRIORITY.getTracKey())); + } else { + return super.getPriority(); + } + } + + @Override + public String getOwner() { + if (taskData != null && taskData.getAttribute(RepositoryTaskAttribute.USER_OWNER) != null) { + return taskData.getAttributeValue(RepositoryTaskAttribute.USER_OWNER); + } else { + return super.getOwner(); + } + } + + // TODO use priority attributes from repository instead of hard coded enum + public static String getMylarPriority(String tracPriority) { + if (tracPriority != null) { + PriorityLevel priority = PriorityLevel.fromPriority(tracPriority); + if (priority != null) { + return priority.toString(); + } + } + return Task.PriorityLevel.P3.toString(); + } + + public static boolean isCompleted(String tracStatus) { + TracTask.Status status = TracTask.Status.fromStatus(tracStatus); + return status == TracTask.Status.CLOSED; + } + +} |