blob: 5ca2e3bdf2636919cb6d8b356100e11e09d5340e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2014 Tasktop Technologies and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Tasktop Technologies - initial API and implementation
* Peter Stibrany - improvements for bug 271197
*******************************************************************************/
package org.eclipse.mylyn.internal.tasks.ui.util;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.mylyn.commons.core.CoreUtil;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.commons.net.Policy;
import org.eclipse.mylyn.internal.tasks.core.ITasksCoreConstants;
import org.eclipse.mylyn.internal.tasks.core.TaskAttachment;
import org.eclipse.mylyn.internal.tasks.core.data.FileTaskAttachmentSource;
import org.eclipse.mylyn.internal.tasks.core.sync.SubmitTaskAttachmentJob;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITaskAttachment;
import org.eclipse.mylyn.tasks.core.RepositoryStatus;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.data.AbstractTaskAttachmentHandler;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import org.eclipse.mylyn.tasks.core.sync.SubmitJob;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.statushandlers.StatusManager;
/**
* @author Steffen Pingel
*/
public class AttachmentUtil {
protected static final int BUFFER_SIZE = 1024;
public static final String CONTEXT_DESCRIPTION = "mylyn/context/zip"; //$NON-NLS-1$
private static final String CONTEXT_DESCRIPTION_LEGACY = "mylar/context/zip"; //$NON-NLS-1$
private static final String CONTEXT_FILENAME = "mylyn-context.zip"; //$NON-NLS-1$
private static final String CONTEXT_CONTENT_TYPE = "application/octet-stream"; //$NON-NLS-1$
private static final String ATTACHMENT_DEFAULT_NAME = "attachment"; //$NON-NLS-1$
private static final String CTYPE_ZIP = "zip"; //$NON-NLS-1$
private static final String CTYPE_TEXT = "text"; //$NON-NLS-1$
private static final String CTYPE_HTML = "html"; //$NON-NLS-1$
public static boolean postContext(AbstractRepositoryConnector connector, TaskRepository repository, ITask task,
String comment, TaskAttribute attribute, IProgressMonitor monitor) throws CoreException {
AbstractTaskAttachmentHandler attachmentHandler = connector.getTaskAttachmentHandler();
TasksUiPlugin.getContextStore().saveActiveContext();
File file = TasksUiPlugin.getContextStore().getFileForContext(task);
if (attachmentHandler != null && file != null && file.exists()) {
FileTaskAttachmentSource attachment = new FileTaskAttachmentSource(file);
attachment.setDescription(CONTEXT_DESCRIPTION);
attachment.setName(CONTEXT_FILENAME);
attachmentHandler.postContent(repository, task, attachment, comment, attribute, monitor);
return true;
}
return false;
}
public static List<ITaskAttachment> getContextAttachments(TaskRepository repository, ITask task) {
List<ITaskAttachment> contextAttachments = new ArrayList<ITaskAttachment>();
TaskData taskData;
try {
taskData = TasksUi.getTaskDataManager().getTaskData(task);
} catch (CoreException e) {
// ignore
return contextAttachments;
}
if (taskData != null) {
List<TaskAttribute> taskAttachments = taskData.getAttributeMapper().getAttributesByType(taskData,
TaskAttribute.TYPE_ATTACHMENT);
for (TaskAttribute attribute : taskAttachments) {
TaskAttachment taskAttachment = new TaskAttachment(repository, task, attribute);
taskData.getAttributeMapper().updateTaskAttachment(taskAttachment, attribute);
if (isContext(taskAttachment)) {
contextAttachments.add(taskAttachment);
}
}
}
return contextAttachments;
}
public static boolean hasContextAttachment(ITask task) {
Assert.isNotNull(task);
TaskRepository repository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
task.getRepositoryUrl());
List<ITaskAttachment> contextAttachments = getContextAttachments(repository, task);
return contextAttachments.size() > 0;
}
public static boolean downloadContext(final ITask task, final ITaskAttachment attachment,
final IRunnableContext context) {
if (task.isActive()) {
TasksUi.getTaskActivityManager().deactivateTask(task);
}
try {
context.run(true, true, new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
File targetFile = TasksUiPlugin.getContextStore().getFileForContext(task);
try {
boolean exceptionThrown = true;
OutputStream out = new BufferedOutputStream(new FileOutputStream(targetFile));
try {
AttachmentUtil.downloadAttachment(attachment, out, monitor);
exceptionThrown = false;
} catch (CoreException e) {
throw new InvocationTargetException(e);
} finally {
out.close();
if (exceptionThrown) {
targetFile.delete();
}
}
} catch (OperationCanceledException e) {
throw new InterruptedException();
} catch (IOException e) {
throw new InvocationTargetException(
new CoreException(new RepositoryStatus(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
RepositoryStatus.ERROR_IO, "Error writing to context file", e))); //$NON-NLS-1$
}
}
});
} catch (InvocationTargetException e) {
if (e.getCause() instanceof CoreException) {
TasksUiInternal.displayStatus(Messages.AttachmentUtil_Mylyn_Information,
((CoreException) e.getCause()).getStatus());
} else {
StatusManager.getManager()
.handle(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Unexpected error while retrieving context", e), StatusManager.SHOW | StatusManager.LOG); //$NON-NLS-1$
}
return false;
} catch (InterruptedException ignored) {
// canceled
return false;
}
TasksUiInternal.getTaskList().notifyElementChanged(task);
TasksUiInternal.activateTaskThroughCommand(task);
return true;
}
public static boolean uploadContext(final TaskRepository repository, final ITask task, final String comment,
final IRunnableContext context) {
TasksUiPlugin.getContextStore().saveActiveContext();
File sourceContextFile = TasksUiPlugin.getContextStore().getFileForContext(task);
if (!sourceContextFile.exists()) {
TasksUiInternal.displayStatus(Messages.AttachmentUtil_Mylyn_Information, new Status(IStatus.WARNING,
TasksUiPlugin.ID_PLUGIN, Messages.AttachmentUtil_The_context_is_empty));
return false;
}
FileTaskAttachmentSource source = new FileTaskAttachmentSource(sourceContextFile);
source.setName(CONTEXT_FILENAME);
source.setDescription(CONTEXT_DESCRIPTION);
source.setContentType(CONTEXT_CONTENT_TYPE);
AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector(
repository.getConnectorKind());
TaskAttribute taskAttribute = null;
try {
TaskData taskData = TasksUi.getTaskDataManager().getTaskData(task);
if (taskData != null) {
taskAttribute = taskData.getRoot().createMappedAttribute(TaskAttribute.NEW_ATTACHMENT);
}
} catch (CoreException e) {
StatusHandler.log(new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN,
"Unexpected error while attaching context. Continuing with task submission.", e)); //$NON-NLS-1$
}
final SubmitJob submitJob = TasksUiInternal.getJobFactory().createSubmitTaskAttachmentJob(connector,
repository, task, source, comment, taskAttribute);
try {
context.run(true, true, new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
if (((SubmitTaskAttachmentJob) submitJob).run(monitor) == Status.CANCEL_STATUS) {
throw new InterruptedException();
}
}
});
} catch (InvocationTargetException e) {
StatusManager.getManager()
.handle(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
"Unexpected error while attaching context", e), StatusManager.SHOW | StatusManager.LOG); //$NON-NLS-1$
return false;
} catch (InterruptedException ignored) {
// canceled
return false;
}
IStatus status = submitJob.getStatus();
if (status != null && status.getSeverity() != IStatus.CANCEL) {
TasksUiInternal.displayStatus(Messages.AttachmentUtil_Mylyn_Information, submitJob.getStatus());
return false;
}
return true;
}
public static boolean hasLocalContext(ITask task) {
Assert.isNotNull(task);
return TasksUiPlugin.getContextStore().hasContext(task);
}
public static boolean isContext(ITaskAttachment attachment) {
return CONTEXT_DESCRIPTION.equals(attachment.getDescription())
|| CONTEXT_DESCRIPTION_LEGACY.equals(attachment.getDescription());
}
public static boolean canUploadAttachment(ITask task) {
TaskRepository repository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
task.getRepositoryUrl());
if (repository != null) {
AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector(
repository.getConnectorKind());
AbstractTaskAttachmentHandler attachmentHandler = connector.getTaskAttachmentHandler();
if (attachmentHandler != null) {
return attachmentHandler.canPostContent(repository, task);
}
}
return false;
}
public static boolean canDownloadAttachment(ITask task) {
TaskRepository repository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(),
task.getRepositoryUrl());
if (repository != null) {
AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector(
repository.getConnectorKind());
AbstractTaskAttachmentHandler attachmentHandler = connector.getTaskAttachmentHandler();
if (attachmentHandler != null) {
return attachmentHandler.canGetContent(repository, task);
}
}
return false;
}
public static void downloadAttachment(ITaskAttachment attachment, OutputStream out, IProgressMonitor monitor)
throws CoreException {
try {
monitor.beginTask(Messages.AttachmentUtil_Downloading_attachment, IProgressMonitor.UNKNOWN);
AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector(
attachment.getConnectorKind());
AbstractTaskAttachmentHandler handler = connector.getTaskAttachmentHandler();
if (handler == null) {
throw new CoreException(new RepositoryStatus(IStatus.INFO, TasksUiPlugin.ID_PLUGIN,
RepositoryStatus.ERROR_INTERNAL, "The repository does not support attachments.")); //$NON-NLS-1$
}
InputStream in = handler.getContent(attachment.getTaskRepository(), attachment.getTask(),
attachment.getTaskAttribute(), monitor);
try {
byte[] buffer = new byte[BUFFER_SIZE];
while (true) {
Policy.checkCanceled(monitor);
int count = in.read(buffer);
if (count == -1) {
return;
}
out.write(buffer, 0, count);
}
} catch (IOException e) {
throw new CoreException(new RepositoryStatus(attachment.getTaskRepository(), IStatus.ERROR,
TasksUiPlugin.ID_PLUGIN, RepositoryStatus.ERROR_IO, "IO error reading attachment: " //$NON-NLS-1$
+ e.getMessage(), e));
} finally {
try {
in.close();
} catch (IOException e) {
StatusHandler.log(new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN,
"Error closing attachment stream", e)); //$NON-NLS-1$
}
}
} finally {
monitor.done();
}
}
public static ITaskAttachment getSelectedAttachment() {
List<ITaskAttachment> attachments = getSelectedAttachments(null);
if (attachments.isEmpty()) {
return null;
}
return attachments.get(0);
}
public static List<ITaskAttachment> getSelectedAttachments(ExecutionEvent event) {
if (event == null) {
// create bogus execution event to obtain active menu selection
IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
if (service != null) {
event = new ExecutionEvent(null, Collections.emptyMap(), null, service.getCurrentState());
} else {
// failed
return Collections.emptyList();
}
}
// prefer local selection, e.g from table in Task Editor
ISelection selection = HandlerUtil.getActiveMenuSelection(event);
if (selection == null || selection.isEmpty()) {
selection = HandlerUtil.getCurrentSelection(event);
}
if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
List<?> items = ((IStructuredSelection) selection).toList();
List<ITaskAttachment> attachments = new ArrayList<ITaskAttachment>();
for (Object item : items) {
if (item instanceof ITaskAttachment) {
attachments.add((ITaskAttachment) item);
}
}
return attachments;
}
return Collections.emptyList();
}
/**
* @deprecated use {@link #getSelectedAttachments(ExecutionEvent)} instead
*/
@Deprecated
public static List<ITaskAttachment> getSelectedAttachments() {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window != null) {
ISelection windowSelection = window.getSelectionService().getSelection();
if (windowSelection instanceof IStructuredSelection && !windowSelection.isEmpty()) {
IStructuredSelection selection = (IStructuredSelection) windowSelection;
List<?> items = selection.toList();
List<ITaskAttachment> attachments = new ArrayList<ITaskAttachment>();
for (Object item : items) {
if (item instanceof ITaskAttachment) {
attachments.add((ITaskAttachment) item);
}
}
return attachments;
}
}
return Collections.emptyList();
}
public static String getAttachmentFilename(ITaskAttachment attachment) {
Assert.isNotNull(attachment);
String name = attachment.getFileName();
if (name != null && name.length() > 0) {
return CoreUtil.asFileName(name);
} else {
String extension = ""; //$NON-NLS-1$
// if no filename is set, make one up with the proper extension so
// we can support opening in that filetype's default editor
String ctype = attachment.getContentType();
if (ctype != null) {
if (ctype.endsWith(CTYPE_HTML)) {
extension = ".html"; //$NON-NLS-1$
} else if (ctype.startsWith(CTYPE_TEXT)) {
extension = ".txt"; //$NON-NLS-1$
} else if (ctype.endsWith(CTYPE_ZIP)) {
extension = ".zip"; //$NON-NLS-1$
} else {
int i = ctype.lastIndexOf("/"); //$NON-NLS-1$
if (i != -1) {
extension = "." + ctype.substring(i + 1); //$NON-NLS-1$
}
}
}
return ATTACHMENT_DEFAULT_NAME + extension;
}
}
}