/******************************************************************************* * Copyright (c) 2015 Tasktop Technologies. * 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: * Tasktop Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.internal.tasks.ui.migrator; import static org.eclipse.mylyn.internal.tasks.ui.migrator.TaskPredicates.isQueryForRepository; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; 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.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.window.Window; import org.eclipse.jface.wizard.Wizard; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.mylyn.commons.core.StatusHandler; import org.eclipse.mylyn.commons.net.AuthenticationCredentials; import org.eclipse.mylyn.commons.net.AuthenticationType; import org.eclipse.mylyn.commons.workbench.WorkbenchUtil; import org.eclipse.mylyn.internal.commons.notifications.feed.ServiceMessage; import org.eclipse.mylyn.internal.tasks.core.ITaskListRunnable; import org.eclipse.mylyn.internal.tasks.core.RepositoryQuery; import org.eclipse.mylyn.internal.tasks.core.UnsubmittedTaskContainer; import org.eclipse.mylyn.internal.tasks.ui.TaskListBackupManager; import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin; import org.eclipse.mylyn.internal.tasks.ui.actions.DeleteAction; import org.eclipse.mylyn.internal.tasks.ui.util.TaskDataSnapshotOperation; import org.eclipse.mylyn.internal.tasks.ui.views.TaskListView; import org.eclipse.mylyn.tasks.core.ITask; import org.eclipse.mylyn.tasks.core.TaskRepository; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.PlatformUI; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; public class ConnectorMigrationUi { private static final String MIGRATE = "migrate"; //$NON-NLS-1$ private static final String COMLETE_MIGRATION = "complete-migration"; //$NON-NLS-1$ private final TaskListView taskListView; private final TaskListBackupManager backupManager; private final TasksState tasksState; public ConnectorMigrationUi(TaskListView taskListView, TaskListBackupManager backupManager, TasksState tasksState) { this.taskListView = taskListView; this.backupManager = backupManager; this.tasksState = tasksState; } /** * @noextend This class is not intended to be subclassed by clients. * @noinstantiate This class is not intended to be instantiated by clients. */ protected class CompleteMigrationJob extends Job { private boolean finishedCompleteMigrationWizard; private final ConnectorMigrator migrator; private CompleteMigrationJob(String name, ConnectorMigrator migrator) { super(name); this.migrator = migrator; } @Override protected IStatus run(IProgressMonitor monitor) { if (!finishedCompleteMigrationWizard) { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { if (taskListView != null && taskListView.getServiceMessageControl() != null) { ServiceMessage message = new ServiceMessage("") { //$NON-NLS-1$ @Override public boolean openLink(String link) { if (link.equals(COMLETE_MIGRATION)) { if (createCompleteMigrationWizard(migrator).open() == Window.OK) { finishedCompleteMigrationWizard = true; return true; } } return false; } }; message.setTitle(Messages.ConnectorMigrationUi_Connector_Migration); message.setDescription(NLS.bind(Messages.ConnectorMigrator_complete_migration_prompt_title, COMLETE_MIGRATION)); message.setImage(Dialog.DLG_IMG_MESSAGE_WARNING); taskListView.getServiceMessageControl().setMessage(message); } } }); schedule(TimeUnit.MILLISECONDS.convert(getCompletionPromptFrequency(), TimeUnit.SECONDS)); } return Status.OK_STATUS; } public void dispose() { finishedCompleteMigrationWizard = true; } } public void promptToMigrate(final ConnectorMigrator migrator) { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { if (taskListView != null && taskListView.getServiceMessageControl() != null) { ServiceMessage message = new ServiceMessage("") { //$NON-NLS-1$ @Override public boolean openLink(String link) { if (link.equals(MIGRATE)) { if (createMigrationWizard(migrator).open() == Window.OK) { createPromptToCompleteMigrationJob(migrator).schedule(); return true; } } return false; } }; message.setTitle(Messages.ConnectorMigrationUi_End_of_Connector_Support); message.setDescription( NLS.bind(Messages.ConnectorMigrator_complete_migration_prompt_message, MIGRATE)); message.setImage(Dialog.DLG_IMG_MESSAGE_INFO); taskListView.getServiceMessageControl().setMessage(message); } } }); } protected Job createPromptToCompleteMigrationJob(ConnectorMigrator migrator) { Job job = new CompleteMigrationJob(Messages.ConnectorMigrationUi_Complete_Connector_Migration_Prompt, migrator); job.setUser(false); job.setSystem(true); return job; } protected WizardDialog createMigrationWizard(ConnectorMigrator migrator) { return createWizardDialog(new ConnectorMigrationWizard(migrator)); } protected WizardDialog createCompleteMigrationWizard(ConnectorMigrator migrator) { return createWizardDialog(new CompleteConnectorMigrationWizard(migrator)); } protected WizardDialog createWizardDialog(Wizard wizard) { WizardDialog dialog = new WizardDialog(WorkbenchUtil.getShell(), wizard); dialog.create(); dialog.setBlockOnOpen(true); return dialog; } /** * @return the frequency in seconds with which the completion prompt will be shown */ protected int getCompletionPromptFrequency() { return 5; } public void warnOfValidationFailure(final List<TaskRepository> failedValidation) { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { String repositoryList = Joiner.on("\n") //$NON-NLS-1$ .join(Iterables.transform(failedValidation, repositoryToLabel())); MessageDialog.openWarning(WorkbenchUtil.getShell(), Messages.ConnectorMigrationUi_Validation_Failed, NLS.bind(Messages.ConnectorMigrationWizard_validation_failed, repositoryList)); } }); } private static Function<TaskRepository, String> repositoryToLabel() { return new Function<TaskRepository, String>() { @Override public String apply(TaskRepository repository) { return repository.getRepositoryLabel(); } }; } protected void backupTaskList(final IProgressMonitor monitor) throws IOException { try { monitor.subTask(Messages.ConnectorMigrationUi_Backing_up_task_list); String backupFolder = TasksUiPlugin.getDefault().getBackupFolderPath(); new File(backupFolder).mkdirs(); String fileName = getBackupFileName(new Date()); new TaskDataSnapshotOperation(backupFolder, fileName).run(new SubProgressMonitor(monitor, 1)); // also take a snapshot because user might try to restore from snapshot PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { @Override public void run() { backupManager.backupNow(true); } }); monitor.worked(1); } catch (InvocationTargetException e) { throw (IOException) e.getCause(); } } protected String getBackupFileName(Date date) { // we use an underscore in the date format to prevent TaskListBackupManager from thinking this is one of its backups // and deleting it (TaskListBackupManager.MYLYN_BACKUP_REGEXP matches any string). return "connector-migration-" + new SimpleDateFormat("yyyy_MM_dd_HHmmss").format(date) + ".zip"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } /** * Deletes the given tasks and repository, and all queries associated with the repository, while preserving the * credentials of <code>newRepository</code>. * * @param newRepository */ protected void delete(final Set<ITask> tasks, final TaskRepository repository, final TaskRepository newRepository, IProgressMonitor monitor) { final Set<RepositoryQuery> queries = Sets.filter(tasksState.getTaskList().getQueries(), isQueryForRepository(repository)); final UnsubmittedTaskContainer unsubmitted = tasksState.getTaskList() .getUnsubmittedContainer(repository.getRepositoryUrl()); try { monitor.subTask(Messages.ConnectorMigrationUi_Deleting_old_repository_tasks_and_queries); try { DeleteAction.prepareDeletion(tasks); if (unsubmitted != null) { DeleteAction.prepareDeletion(unsubmitted.getChildren()); } DeleteAction.prepareDeletion(queries); } catch (Exception e) {// in case an error happens closing editors, we still want to delete StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, e.getMessage(), e)); } tasksState.getTaskList().run(new ITaskListRunnable() { @Override public void execute(IProgressMonitor monitor) throws CoreException { for (ITask task : tasks) { delete(task); } if (unsubmitted != null) { for (ITask task : unsubmitted.getChildren()) { delete(task); } } DeleteAction.performDeletion(queries); Map<AuthenticationType, AuthenticationCredentials> credentialsMap = new HashMap<>(); for (AuthenticationType type : AuthenticationType.values()) { AuthenticationCredentials credentials = repository.getCredentials(type); if (credentials != null) { credentialsMap.put(type, credentials); } } tasksState.getRepositoryManager().removeRepository(repository); for (AuthenticationType type : credentialsMap.keySet()) { newRepository.setCredentials(type, credentialsMap.get(type), newRepository.getSavePassword(type)); } } }, monitor); tasksState.getRepositoryModel().clear(); } catch (CoreException e) { StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, Messages.ConnectorMigrationUi_Error_deleting_task, e)); } } /** * Delete a task without deleting the context. */ protected void delete(ITask task) { tasksState.getTaskList().deleteTask(task); try { tasksState.getTaskDataManager().deleteTaskData(task); } catch (CoreException e) { StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Failed to delete task data", e));//$NON-NLS-1$ } } public void notifyMigrationComplete() { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { MessageDialog.openInformation(WorkbenchUtil.getShell(), Messages.ConnectorMigrationUi_Connector_Migration_Complete, Messages.ConnectorMigrationUi_Connector_migration_completed_successfully_You_may_resume_using_the_task_list); } }); } }