001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.oauth; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.BorderLayout; 007import java.awt.FlowLayout; 008import java.awt.GridBagConstraints; 009import java.awt.GridBagLayout; 010import java.awt.Insets; 011import java.awt.event.ActionEvent; 012import java.beans.PropertyChangeEvent; 013import java.beans.PropertyChangeListener; 014import java.util.concurrent.Executor; 015 016import javax.swing.AbstractAction; 017import javax.swing.BorderFactory; 018import javax.swing.JButton; 019import javax.swing.JCheckBox; 020import javax.swing.JLabel; 021import javax.swing.JPanel; 022import javax.swing.JTabbedPane; 023import javax.swing.event.DocumentEvent; 024import javax.swing.event.DocumentListener; 025import javax.swing.text.JTextComponent; 026 027import org.openstreetmap.josm.data.oauth.OAuthAccessTokenHolder; 028import org.openstreetmap.josm.data.oauth.OAuthToken; 029import org.openstreetmap.josm.gui.widgets.DefaultTextComponentValidator; 030import org.openstreetmap.josm.gui.widgets.HtmlPanel; 031import org.openstreetmap.josm.gui.widgets.JosmTextField; 032import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator; 033import org.openstreetmap.josm.tools.ImageProvider; 034 035/** 036 * This is an UI which supports a JOSM user to get an OAuth Access Token in a fully manual process. 037 * 038 * @since 2746 039 */ 040public class ManualAuthorizationUI extends AbstractAuthorizationUI { 041 042 private final JosmTextField tfAccessTokenKey = new JosmTextField(); 043 private transient AccessTokenKeyValidator valAccessTokenKey; 044 private final JosmTextField tfAccessTokenSecret = new JosmTextField(); 045 private transient AccessTokenSecretValidator valAccessTokenSecret; 046 private final JCheckBox cbSaveToPreferences = new JCheckBox(tr("Save Access Token in preferences")); 047 private final HtmlPanel pnlMessage = new HtmlPanel(); 048 private final transient Executor executor; 049 050 /** 051 * Constructs a new {@code ManualAuthorizationUI} for the given API URL. 052 * @param apiUrl The OSM API URL 053 * @param executor the executor used for running the HTTP requests for the authorization 054 * @since 5422 055 */ 056 public ManualAuthorizationUI(String apiUrl, Executor executor) { 057 super(/* dont pass apiURL because setApiUrl is overriden and references a local field */); 058 setApiUrl(apiUrl); 059 this.executor = executor; 060 build(); 061 } 062 063 protected JPanel buildAccessTokenPanel() { 064 JPanel pnl = new JPanel(new GridBagLayout()); 065 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 066 GridBagConstraints gc = new GridBagConstraints(); 067 AccessTokenBuilder accessTokenBuilder = new AccessTokenBuilder(); 068 069 // the access token key input field 070 gc.anchor = GridBagConstraints.NORTHWEST; 071 gc.fill = GridBagConstraints.HORIZONTAL; 072 gc.weightx = 0.0; 073 gc.gridwidth = 2; 074 gc.insets = new Insets(0, 0, 5, 0); 075 pnlMessage.setText("<html><body>" 076 + tr("Please enter an OAuth Access Token which is authorized to access the OSM server " 077 + "''{0}''.", 078 getApiUrl()) + "</body></html>"); 079 pnl.add(pnlMessage, gc); 080 081 // the access token key input field 082 gc.gridy = 1; 083 gc.weightx = 0.0; 084 gc.gridwidth = 1; 085 gc.insets = new Insets(0, 0, 0, 3); 086 pnl.add(new JLabel(tr("Access Token Key:")), gc); 087 088 gc.gridx = 1; 089 gc.weightx = 1.0; 090 pnl.add(tfAccessTokenKey, gc); 091 SelectAllOnFocusGainedDecorator.decorate(tfAccessTokenKey); 092 valAccessTokenKey = new AccessTokenKeyValidator(tfAccessTokenKey); 093 valAccessTokenKey.validate(); 094 tfAccessTokenKey.getDocument().addDocumentListener(accessTokenBuilder); 095 096 // the access token key input field 097 gc.gridy = 2; 098 gc.gridx = 0; 099 gc.weightx = 0.0; 100 pnl.add(new JLabel(tr("Access Token Secret:")), gc); 101 102 gc.gridx = 1; 103 gc.weightx = 1.0; 104 pnl.add(tfAccessTokenSecret, gc); 105 SelectAllOnFocusGainedDecorator.decorate(tfAccessTokenSecret); 106 valAccessTokenSecret = new AccessTokenSecretValidator(tfAccessTokenSecret); 107 valAccessTokenSecret.validate(); 108 tfAccessTokenSecret.getDocument().addDocumentListener(accessTokenBuilder); 109 110 // the checkbox for saving to preferences 111 gc.gridy = 3; 112 gc.gridx = 0; 113 gc.gridwidth = 2; 114 gc.weightx = 1.0; 115 pnl.add(cbSaveToPreferences, gc); 116 cbSaveToPreferences.setSelected(OAuthAccessTokenHolder.getInstance().isSaveToPreferences()); 117 118 // filler - grab remaining space 119 gc.gridy = 3; 120 gc.gridx = 0; 121 gc.gridwidth = 2; 122 gc.weightx = 1.0; 123 gc.weighty = 1.0; 124 gc.fill = GridBagConstraints.BOTH; 125 pnl.add(new JPanel(), gc); 126 return pnl; 127 } 128 129 protected JPanel buildTabbedPreferencesPanel() { 130 JPanel pnl = new JPanel(new BorderLayout()); 131 132 JTabbedPane tp = new JTabbedPane(); 133 tp.add(buildAccessTokenPanel()); 134 tp.add(getAdvancedPropertiesPanel()); 135 136 tp.setTitleAt(0, tr("Access Token")); 137 tp.setTitleAt(1, tr("Advanced OAuth parameters")); 138 139 tp.setToolTipTextAt(0, tr("Enter the OAuth Access Token")); 140 tp.setToolTipTextAt(1, tr("Enter advanced OAuth properties")); 141 142 pnl.add(tp, BorderLayout.CENTER); 143 return pnl; 144 } 145 146 protected JPanel buildActionsPanel() { 147 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT)); 148 TestAccessTokenAction actTestAccessToken = new TestAccessTokenAction(); 149 pnl.add(new JButton(actTestAccessToken)); 150 this.addPropertyChangeListener(actTestAccessToken); 151 return pnl; 152 } 153 154 @Override 155 public void setApiUrl(String apiUrl) { 156 super.setApiUrl(apiUrl); 157 if (pnlMessage != null) { 158 pnlMessage.setText(tr("<html><body>" 159 + "Please enter an OAuth Access Token which is authorized to access the OSM server " 160 + "''{0}''." 161 + "</body></html>", 162 getApiUrl() 163 )); 164 } 165 } 166 167 protected final void build() { 168 setLayout(new BorderLayout()); 169 setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 170 add(buildTabbedPreferencesPanel(), BorderLayout.CENTER); 171 add(buildActionsPanel(), BorderLayout.SOUTH); 172 } 173 174 @Override 175 public boolean isSaveAccessTokenToPreferences() { 176 return cbSaveToPreferences.isSelected(); 177 } 178 179 private static class AccessTokenKeyValidator extends DefaultTextComponentValidator { 180 AccessTokenKeyValidator(JTextComponent tc) { 181 super(tc, tr("Please enter an Access Token Key"), 182 tr("The Access Token Key must not be empty. Please enter an Access Token Key")); 183 } 184 } 185 186 private static class AccessTokenSecretValidator extends DefaultTextComponentValidator { 187 AccessTokenSecretValidator(JTextComponent tc) { 188 super(tc, tr("Please enter an Access Token Secret"), 189 tr("The Access Token Secret must not be empty. Please enter an Access Token Secret")); 190 } 191 } 192 193 class AccessTokenBuilder implements DocumentListener { 194 195 public void build() { 196 if (!valAccessTokenKey.isValid() || !valAccessTokenSecret.isValid()) { 197 setAccessToken(null); 198 } else { 199 setAccessToken(new OAuthToken(tfAccessTokenKey.getText().trim(), tfAccessTokenSecret.getText().trim())); 200 } 201 } 202 203 @Override 204 public void changedUpdate(DocumentEvent e) { 205 build(); 206 } 207 208 @Override 209 public void insertUpdate(DocumentEvent e) { 210 build(); 211 } 212 213 @Override 214 public void removeUpdate(DocumentEvent e) { 215 build(); 216 } 217 } 218 219 /** 220 * Action for testing an Access Token 221 */ 222 class TestAccessTokenAction extends AbstractAction implements PropertyChangeListener { 223 TestAccessTokenAction() { 224 putValue(NAME, tr("Test Access Token")); 225 new ImageProvider("oauth", "oauth-small").getResource().attachImageIcon(this); 226 putValue(SHORT_DESCRIPTION, tr("Click to test the Access Token")); 227 updateEnabledState(); 228 } 229 230 @Override 231 public void actionPerformed(ActionEvent evt) { 232 TestAccessTokenTask task = new TestAccessTokenTask( 233 ManualAuthorizationUI.this, 234 getApiUrl(), 235 getAdvancedPropertiesPanel().getAdvancedParameters(), 236 getAccessToken() 237 ); 238 executor.execute(task); 239 } 240 241 protected final void updateEnabledState() { 242 setEnabled(hasAccessToken()); 243 } 244 245 @Override 246 public void propertyChange(PropertyChangeEvent evt) { 247 if (!evt.getPropertyName().equals(AbstractAuthorizationUI.ACCESS_TOKEN_PROP)) 248 return; 249 updateEnabledState(); 250 } 251 } 252}