mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
When SVN authentication is failed, suggest to clear authentication cache or change configuration directory right in the error message
for IDEA-92220 "Click to fix" link doesn't work if invalid SVN credentials was entered
This commit is contained in:
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
package com.intellij.openapi.vcs.ui;
|
||||
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationGroup;
|
||||
import com.intellij.notification.NotificationListener;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -23,11 +26,14 @@ import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.popup.Balloon;
|
||||
import com.intellij.openapi.ui.popup.BalloonBuilder;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.util.NamedRunnable;
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import java.awt.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -43,32 +49,37 @@ public class VcsBalloonProblemNotifier implements Runnable {
|
||||
private final String myMessage;
|
||||
private final MessageType myMessageType;
|
||||
private final boolean myShowOverChangesView;
|
||||
@Nullable private final NamedRunnable[] myNotificationListener;
|
||||
|
||||
public VcsBalloonProblemNotifier(@NotNull final Project project, @NotNull final String message, final MessageType messageType) {
|
||||
this(project, message, messageType, true);
|
||||
this(project, message, messageType, true, null);
|
||||
}
|
||||
|
||||
public VcsBalloonProblemNotifier(@NotNull final Project project, @NotNull final String message, final MessageType messageType, boolean showOverChangesView) {
|
||||
public VcsBalloonProblemNotifier(@NotNull final Project project, @NotNull final String message, final MessageType messageType, boolean showOverChangesView,
|
||||
@Nullable final NamedRunnable[] notificationListener) {
|
||||
myProject = project;
|
||||
myMessage = message;
|
||||
myMessageType = messageType;
|
||||
myShowOverChangesView = showOverChangesView;
|
||||
myNotificationListener = notificationListener;
|
||||
}
|
||||
|
||||
public static void showOverChangesView(@NotNull final Project project, @NotNull final String message, final MessageType type) {
|
||||
show(project, message, type, true);
|
||||
public static void showOverChangesView(@NotNull final Project project, @NotNull final String message, final MessageType type,
|
||||
final NamedRunnable... notificationListener) {
|
||||
show(project, message, type, true, notificationListener);
|
||||
}
|
||||
|
||||
public static void showOverVersionControlView(@NotNull final Project project, @NotNull final String message, final MessageType type) {
|
||||
show(project, message, type, false);
|
||||
show(project, message, type, false, null);
|
||||
}
|
||||
|
||||
private static void show(final Project project, final String message, final MessageType type, final boolean showOverChangesView) {
|
||||
private static void show(final Project project, final String message, final MessageType type, final boolean showOverChangesView,
|
||||
@Nullable final NamedRunnable[] notificationListener) {
|
||||
final Application application = ApplicationManager.getApplication();
|
||||
if (application.isHeadlessEnvironment()) return;
|
||||
final Runnable showErrorAction = new Runnable() {
|
||||
public void run() {
|
||||
new VcsBalloonProblemNotifier(project, message, type, showOverChangesView).run();
|
||||
new VcsBalloonProblemNotifier(project, message, type, showOverChangesView, notificationListener).run();
|
||||
}
|
||||
};
|
||||
if (application.isDispatchThread()) {
|
||||
@@ -80,7 +91,40 @@ public class VcsBalloonProblemNotifier implements Runnable {
|
||||
}
|
||||
|
||||
public void run() {
|
||||
NOTIFICATION_GROUP.createNotification(myMessage, myMessageType).notify(myProject.isDefault() ? null : myProject);
|
||||
final Notification notification;
|
||||
if (myNotificationListener != null && myNotificationListener.length > 0) {
|
||||
final NotificationType type = myMessageType.toNotificationType();
|
||||
final StringBuilder sb = new StringBuilder(myMessage);
|
||||
for (NamedRunnable runnable : myNotificationListener) {
|
||||
final String name = runnable.toString();
|
||||
sb.append("<br/><a href=\"").append(name).append("\">").append(name).append("</a>");
|
||||
}
|
||||
notification = NOTIFICATION_GROUP.createNotification(type.name(), sb.toString(), myMessageType.toNotificationType(),
|
||||
new NotificationListener() {
|
||||
@Override
|
||||
public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
|
||||
if (HyperlinkEvent.EventType.ACTIVATED.equals(event.getEventType())) {
|
||||
if (myNotificationListener.length == 1) {
|
||||
myNotificationListener[0].run();
|
||||
} else {
|
||||
final String description = event.getDescription();
|
||||
if (description != null) {
|
||||
for (NamedRunnable runnable : myNotificationListener) {
|
||||
if (description.equals(runnable.toString())) {
|
||||
runnable.run();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
notification.expire();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
notification = NOTIFICATION_GROUP.createNotification(myMessage, myMessageType);
|
||||
}
|
||||
notification.notify(myProject.isDefault() ? null : myProject);
|
||||
}
|
||||
|
||||
public static void showBalloonForComponent(@NotNull JComponent component, @NotNull final String message, final MessageType type,
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.popup.Balloon;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.util.NamedRunnable;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.impl.GenericNotifierImpl;
|
||||
@@ -33,6 +34,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.IdeFrame;
|
||||
import com.intellij.openapi.wm.ex.WindowManagerEx;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.ThreeState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -321,14 +323,37 @@ public class SvnAuthenticationNotifier extends GenericNotifierImpl<SvnAuthentica
|
||||
} catch (SVNCancelException e) {
|
||||
log(e); // auth canceled
|
||||
return false;
|
||||
} catch (SVNException e) {
|
||||
} catch (final SVNException e) {
|
||||
if (e.getErrorMessage().getErrorCode().isAuthentication()) {
|
||||
log(e);
|
||||
return false;
|
||||
}
|
||||
LOG.info("some other exc", e);
|
||||
if (interactive) {
|
||||
VcsBalloonProblemNotifier.showOverChangesView(project, "Authentication failed: " + e.getMessage(), MessageType.ERROR);
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VcsBalloonProblemNotifier.showOverChangesView(project, "Authentication failed: " + e.getMessage(), MessageType.ERROR,
|
||||
new NamedRunnable(SvnBundle.message("confirmation.title.clear.authentication.cache")) {
|
||||
@Override
|
||||
public void run() {
|
||||
SvnConfigurable.clearAuthenticationCache(project, null, configuration.getConfigurationDirectory());
|
||||
}
|
||||
},
|
||||
new NamedRunnable(SvnBundle.message("action.title.select.configuration.directory")) {
|
||||
@Override
|
||||
public void run() {
|
||||
SvnConfigurable.selectConfigirationDirectory(configuration.getConfigurationDirectory(),
|
||||
new Consumer<String>() {
|
||||
@Override
|
||||
public void consume(String s) {
|
||||
configuration.setConfigurationDirectory(s);
|
||||
}
|
||||
}, project, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, ModalityState.NON_MODAL, project.getDisposed());
|
||||
}
|
||||
return false; /// !!!! any exception means user should be notified that authorization failed
|
||||
}
|
||||
|
||||
@@ -154,6 +154,7 @@ status.group.name.locked=Locked
|
||||
checkbox.configure.use.system.default.configuration.directory=&Use system default Subversion configuration directory
|
||||
label.configuration.configuration.directory=Subversion configuration &directory:
|
||||
dialog.title.select.configuration.directory=Select Configuration Directory
|
||||
action.title.select.configuration.directory=Change Configuration Directory
|
||||
dialog.description.select.configuration.directory=Select Subversion configuration directory or create new one
|
||||
button.text.clear.authentication.cache=&Clear Auth Cache
|
||||
label.text.delete.stored.credentials=Delete all stored credentials for 'http', 'svn' and 'svn+ssh' protocols
|
||||
|
||||
@@ -32,8 +32,11 @@ import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.ui.MultiLineTooltipUI;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.idea.svn.config.ConfigureProxiesListener;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -93,37 +96,21 @@ public class SvnConfigurable implements Configurable {
|
||||
|
||||
myClearAuthButton.addActionListener(new ActionListener(){
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
String path = myConfigurationDirectoryText.getText();
|
||||
if (path != null) {
|
||||
int result = Messages.showYesNoDialog(myComponent, SvnBundle.message("confirmation.text.delete.stored.authentication.information"),
|
||||
SvnBundle.message("confirmation.title.clear.authentication.cache"),
|
||||
Messages.getWarningIcon());
|
||||
if (result == 0) {
|
||||
SvnConfiguration.RUNTIME_AUTH_CACHE.clear();
|
||||
SvnConfiguration.getInstance(myProject).clearAuthenticationDirectory(myProject);
|
||||
}
|
||||
}
|
||||
|
||||
clearAuthenticationCache(myProject, myComponent, myConfigurationDirectoryText.getText());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
final FileChooserDescriptor descriptor = createFileDescriptor();
|
||||
|
||||
myConfigurationDirectoryText.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
@NonNls String path = myConfigurationDirectoryText.getText().trim();
|
||||
path = "file://" + path.replace(File.separatorChar, '/');
|
||||
VirtualFile root = VirtualFileManager.getInstance().findFileByUrl(path);
|
||||
|
||||
String oldValue = PropertiesComponent.getInstance().getValue("FileChooser.showHiddens");
|
||||
PropertiesComponent.getInstance().setValue("FileChooser.showHiddens", Boolean.TRUE.toString());
|
||||
VirtualFile file = FileChooser.chooseFile(descriptor, myComponent, myProject, root);
|
||||
PropertiesComponent.getInstance().setValue("FileChooser.showHiddens", oldValue);
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
myConfigurationDirectoryText.setText(file.getPath().replace('/', File.separatorChar));
|
||||
selectConfigirationDirectory(path, new Consumer<String>() {
|
||||
@Override
|
||||
public void consume(String s) {
|
||||
myConfigurationDirectoryText.setText(s);
|
||||
}
|
||||
}, myProject, myComponent);
|
||||
}
|
||||
});
|
||||
myConfigurationDirectoryText.setEditable(false);
|
||||
@@ -150,6 +137,42 @@ public class SvnConfigurable implements Configurable {
|
||||
myJavaHLInfo.setVisible(internal);
|
||||
}
|
||||
|
||||
public static void selectConfigirationDirectory(@NotNull String path, @NotNull final Consumer<String> dirConsumer,
|
||||
final Project project, @Nullable final Component component) {
|
||||
final FileChooserDescriptor descriptor = createFileDescriptor();
|
||||
path = "file://" + path.replace(File.separatorChar, '/');
|
||||
VirtualFile root = VirtualFileManager.getInstance().findFileByUrl(path);
|
||||
|
||||
String oldValue = PropertiesComponent.getInstance().getValue("FileChooser.showHiddens");
|
||||
PropertiesComponent.getInstance().setValue("FileChooser.showHiddens", Boolean.TRUE.toString());
|
||||
VirtualFile file = FileChooser.chooseFile(descriptor, component, project, root);
|
||||
PropertiesComponent.getInstance().setValue("FileChooser.showHiddens", oldValue);
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
final String resultPath = file.getPath().replace('/', File.separatorChar);
|
||||
dirConsumer.consume(resultPath);
|
||||
}
|
||||
|
||||
public static void clearAuthenticationCache(@NotNull final Project project, final Component component, final String configDirPath) {
|
||||
if (configDirPath != null) {
|
||||
int result;
|
||||
if (component == null) {
|
||||
result = Messages.showYesNoDialog(project, SvnBundle.message("confirmation.text.delete.stored.authentication.information"),
|
||||
SvnBundle.message("confirmation.title.clear.authentication.cache"),
|
||||
Messages.getWarningIcon());
|
||||
} else {
|
||||
result = Messages.showYesNoDialog(component, SvnBundle.message("confirmation.text.delete.stored.authentication.information"),
|
||||
SvnBundle.message("confirmation.title.clear.authentication.cache"),
|
||||
Messages.getWarningIcon());
|
||||
}
|
||||
if (result == 0) {
|
||||
SvnConfiguration.RUNTIME_AUTH_CACHE.clear();
|
||||
SvnConfiguration.getInstance(project).clearAuthenticationDirectory(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static FileChooserDescriptor createFileDescriptor() {
|
||||
final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
|
||||
descriptor.setShowFileSystemRoots(true);
|
||||
|
||||
@@ -488,7 +488,7 @@ public class SvnConfiguration implements PersistentStateComponent<Element> {
|
||||
|
||||
public static class AuthStorage implements ISVNAuthenticationStorage {
|
||||
|
||||
private final Map<String, Object> myStorage = new Hashtable<String, Object>();
|
||||
private final Map<String, Object> myStorage = Collections.synchronizedMap(new HashMap<String, Object>());
|
||||
|
||||
public void clear() {
|
||||
myStorage.clear();
|
||||
|
||||
Reference in New Issue
Block a user