// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.oauth; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.concurrent.Executor; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.JTextComponent; import org.openstreetmap.josm.data.oauth.OAuthToken; import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder; import org.openstreetmap.josm.gui.widgets.DefaultTextComponentValidator; import org.openstreetmap.josm.gui.widgets.HtmlPanel; import org.openstreetmap.josm.gui.widgets.JosmTextField; import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator; import org.openstreetmap.josm.tools.ImageProvider; /** * This is an UI which supports a JOSM user to get an OAuth Access Token in a fully manual process. * * @since 2746 */ public class ManualAuthorizationUI extends AbstractAuthorizationUI { private final JosmTextField tfAccessTokenKey = new JosmTextField(); private transient AccessTokenKeyValidator valAccessTokenKey; private final JosmTextField tfAccessTokenSecret = new JosmTextField(); private transient AccessTokenSecretValidator valAccessTokenSecret; private final JCheckBox cbSaveToPreferences = new JCheckBox(tr("Save Access Token in preferences")); private final HtmlPanel pnlMessage = new HtmlPanel(); private final transient Executor executor; /** * Constructs a new {@code ManualAuthorizationUI} for the given API URL. * @param apiUrl The OSM API URL * @param executor the executor used for running the HTTP requests for the authorization * @since 5422 */ public ManualAuthorizationUI(String apiUrl, Executor executor) { super(/* dont pass apiURL because setApiUrl is overriden and references a local field */); setApiUrl(apiUrl); this.executor = executor; build(); } protected JPanel buildAccessTokenPanel() { JPanel pnl = new JPanel(new GridBagLayout()); pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); GridBagConstraints gc = new GridBagConstraints(); AccessTokenBuilder accessTokenBuilder = new AccessTokenBuilder(); // the access token key input field gc.anchor = GridBagConstraints.NORTHWEST; gc.fill = GridBagConstraints.HORIZONTAL; gc.weightx = 0.0; gc.gridwidth = 2; gc.insets = new Insets(0, 0, 5, 0); pnlMessage.setText("<html><body>" + tr("Please enter an OAuth Access Token which is authorized to access the OSM server " + "''{0}''.", getApiUrl()) + "</body></html>"); pnl.add(pnlMessage, gc); // the access token key input field gc.gridy = 1; gc.weightx = 0.0; gc.gridwidth = 1; gc.insets = new Insets(0, 0, 0, 3); pnl.add(new JLabel(tr("Access Token Key:")), gc); gc.gridx = 1; gc.weightx = 1.0; pnl.add(tfAccessTokenKey, gc); SelectAllOnFocusGainedDecorator.decorate(tfAccessTokenKey); valAccessTokenKey = new AccessTokenKeyValidator(tfAccessTokenKey); valAccessTokenKey.validate(); tfAccessTokenKey.getDocument().addDocumentListener(accessTokenBuilder); // the access token key input field gc.gridy = 2; gc.gridx = 0; gc.weightx = 0.0; pnl.add(new JLabel(tr("Access Token Secret:")), gc); gc.gridx = 1; gc.weightx = 1.0; pnl.add(tfAccessTokenSecret, gc); SelectAllOnFocusGainedDecorator.decorate(tfAccessTokenSecret); valAccessTokenSecret = new AccessTokenSecretValidator(tfAccessTokenSecret); valAccessTokenSecret.validate(); tfAccessTokenSecret.getDocument().addDocumentListener(accessTokenBuilder); // the checkbox for saving to preferences gc.gridy = 3; gc.gridx = 0; gc.gridwidth = 2; gc.weightx = 1.0; pnl.add(cbSaveToPreferences, gc); cbSaveToPreferences.setSelected(OAuthAccessTokenHolder.getInstance().isSaveToPreferences()); // filler - grab remaining space gc.gridy = 3; gc.gridx = 0; gc.gridwidth = 2; gc.weightx = 1.0; gc.weighty = 1.0; gc.fill = GridBagConstraints.BOTH; pnl.add(new JPanel(), gc); return pnl; } protected JPanel buildTabbedPreferencesPanel() { JPanel pnl = new JPanel(new BorderLayout()); JTabbedPane tp = new JTabbedPane(); tp.add(buildAccessTokenPanel()); tp.add(getAdvancedPropertiesPanel()); tp.setTitleAt(0, tr("Access Token")); tp.setTitleAt(1, tr("Advanced OAuth parameters")); tp.setToolTipTextAt(0, tr("Enter the OAuth Access Token")); tp.setToolTipTextAt(1, tr("Enter advanced OAuth properties")); pnl.add(tp, BorderLayout.CENTER); return pnl; } protected JPanel buildActionsPanel() { JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT)); TestAccessTokenAction actTestAccessToken = new TestAccessTokenAction(); pnl.add(new JButton(actTestAccessToken)); this.addPropertyChangeListener(actTestAccessToken); return pnl; } @Override public void setApiUrl(String apiUrl) { super.setApiUrl(apiUrl); if (pnlMessage != null) { pnlMessage.setText(tr("<html><body>" + "Please enter an OAuth Access Token which is authorized to access the OSM server " + "''{0}''." + "</body></html>", getApiUrl() )); } } protected final void build() { setLayout(new BorderLayout()); setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); add(buildTabbedPreferencesPanel(), BorderLayout.CENTER); add(buildActionsPanel(), BorderLayout.SOUTH); } @Override public boolean isSaveAccessTokenToPreferences() { return cbSaveToPreferences.isSelected(); } private static class AccessTokenKeyValidator extends DefaultTextComponentValidator { AccessTokenKeyValidator(JTextComponent tc) { super(tc, tr("Please enter an Access Token Key"), tr("The Access Token Key must not be empty. Please enter an Access Token Key")); } } private static class AccessTokenSecretValidator extends DefaultTextComponentValidator { AccessTokenSecretValidator(JTextComponent tc) { super(tc, tr("Please enter an Access Token Secret"), tr("The Access Token Secret must not be empty. Please enter an Access Token Secret")); } } class AccessTokenBuilder implements DocumentListener { public void build() { if (!valAccessTokenKey.isValid() || !valAccessTokenSecret.isValid()) { setAccessToken(null); } else { setAccessToken(new OAuthToken(tfAccessTokenKey.getText().trim(), tfAccessTokenSecret.getText().trim())); } } @Override public void changedUpdate(DocumentEvent e) { build(); } @Override public void insertUpdate(DocumentEvent e) { build(); } @Override public void removeUpdate(DocumentEvent e) { build(); } } /** * Action for testing an Access Token */ class TestAccessTokenAction extends AbstractAction implements PropertyChangeListener { TestAccessTokenAction() { putValue(NAME, tr("Test Access Token")); new ImageProvider("oauth", "oauth-small").getResource().attachImageIcon(this); putValue(SHORT_DESCRIPTION, tr("Click to test the Access Token")); updateEnabledState(); } @Override public void actionPerformed(ActionEvent evt) { TestAccessTokenTask task = new TestAccessTokenTask( ManualAuthorizationUI.this, getApiUrl(), getAdvancedPropertiesPanel().getAdvancedParameters(), getAccessToken() ); executor.execute(task); } protected final void updateEnabledState() { setEnabled(hasAccessToken()); } @Override public void propertyChange(PropertyChangeEvent evt) { if (!evt.getPropertyName().equals(AbstractAuthorizationUI.ACCESS_TOKEN_PROP)) return; updateEnabledState(); } } }