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