From 6693c4017d6d3f1b2526a92bc14871785a213daf Mon Sep 17 00:00:00 2001 From: John Phillips Date: Thu, 11 May 2023 21:17:04 -0400 Subject: [PATCH 1/4] Project level settings Settings are stored per project. New projects load from the default settings. Settings panel has buttons to save to and load from the default settings. --- .../plugin/gerrit/GerritSettings.java | 275 +++++++++++------- .../plugin/gerrit/GerritSettingsData.java | 209 +++++++++++++ .../plugin/gerrit/ui/SettingsPanel.form | 41 ++- .../plugin/gerrit/ui/SettingsPanel.java | 49 ++++ 4 files changed, 465 insertions(+), 109 deletions(-) create mode 100644 src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java diff --git a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java index fd01fa51..44efa586 100644 --- a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java +++ b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java @@ -17,22 +17,33 @@ package com.urswolfer.intellij.plugin.gerrit; -import com.google.common.base.Optional; import com.google.common.base.Strings; import com.intellij.credentialStore.CredentialAttributes; import com.intellij.credentialStore.Credentials; +import com.intellij.ide.DataManager; import com.intellij.ide.passwordSafe.PasswordSafe; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; import com.urswolfer.gerrit.client.rest.GerritAuthData; import com.urswolfer.intellij.plugin.gerrit.ui.ShowProjectColumn; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; + /** * Parts based on org.jetbrains.plugins.github.GithubSettings * @@ -43,126 +54,154 @@ public class GerritSettings implements PersistentStateComponent, GerritAuthData { private static final String GERRIT_SETTINGS_TAG = "GerritSettings"; - private static final String LOGIN = "Login"; - private static final String HOST = "Host"; - private static final String AUTOMATIC_REFRESH = "AutomaticRefresh"; - private static final String LIST_ALL_CHANGES = "ListAllChanges"; - private static final String REFRESH_TIMEOUT = "RefreshTimeout"; - private static final String REVIEW_NOTIFICATIONS = "ReviewNotifications"; - private static final String PUSH_TO_GERRIT = "PushToGerrit"; - private static final String SHOW_CHANGE_NUMBER_COLUMN = "ShowChangeNumberColumn"; - private static final String SHOW_CHANGE_ID_COLUMN = "ShowChangeIdColumn"; - private static final String SHOW_TOPIC_COLUMN = "ShowTopicColumn"; - private static final String SHOW_PROJECT_COLUMN = "ShowProjectColumn"; - private static final String CLONE_BASE_URL = "CloneBaseUrl"; private static final String GERRIT_SETTINGS_PASSWORD_KEY = "GERRIT_SETTINGS_PASSWORD_KEY"; private static final CredentialAttributes CREDENTIAL_ATTRIBUTES = new CredentialAttributes(GerritSettings.class.getName(), GERRIT_SETTINGS_PASSWORD_KEY); - private String login = ""; - private String host = ""; - private boolean listAllChanges = false; - private boolean automaticRefresh = true; - private int refreshTimeout = 15; - private boolean refreshNotifications = true; - private boolean pushToGerrit = false; - private boolean showChangeNumberColumn = false; - private boolean showChangeIdColumn = false; - private boolean showTopicColumn = false; - private ShowProjectColumn showProjectColumn = ShowProjectColumn.AUTO; - private String cloneBaseUrl = ""; - - private Optional preloadedPassword; + private Map projectCredentialAttributes = new HashMap(); + + private Map preloadedPasswords = new HashMap(); + private Map projectSettings = new HashMap(); private Logger log; public Element getState() { - final Element element = new Element(GERRIT_SETTINGS_TAG); - element.setAttribute(LOGIN, (getLogin() != null ? getLogin() : "")); - element.setAttribute(HOST, (getHost() != null ? getHost() : "")); - element.setAttribute(LIST_ALL_CHANGES, Boolean.toString(getListAllChanges())); - element.setAttribute(AUTOMATIC_REFRESH, Boolean.toString(getAutomaticRefresh())); - element.setAttribute(REFRESH_TIMEOUT, Integer.toString(getRefreshTimeout())); - element.setAttribute(REVIEW_NOTIFICATIONS, Boolean.toString(getReviewNotifications())); - element.setAttribute(PUSH_TO_GERRIT, Boolean.toString(getPushToGerrit())); - element.setAttribute(SHOW_CHANGE_NUMBER_COLUMN, Boolean.toString(getShowChangeNumberColumn())); - element.setAttribute(SHOW_CHANGE_ID_COLUMN, Boolean.toString(getShowChangeIdColumn())); - element.setAttribute(SHOW_TOPIC_COLUMN, Boolean.toString(getShowTopicColumn())); - element.setAttribute(SHOW_PROJECT_COLUMN, getShowProjectColumn().name()); - element.setAttribute(CLONE_BASE_URL, (getCloneBaseUrl() != null ? getCloneBaseUrl() : "")); + // Handle global settings separately for backwards compatibility + GerritSettingsData globalSettings = projectSettings.get(GERRIT_SETTINGS_TAG); + final Element element = globalSettings.getAsElement(GERRIT_SETTINGS_TAG); + + // If we don't have project settings yet, default to global settings. + String currentProjectName = getCurrentProjectName(); + if(!currentProjectName.isEmpty()) { + if(!projectSettings.containsKey(currentProjectName)) { + addProjectSettingsFromDefault(currentProjectName); + } + } + + Element projects = new Element("Projects"); + projectSettings.forEach((projectName, settings) -> { + if(projectName!=null && !projectName.equals(GERRIT_SETTINGS_TAG)) { + final Element projectElement = settings.getAsElement(projectName); + projects.addContent(projectElement); + } + }); + element.addContent(projects); + return element; } public void loadState(@NotNull final Element element) { // All the logic on retrieving password was moved to getPassword action to cleanup initialization process try { - setLogin(element.getAttributeValue(LOGIN)); - setHost(element.getAttributeValue(HOST)); - - setListAllChanges(getBooleanValue(element, LIST_ALL_CHANGES)); - setAutomaticRefresh(getBooleanValue(element, AUTOMATIC_REFRESH)); - setRefreshTimeout(getIntegerValue(element, REFRESH_TIMEOUT)); - setReviewNotifications(getBooleanValue(element, REVIEW_NOTIFICATIONS)); - setPushToGerrit(getBooleanValue(element, PUSH_TO_GERRIT)); - setShowChangeNumberColumn(getBooleanValue(element, SHOW_CHANGE_NUMBER_COLUMN)); - setShowChangeIdColumn(getBooleanValue(element, SHOW_CHANGE_ID_COLUMN)); - setShowTopicColumn(getBooleanValue(element, SHOW_TOPIC_COLUMN)); - setShowProjectColumn(getShowProjectColumnValue(element, SHOW_PROJECT_COLUMN)); - setCloneBaseUrl(element.getAttributeValue(CLONE_BASE_URL)); + // Load global settings + projectSettings.put(GERRIT_SETTINGS_TAG, new GerritSettingsData(element, log)); + + List projectSettingsElements = element.getChild("Projects").getChildren(); + for (Element projectSettingsElement : projectSettingsElements) { + String projectName = projectSettingsElement.getAttributeValue("name"); + if(!projectName.isEmpty()) { + projectSettings.put(projectName, new GerritSettingsData(projectSettingsElement, log)); + } + } + } catch (Exception e) { log.error("Error happened while loading gerrit settings: " + e); } } - private boolean getBooleanValue(Element element, String attributeName) { - String attributeValue = element.getAttributeValue(attributeName); - if (attributeValue != null) { - return Boolean.valueOf(attributeValue); - } else { - return false; + private String getCurrentProjectName() { + Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); + if(openProjects.length == 1) { + return openProjects[0].getName(); + } + + // If there are multiple projects open, try to get the project from the focus. + try{ + DataContext context = DataManager.getInstance().getDataContextFromFocusAsync().blockingGet(200); + if(context != null) { + Project currentProject = context.getData(CommonDataKeys.PROJECT); + if(currentProject != null) { + return currentProject.getName(); + } + } + } catch (Exception e) { + // Project may just not be loaded yet. } + return ""; } - private int getIntegerValue(Element element, String attributeName) { - String attributeValue = element.getAttributeValue(attributeName); - if (attributeValue != null) { - return Integer.valueOf(attributeValue); - } else { - return 0; + private void addProjectSettingsFromDefault(String projectName){ + // Default to global settings + Element globalSettings = getDefaultSettings().getAsElement(GERRIT_SETTINGS_TAG); + projectSettings.put(projectName, new GerritSettingsData(globalSettings, log)); + setPassword(getDefaultPassword()); + } + + public GerritSettingsData getDefaultSettings(){ + return projectSettings.get(GERRIT_SETTINGS_TAG); + } + + public T getForCurrentProject(Function method) { + String projectName = getCurrentProjectName(); + if(!projectName.isEmpty()) { + GerritSettingsData settings = projectSettings.get(projectName); + return method.apply(settings); + } + return null; + } + public void setForCurrentProject(Consumer method) { + String projectName = getCurrentProjectName(); + if(!projectName.isEmpty()) { + GerritSettingsData settings = projectSettings.get(projectName); + method.accept(settings); } } - private ShowProjectColumn getShowProjectColumnValue(Element element, String attributeName) { - String attributeValue = element.getAttributeValue(attributeName); - if (attributeValue != null) { - return ShowProjectColumn.valueOf(attributeValue); - } else { - return ShowProjectColumn.AUTO; + private CredentialAttributes getProjectCredentialAttributes() { + String projectName = getCurrentProjectName(); + if(!projectName.isEmpty()) { + String passwordKey = GERRIT_SETTINGS_PASSWORD_KEY + "_" +Base64.getEncoder().encodeToString(projectName.getBytes()); + return new CredentialAttributes(GerritSettings.class.getName(), passwordKey); } + return null; + } + + public String getDefaultPassword() { + Credentials credentials = PasswordSafe.getInstance().get(CREDENTIAL_ATTRIBUTES); + return credentials != null ? credentials.getPasswordAsString() : ""; + } + + public void setDefaultPassword(String password) { + PasswordSafe.getInstance().set(CREDENTIAL_ATTRIBUTES, new Credentials(null, password)); } @Override @Nullable public String getLogin() { - return login; + return getForCurrentProject(GerritSettingsData::getLogin); } @Override @NotNull public String getPassword() { if (!ApplicationManager.getApplication().isDispatchThread()) { - if (preloadedPassword == null) { + if (preloadedPasswords.get(getCurrentProjectName()) == null) { throw new IllegalStateException("Need to call #preloadPassword when password is required in background thread"); } } else { preloadPassword(); } - return preloadedPassword.or(""); + String password = preloadedPasswords.get(getCurrentProjectName()); + return password != null ? password : ""; } public void preloadPassword() { - Credentials credentials = PasswordSafe.getInstance().get(CREDENTIAL_ATTRIBUTES); + Credentials credentials = null; + CredentialAttributes projectAttributes = getProjectCredentialAttributes(); + if(projectAttributes != null) { + credentials = PasswordSafe.getInstance().get(projectAttributes); + } String password = credentials != null ? credentials.getPasswordAsString() : null; - preloadedPassword = Optional.fromNullable(password); + preloadedPasswords.put(getCurrentProjectName(), password); } @Override @@ -172,7 +211,8 @@ public boolean isHttpPassword() { @Override public String getHost() { - return host; + String host = getForCurrentProject(GerritSettingsData::getHost); + return host != null ? host : ""; } @Override @@ -181,31 +221,40 @@ public boolean isLoginAndPasswordAvailable() { } public boolean getListAllChanges() { - return listAllChanges; + return getForCurrentProject(GerritSettingsData::getListAllChanges); } public void setListAllChanges(boolean listAllChanges) { - this.listAllChanges = listAllChanges; + setForCurrentProject(settings -> { + settings.setListAllChanges(listAllChanges); + }); } public boolean getAutomaticRefresh() { - return automaticRefresh; + return getForCurrentProject(GerritSettingsData::getAutomaticRefresh); } public int getRefreshTimeout() { - return refreshTimeout; + return getForCurrentProject(GerritSettingsData::getRefreshTimeout); } public boolean getReviewNotifications() { - return refreshNotifications; + return getForCurrentProject(GerritSettingsData::getReviewNotifications); } public void setLogin(final String login) { - this.login = login != null ? login : ""; + setForCurrentProject(settings -> { + settings.setLogin(login); + }); } public void setPassword(final String password) { - PasswordSafe.getInstance().set(CREDENTIAL_ATTRIBUTES, new Credentials(null, password != null ? password : "")); + CredentialAttributes projectAttributes = getProjectCredentialAttributes(); + if(projectAttributes != null) { + PasswordSafe.getInstance().set(projectAttributes, new Credentials(null, password != null ? password : "")); + } else { + log.error("Unable to load credentials to set password"); + } } public void forgetPassword() { @@ -213,74 +262,98 @@ public void forgetPassword() { } public void setHost(final String host) { - this.host = host; + setForCurrentProject(settings -> { + settings.setHost(host); + }); } public void setAutomaticRefresh(final boolean automaticRefresh) { - this.automaticRefresh = automaticRefresh; + setForCurrentProject(settings -> { + settings.setAutomaticRefresh(automaticRefresh); + }); } public void setRefreshTimeout(final int refreshTimeout) { - this.refreshTimeout = refreshTimeout; + setForCurrentProject(settings -> { + settings.setRefreshTimeout(refreshTimeout); + }); } public void setReviewNotifications(final boolean reviewNotifications) { - refreshNotifications = reviewNotifications; + setForCurrentProject(settings -> { + settings.setReviewNotifications(reviewNotifications); + }); } public void setPushToGerrit(boolean pushToGerrit) { - this.pushToGerrit = pushToGerrit; + setForCurrentProject(settings -> { + settings.setPushToGerrit(pushToGerrit); + }); } public boolean getPushToGerrit() { - return pushToGerrit; + return getForCurrentProject(GerritSettingsData::getPushToGerrit); } public boolean getShowChangeNumberColumn() { - return showChangeNumberColumn; + return getForCurrentProject(GerritSettingsData::getShowChangeNumberColumn); } public void setShowChangeNumberColumn(boolean showChangeNumberColumn) { - this.showChangeNumberColumn = showChangeNumberColumn; + setForCurrentProject(settings -> { + settings.setShowChangeNumberColumn(showChangeNumberColumn); + }); } public boolean getShowChangeIdColumn() { - return showChangeIdColumn; + return getForCurrentProject(GerritSettingsData::getShowChangeIdColumn); } public void setShowChangeIdColumn(boolean showChangeIdColumn) { - this.showChangeIdColumn = showChangeIdColumn; + setForCurrentProject(settings -> { + settings.setShowChangeIdColumn(showChangeIdColumn); + }); } public boolean getShowTopicColumn() { - return showTopicColumn; + return getForCurrentProject(GerritSettingsData::getShowTopicColumn); } public ShowProjectColumn getShowProjectColumn() { - return showProjectColumn; + return getForCurrentProject(GerritSettingsData::getShowProjectColumn); } public void setShowProjectColumn(ShowProjectColumn showProjectColumn) { - this.showProjectColumn = showProjectColumn; + setForCurrentProject(settings -> { + settings.setShowProjectColumn(showProjectColumn); + }); } public void setShowTopicColumn(boolean showTopicColumn) { - this.showTopicColumn = showTopicColumn; + setForCurrentProject(settings -> { + settings.setShowTopicColumn(showTopicColumn); + }); } public void setCloneBaseUrl(String cloneBaseUrl) { - this.cloneBaseUrl = cloneBaseUrl; + setForCurrentProject(settings -> { + settings.setCloneBaseUrl(cloneBaseUrl); + }); } public String getCloneBaseUrl() { - return cloneBaseUrl; + return getForCurrentProject(GerritSettingsData::getCloneBaseUrl); } public void setLog(Logger log) { + // Set for both here and the current project this.log = log; + setForCurrentProject(settings -> { + settings.setLog(log); + }); } public String getCloneBaseUrlOrHost() { - return Strings.isNullOrEmpty(cloneBaseUrl) ? host : cloneBaseUrl; + return getForCurrentProject(GerritSettingsData::getCloneBaseUrlOrHost); } } diff --git a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java new file mode 100644 index 00000000..66cfa96e --- /dev/null +++ b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java @@ -0,0 +1,209 @@ +package com.urswolfer.intellij.plugin.gerrit; + +import com.google.common.base.Strings; +import com.intellij.openapi.diagnostic.Logger; +import com.urswolfer.intellij.plugin.gerrit.ui.ShowProjectColumn; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class GerritSettingsData { + private static final String LOGIN = "Login"; + private static final String HOST = "Host"; + private static final String AUTOMATIC_REFRESH = "AutomaticRefresh"; + private static final String LIST_ALL_CHANGES = "ListAllChanges"; + private static final String REFRESH_TIMEOUT = "RefreshTimeout"; + private static final String REVIEW_NOTIFICATIONS = "ReviewNotifications"; + private static final String PUSH_TO_GERRIT = "PushToGerrit"; + private static final String SHOW_CHANGE_NUMBER_COLUMN = "ShowChangeNumberColumn"; + private static final String SHOW_CHANGE_ID_COLUMN = "ShowChangeIdColumn"; + private static final String SHOW_TOPIC_COLUMN = "ShowTopicColumn"; + private static final String SHOW_PROJECT_COLUMN = "ShowProjectColumn"; + private static final String CLONE_BASE_URL = "CloneBaseUrl"; + + private String login = ""; + private String host = ""; + private boolean listAllChanges = false; + private boolean automaticRefresh = true; + private int refreshTimeout = 15; + private boolean refreshNotifications = true; + private boolean pushToGerrit = false; + private boolean showChangeNumberColumn = false; + private boolean showChangeIdColumn = false; + private boolean showTopicColumn = false; + private ShowProjectColumn showProjectColumn = ShowProjectColumn.AUTO; + private String cloneBaseUrl = ""; + + private Logger logger; + + public GerritSettingsData(@NotNull final Element element, Logger log) { + this.logger = log; + + try { + setLogin(element.getAttributeValue(LOGIN)); + setHost(element.getAttributeValue(HOST)); + + setListAllChanges(getBooleanValue(element, LIST_ALL_CHANGES)); + setAutomaticRefresh(getBooleanValue(element, AUTOMATIC_REFRESH)); + setRefreshTimeout(getIntegerValue(element, REFRESH_TIMEOUT)); + setReviewNotifications(getBooleanValue(element, REVIEW_NOTIFICATIONS)); + setPushToGerrit(getBooleanValue(element, PUSH_TO_GERRIT)); + setShowChangeNumberColumn(getBooleanValue(element, SHOW_CHANGE_NUMBER_COLUMN)); + setShowChangeIdColumn(getBooleanValue(element, SHOW_CHANGE_ID_COLUMN)); + setShowTopicColumn(getBooleanValue(element, SHOW_TOPIC_COLUMN)); + setShowProjectColumn(getShowProjectColumnValue(element, SHOW_PROJECT_COLUMN)); + setCloneBaseUrl(element.getAttributeValue(CLONE_BASE_URL)); + } catch (Exception e) { + logger.error("Error happened while loading gerrit settings: " + e); + } + } + + private boolean getBooleanValue(Element element, String attributeName) { + String attributeValue = element.getAttributeValue(attributeName); + if (attributeValue != null) { + return Boolean.valueOf(attributeValue); + } else { + return false; + } + } + + private int getIntegerValue(Element element, String attributeName) { + String attributeValue = element.getAttributeValue(attributeName); + if (attributeValue != null) { + return Integer.valueOf(attributeValue); + } else { + return 0; + } + } + + private ShowProjectColumn getShowProjectColumnValue(Element element, String attributeName) { + String attributeValue = element.getAttributeValue(attributeName); + if (attributeValue != null) { + return ShowProjectColumn.valueOf(attributeValue); + } else { + return ShowProjectColumn.AUTO; + } + } + + @Nullable + public String getLogin() { + return login; + } + + public String getHost() { + return host; + } + + public boolean getListAllChanges() { + return listAllChanges; + } + + public void setListAllChanges(boolean listAllChanges) { + this.listAllChanges = listAllChanges; + } + + public boolean getAutomaticRefresh() { + return automaticRefresh; + } + + public int getRefreshTimeout() { + return refreshTimeout; + } + + public boolean getReviewNotifications() { + return refreshNotifications; + } + + public void setLogin(final String login) { + this.login = login != null ? login : ""; + } + + public void setHost(final String host) { + this.host = host; + } + + public void setAutomaticRefresh(final boolean automaticRefresh) { + this.automaticRefresh = automaticRefresh; + } + + public void setRefreshTimeout(final int refreshTimeout) { + this.refreshTimeout = refreshTimeout; + } + + public void setReviewNotifications(final boolean reviewNotifications) { + refreshNotifications = reviewNotifications; + } + + public void setPushToGerrit(boolean pushToGerrit) { + this.pushToGerrit = pushToGerrit; + } + + public boolean getPushToGerrit() { + return pushToGerrit; + } + + public boolean getShowChangeNumberColumn() { + return showChangeNumberColumn; + } + + public void setShowChangeNumberColumn(boolean showChangeNumberColumn) { + this.showChangeNumberColumn = showChangeNumberColumn; + } + + public boolean getShowChangeIdColumn() { + return showChangeIdColumn; + } + + public void setShowChangeIdColumn(boolean showChangeIdColumn) { + this.showChangeIdColumn = showChangeIdColumn; + } + + public boolean getShowTopicColumn() { + return showTopicColumn; + } + + public ShowProjectColumn getShowProjectColumn() { + return showProjectColumn; + } + + public void setShowProjectColumn(ShowProjectColumn showProjectColumn) { + this.showProjectColumn = showProjectColumn; + } + + public void setShowTopicColumn(boolean showTopicColumn) { + this.showTopicColumn = showTopicColumn; + } + + public void setCloneBaseUrl(String cloneBaseUrl) { + this.cloneBaseUrl = cloneBaseUrl; + } + + public String getCloneBaseUrl() { + return cloneBaseUrl; + } + + public String getCloneBaseUrlOrHost() { + return Strings.isNullOrEmpty(cloneBaseUrl) ? host : cloneBaseUrl; + } + + public void setLog(Logger log) { + this.logger = log; + } + + public Element getAsElement(String elementName){ + final Element element = new Element(elementName); + element.setAttribute(LOGIN, (getLogin() != null ? getLogin() : "")); + element.setAttribute(HOST, (getHost() != null ? getHost() : "")); + element.setAttribute(LIST_ALL_CHANGES, Boolean.toString(getListAllChanges())); + element.setAttribute(AUTOMATIC_REFRESH, Boolean.toString(getAutomaticRefresh())); + element.setAttribute(REFRESH_TIMEOUT, Integer.toString(getRefreshTimeout())); + element.setAttribute(REVIEW_NOTIFICATIONS, Boolean.toString(getReviewNotifications())); + element.setAttribute(PUSH_TO_GERRIT, Boolean.toString(getPushToGerrit())); + element.setAttribute(SHOW_CHANGE_NUMBER_COLUMN, Boolean.toString(getShowChangeNumberColumn())); + element.setAttribute(SHOW_CHANGE_ID_COLUMN, Boolean.toString(getShowChangeIdColumn())); + element.setAttribute(SHOW_TOPIC_COLUMN, Boolean.toString(getShowTopicColumn())); + element.setAttribute(SHOW_PROJECT_COLUMN, getShowProjectColumn().name()); + element.setAttribute(CLONE_BASE_URL, (getCloneBaseUrl() != null ? getCloneBaseUrl() : "")); + return element; + } +} diff --git a/src/main/java/com/urswolfer/intellij/plugin/gerrit/ui/SettingsPanel.form b/src/main/java/com/urswolfer/intellij/plugin/gerrit/ui/SettingsPanel.form index 0db70d1e..b61f583c 100644 --- a/src/main/java/com/urswolfer/intellij/plugin/gerrit/ui/SettingsPanel.form +++ b/src/main/java/com/urswolfer/intellij/plugin/gerrit/ui/SettingsPanel.form @@ -3,7 +3,7 @@ - + @@ -22,9 +22,6 @@ - - - @@ -100,15 +97,12 @@ - + - - - @@ -288,6 +282,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/urswolfer/intellij/plugin/gerrit/ui/SettingsPanel.java b/src/main/java/com/urswolfer/intellij/plugin/gerrit/ui/SettingsPanel.java index e0c78698..bd76ffef 100644 --- a/src/main/java/com/urswolfer/intellij/plugin/gerrit/ui/SettingsPanel.java +++ b/src/main/java/com/urswolfer/intellij/plugin/gerrit/ui/SettingsPanel.java @@ -28,6 +28,7 @@ import com.intellij.ui.components.JBTextField; import com.urswolfer.gerrit.client.rest.GerritAuthData; import com.urswolfer.intellij.plugin.gerrit.GerritSettings; +import com.urswolfer.intellij.plugin.gerrit.GerritSettingsData; import com.urswolfer.intellij.plugin.gerrit.rest.GerritUtil; import javax.swing.*; @@ -50,6 +51,8 @@ public class SettingsPanel { private JTextPane gerritLoginInfoTextField; private JPanel loginPane; private JButton testButton; + private JButton setDefaultsButton; + private JButton loadDefaultsButton; private JBTextField hostTextField; private JSpinner refreshTimeoutSpinner; private JPanel settingsPane; @@ -108,6 +111,52 @@ public boolean isLoginAndPasswordAvailable() { } }); + setDefaultsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // TODO add confirmation dialog? + // Save these settings as the default settings + GerritSettingsData defaultSettings = gerritSettings.getDefaultSettings(); + String password = isPasswordModified() ? getPassword() : gerritSettings.getPassword(); + gerritSettings.setDefaultPassword(password); + defaultSettings.setLogin(getLogin()); + defaultSettings.setHost(getHost()); + defaultSettings.setRefreshTimeout(getRefreshTimeout()); + defaultSettings.setReviewNotifications(getReviewNotifications()); + defaultSettings.setAutomaticRefresh(getAutomaticRefresh()); + defaultSettings.setListAllChanges(getListAllChanges()); + defaultSettings.setPushToGerrit(getPushToGerrit()); + defaultSettings.setShowChangeNumberColumn(getShowChangeNumberColumn()); + defaultSettings.setShowChangeIdColumn(getShowChangeIdColumn()); + defaultSettings.setShowTopicColumn(getShowTopicColumn()); + defaultSettings.setShowProjectColumn(getShowProjectColumn()); + defaultSettings.setCloneBaseUrl(getCloneBaseUrl()); + Messages.showInfoMessage(pane, "Default settings updated", "Success"); + } + }); + loadDefaultsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // TODO add confirmation dialog? + // Load default settings + GerritSettingsData defaultSettings = gerritSettings.getDefaultSettings(); + loginTextField.setText(defaultSettings.getLogin()); + passwordField.setText(gerritSettings.getDefaultPassword()); + hostTextField.setText(defaultSettings.getHost()); + refreshTimeoutSpinner.setValue(defaultSettings.getRefreshTimeout()); + notificationOnNewReviewsCheckbox.setSelected(defaultSettings.getReviewNotifications()); + automaticRefreshCheckbox.setSelected(defaultSettings.getAutomaticRefresh()); + listAllChangesCheckbox.setSelected(defaultSettings.getListAllChanges()); + pushToGerritCheckbox.setSelected(defaultSettings.getPushToGerrit()); + showChangeNumberColumnCheckBox.setSelected(defaultSettings.getShowChangeNumberColumn()); + showChangeIdColumnCheckBox.setSelected(defaultSettings.getShowChangeIdColumn()); + showTopicColumnCheckBox.setSelected(defaultSettings.getShowTopicColumn()); + showProjectColumnComboBox.setSelectedItem(defaultSettings.getShowProjectColumn()); + cloneBaseUrlTextField.setText(defaultSettings.getCloneBaseUrl()); + Messages.showInfoMessage(pane, "Reset project settings to match default", "Success"); + } + }); + hostTextField.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { From d742be0098e79f859ecd76e6d321cacf3ee20c07 Mon Sep 17 00:00:00 2001 From: John Phillips Date: Thu, 11 May 2023 23:55:32 -0400 Subject: [PATCH 2/4] Password persistence Fixed an issue with credentials in PasswordSafe. Added a few null safety checks. --- .../plugin/gerrit/GerritSettings.java | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java index 44efa586..93e61070 100644 --- a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java +++ b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java @@ -33,9 +33,11 @@ import com.intellij.openapi.project.ProjectManager; import com.urswolfer.gerrit.client.rest.GerritAuthData; import com.urswolfer.intellij.plugin.gerrit.ui.ShowProjectColumn; +import org.apache.commons.lang.BooleanUtils; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.roaringbitmap.longlong.IntegerUtil; import java.util.Base64; import java.util.HashMap; @@ -95,11 +97,14 @@ public void loadState(@NotNull final Element element) { // Load global settings projectSettings.put(GERRIT_SETTINGS_TAG, new GerritSettingsData(element, log)); - List projectSettingsElements = element.getChild("Projects").getChildren(); - for (Element projectSettingsElement : projectSettingsElements) { - String projectName = projectSettingsElement.getAttributeValue("name"); - if(!projectName.isEmpty()) { - projectSettings.put(projectName, new GerritSettingsData(projectSettingsElement, log)); + Element projectDom = element.getChild("Projects"); + if(projectDom != null) { + List projectSettingsElements = projectDom.getChildren(); + for (Element projectSettingsElement : projectSettingsElements) { + String projectName = projectSettingsElement.getName(); + if (!projectName.isEmpty()) { + projectSettings.put(projectName, new GerritSettingsData(projectSettingsElement, log)); + } } } @@ -144,7 +149,9 @@ public T getForCurrentProject(Function method) { String projectName = getCurrentProjectName(); if(!projectName.isEmpty()) { GerritSettingsData settings = projectSettings.get(projectName); - return method.apply(settings); + if(settings != null) { + return method.apply(settings); + } } return null; } @@ -152,15 +159,18 @@ public void setForCurrentProject(Consumer method) { String projectName = getCurrentProjectName(); if(!projectName.isEmpty()) { GerritSettingsData settings = projectSettings.get(projectName); - method.accept(settings); + if(settings != null) { + method.accept(settings); + } } } private CredentialAttributes getProjectCredentialAttributes() { String projectName = getCurrentProjectName(); if(!projectName.isEmpty()) { - String passwordKey = GERRIT_SETTINGS_PASSWORD_KEY + "_" +Base64.getEncoder().encodeToString(projectName.getBytes()); - return new CredentialAttributes(GerritSettings.class.getName(), passwordKey); + String passwordKey = Base64.getEncoder().encodeToString(projectName.getBytes()).replace("=", ""); + // Can't use the same service name or PasswordSafe will overwrite the password. + return new CredentialAttributes(passwordKey, passwordKey); } return null; } @@ -221,7 +231,7 @@ public boolean isLoginAndPasswordAvailable() { } public boolean getListAllChanges() { - return getForCurrentProject(GerritSettingsData::getListAllChanges); + return BooleanUtils.isTrue(getForCurrentProject(GerritSettingsData::getListAllChanges)); } public void setListAllChanges(boolean listAllChanges) { @@ -231,15 +241,16 @@ public void setListAllChanges(boolean listAllChanges) { } public boolean getAutomaticRefresh() { - return getForCurrentProject(GerritSettingsData::getAutomaticRefresh); + return BooleanUtils.isTrue(getForCurrentProject(GerritSettingsData::getAutomaticRefresh)); } public int getRefreshTimeout() { - return getForCurrentProject(GerritSettingsData::getRefreshTimeout); + Integer timeout = getForCurrentProject(GerritSettingsData::getRefreshTimeout); + return timeout != null ? timeout : 0; } public boolean getReviewNotifications() { - return getForCurrentProject(GerritSettingsData::getReviewNotifications); + return BooleanUtils.isTrue(getForCurrentProject(GerritSettingsData::getReviewNotifications)); } public void setLogin(final String login) { @@ -292,11 +303,11 @@ public void setPushToGerrit(boolean pushToGerrit) { } public boolean getPushToGerrit() { - return getForCurrentProject(GerritSettingsData::getPushToGerrit); + return BooleanUtils.isTrue(getForCurrentProject(GerritSettingsData::getPushToGerrit)); } public boolean getShowChangeNumberColumn() { - return getForCurrentProject(GerritSettingsData::getShowChangeNumberColumn); + return BooleanUtils.isTrue(getForCurrentProject(GerritSettingsData::getShowChangeNumberColumn)); } public void setShowChangeNumberColumn(boolean showChangeNumberColumn) { @@ -306,7 +317,7 @@ public void setShowChangeNumberColumn(boolean showChangeNumberColumn) { } public boolean getShowChangeIdColumn() { - return getForCurrentProject(GerritSettingsData::getShowChangeIdColumn); + return BooleanUtils.isTrue(getForCurrentProject(GerritSettingsData::getShowChangeIdColumn)); } public void setShowChangeIdColumn(boolean showChangeIdColumn) { @@ -316,11 +327,12 @@ public void setShowChangeIdColumn(boolean showChangeIdColumn) { } public boolean getShowTopicColumn() { - return getForCurrentProject(GerritSettingsData::getShowTopicColumn); + return BooleanUtils.isTrue(getForCurrentProject(GerritSettingsData::getShowTopicColumn)); } public ShowProjectColumn getShowProjectColumn() { - return getForCurrentProject(GerritSettingsData::getShowProjectColumn); + ShowProjectColumn showProjectColumn = getForCurrentProject(GerritSettingsData::getShowProjectColumn); + return showProjectColumn != null ? showProjectColumn : ShowProjectColumn.AUTO; } public void setShowProjectColumn(ShowProjectColumn showProjectColumn) { @@ -354,6 +366,7 @@ public void setLog(Logger log) { } public String getCloneBaseUrlOrHost() { - return getForCurrentProject(GerritSettingsData::getCloneBaseUrlOrHost); + String urlOrHost = getForCurrentProject(GerritSettingsData::getCloneBaseUrlOrHost); + return urlOrHost != null ? urlOrHost : ""; } } From f207bfbf290599c0873586e7ac4980f2292e65d4 Mon Sep 17 00:00:00 2001 From: John Phillips Date: Fri, 12 May 2023 01:33:24 -0400 Subject: [PATCH 3/4] XML persistence fixes --- .../plugin/gerrit/GerritSettings.java | 23 ++++++++++++------- .../plugin/gerrit/GerritSettingsData.java | 5 ++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java index 93e61070..579d421f 100644 --- a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java +++ b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java @@ -69,7 +69,8 @@ public class GerritSettings implements PersistentStateComponent, Gerrit public Element getState() { // Handle global settings separately for backwards compatibility GerritSettingsData globalSettings = projectSettings.get(GERRIT_SETTINGS_TAG); - final Element element = globalSettings.getAsElement(GERRIT_SETTINGS_TAG); + final Element mainElement = new Element(GERRIT_SETTINGS_TAG); + globalSettings.fillElement(mainElement,GERRIT_SETTINGS_TAG); // If we don't have project settings yet, default to global settings. String currentProjectName = getCurrentProjectName(); @@ -82,13 +83,13 @@ public Element getState() { Element projects = new Element("Projects"); projectSettings.forEach((projectName, settings) -> { if(projectName!=null && !projectName.equals(GERRIT_SETTINGS_TAG)) { - final Element projectElement = settings.getAsElement(projectName); - projects.addContent(projectElement); + Element project = new Element("project"); + projects.addContent(settings.fillElement(project,projectName)); } }); - element.addContent(projects); + mainElement.addContent(projects); - return element; + return mainElement; } public void loadState(@NotNull final Element element) { @@ -101,8 +102,8 @@ public void loadState(@NotNull final Element element) { if(projectDom != null) { List projectSettingsElements = projectDom.getChildren(); for (Element projectSettingsElement : projectSettingsElements) { - String projectName = projectSettingsElement.getName(); - if (!projectName.isEmpty()) { + String projectName = projectSettingsElement.getAttributeValue("name"); + if (!Strings.isNullOrEmpty(projectName)) { projectSettings.put(projectName, new GerritSettingsData(projectSettingsElement, log)); } } @@ -136,7 +137,7 @@ private String getCurrentProjectName() { private void addProjectSettingsFromDefault(String projectName){ // Default to global settings - Element globalSettings = getDefaultSettings().getAsElement(GERRIT_SETTINGS_TAG); + Element globalSettings = getDefaultSettings().fillElement(new Element(GERRIT_SETTINGS_TAG),GERRIT_SETTINGS_TAG); projectSettings.put(projectName, new GerritSettingsData(globalSettings, log)); setPassword(getDefaultPassword()); } @@ -148,6 +149,9 @@ public GerritSettingsData getDefaultSettings(){ public T getForCurrentProject(Function method) { String projectName = getCurrentProjectName(); if(!projectName.isEmpty()) { + if(!projectSettings.containsKey(projectName)) { + addProjectSettingsFromDefault(projectName); + } GerritSettingsData settings = projectSettings.get(projectName); if(settings != null) { return method.apply(settings); @@ -158,6 +162,9 @@ public T getForCurrentProject(Function method) { public void setForCurrentProject(Consumer method) { String projectName = getCurrentProjectName(); if(!projectName.isEmpty()) { + if(!projectSettings.containsKey(projectName)) { + addProjectSettingsFromDefault(projectName); + } GerritSettingsData settings = projectSettings.get(projectName); if(settings != null) { method.accept(settings); diff --git a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java index 66cfa96e..0f9adb27 100644 --- a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java +++ b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable; public class GerritSettingsData { + private static final String NAME = "name"; private static final String LOGIN = "Login"; private static final String HOST = "Host"; private static final String AUTOMATIC_REFRESH = "AutomaticRefresh"; @@ -190,8 +191,8 @@ public void setLog(Logger log) { this.logger = log; } - public Element getAsElement(String elementName){ - final Element element = new Element(elementName); + public Element fillElement(Element element, String elementName){ + element.setAttribute(NAME, elementName); element.setAttribute(LOGIN, (getLogin() != null ? getLogin() : "")); element.setAttribute(HOST, (getHost() != null ? getHost() : "")); element.setAttribute(LIST_ALL_CHANGES, Boolean.toString(getListAllChanges())); From 3d3b12b094915e40f0614b1969a946f58803a4b5 Mon Sep 17 00:00:00 2001 From: John Phillips Date: Fri, 12 May 2023 22:23:53 -0400 Subject: [PATCH 4/4] XML constants adjustments --- .../intellij/plugin/gerrit/GerritSettings.java | 10 ++++++---- .../intellij/plugin/gerrit/GerritSettingsData.java | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java index 579d421f..6e8536f1 100644 --- a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java +++ b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettings.java @@ -56,6 +56,8 @@ public class GerritSettings implements PersistentStateComponent, GerritAuthData { private static final String GERRIT_SETTINGS_TAG = "GerritSettings"; + private static final String PROJECT_LIST_TAG = "projects"; + private static final String PROJECT_TAG = "project"; private static final String GERRIT_SETTINGS_PASSWORD_KEY = "GERRIT_SETTINGS_PASSWORD_KEY"; private static final CredentialAttributes CREDENTIAL_ATTRIBUTES = new CredentialAttributes(GerritSettings.class.getName(), GERRIT_SETTINGS_PASSWORD_KEY); @@ -80,10 +82,10 @@ public Element getState() { } } - Element projects = new Element("Projects"); + Element projects = new Element(PROJECT_LIST_TAG); projectSettings.forEach((projectName, settings) -> { if(projectName!=null && !projectName.equals(GERRIT_SETTINGS_TAG)) { - Element project = new Element("project"); + Element project = new Element(PROJECT_TAG); projects.addContent(settings.fillElement(project,projectName)); } }); @@ -98,11 +100,11 @@ public void loadState(@NotNull final Element element) { // Load global settings projectSettings.put(GERRIT_SETTINGS_TAG, new GerritSettingsData(element, log)); - Element projectDom = element.getChild("Projects"); + Element projectDom = element.getChild(PROJECT_LIST_TAG); if(projectDom != null) { List projectSettingsElements = projectDom.getChildren(); for (Element projectSettingsElement : projectSettingsElements) { - String projectName = projectSettingsElement.getAttributeValue("name"); + String projectName = projectSettingsElement.getAttributeValue(GerritSettingsData.NAME); if (!Strings.isNullOrEmpty(projectName)) { projectSettings.put(projectName, new GerritSettingsData(projectSettingsElement, log)); } diff --git a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java index 0f9adb27..90c1abc0 100644 --- a/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java +++ b/src/main/java/com/urswolfer/intellij/plugin/gerrit/GerritSettingsData.java @@ -8,7 +8,8 @@ import org.jetbrains.annotations.Nullable; public class GerritSettingsData { - private static final String NAME = "name"; + public static final String NAME = "name"; + private static final String LOGIN = "Login"; private static final String HOST = "Host"; private static final String AUTOMATIC_REFRESH = "AutomaticRefresh";