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