001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.gui.oauth;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.awt.BorderLayout;
007    import java.awt.FlowLayout;
008    import java.awt.GridBagConstraints;
009    import java.awt.GridBagLayout;
010    import java.awt.Insets;
011    import java.awt.event.ActionEvent;
012    import java.beans.PropertyChangeEvent;
013    import java.beans.PropertyChangeListener;
014    
015    import javax.swing.AbstractAction;
016    import javax.swing.BorderFactory;
017    import javax.swing.JCheckBox;
018    import javax.swing.JLabel;
019    import javax.swing.JPanel;
020    import javax.swing.JTabbedPane;
021    import javax.swing.JTextField;
022    import javax.swing.event.DocumentEvent;
023    import javax.swing.event.DocumentListener;
024    import javax.swing.text.JTextComponent;
025    
026    import org.openstreetmap.josm.Main;
027    import org.openstreetmap.josm.data.oauth.OAuthToken;
028    import org.openstreetmap.josm.gui.SideButton;
029    import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
030    import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
031    import org.openstreetmap.josm.gui.widgets.HtmlPanel;
032    import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
033    import 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     */
041    public class ManualAuthorizationUI extends AbstractAuthorizationUI{
042    
043        private JTextField tfAccessTokenKey;
044        private AccessTokenKeyValidator valAccessTokenKey;
045        private JTextField tfAccessTokenSecret;
046        private 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 JTextField(), 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 JTextField(), 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 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        static private class AccessTokenKeyValidator extends AbstractTextComponentValidator {
178    
179            public AccessTokenKeyValidator(JTextComponent tc) throws IllegalArgumentException {
180                super(tc);
181            }
182    
183            @Override
184            public boolean isValid() {
185                return ! getComponent().getText().trim().equals("");
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        static private class AccessTokenSecretValidator extends AbstractTextComponentValidator {
199            public AccessTokenSecretValidator(JTextComponent tc) throws IllegalArgumentException {
200                super(tc);
201            }
202    
203            @Override
204            public boolean isValid() {
205                return ! getComponent().getText().trim().equals("");
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            public void changedUpdate(DocumentEvent e) {
228                build();
229            }
230    
231            public void insertUpdate(DocumentEvent e) {
232                build();
233            }
234    
235            public void removeUpdate(DocumentEvent e) {
236                build();
237            }
238        }
239    
240        /**
241         * Action for testing an Access Token
242         */
243        class TestAccessTokenAction extends AbstractAction implements PropertyChangeListener {
244            public TestAccessTokenAction() {
245                putValue(NAME, tr("Test Access Token"));
246                putValue(SMALL_ICON, ImageProvider.get("oauth", "oauth"));
247                putValue(SHORT_DESCRIPTION, tr("Click to test the Access Token"));
248                updateEnabledState();
249            }
250    
251            public void actionPerformed(ActionEvent evt) {
252                TestAccessTokenTask task = new TestAccessTokenTask(
253                        ManualAuthorizationUI.this,
254                        getApiUrl(),
255                        getAdvancedPropertiesPanel().getAdvancedParameters(),
256                        getAccessToken()
257                );
258                Main.worker.submit(task);
259            }
260    
261            protected void updateEnabledState() {
262                setEnabled(hasAccessToken());
263            }
264    
265            public void propertyChange(PropertyChangeEvent evt) {
266                if (! evt.getPropertyName().equals(AbstractAuthorizationUI.ACCESS_TOKEN_PROP))
267                    return;
268                updateEnabledState();
269            }
270        }
271    }