source: josm/trunk/src/org/openstreetmap/josm/gui/oauth/OAuthAuthorizationWizard.java@ 12634

Last change on this file since 12634 was 12634, checked in by Don-vip, 7 years ago

see #15182 - deprecate Main.worker, replace it by gui.MainApplication.worker + code refactoring to make sure only editor packages use it

  • Property svn:eol-style set to native
File size: 15.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.oauth;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
8import java.awt.Dimension;
9import java.awt.FlowLayout;
10import java.awt.Font;
11import java.awt.GridBagConstraints;
12import java.awt.GridBagLayout;
13import java.awt.Insets;
14import java.awt.event.ActionEvent;
15import java.awt.event.ComponentAdapter;
16import java.awt.event.ComponentEvent;
17import java.awt.event.ItemEvent;
18import java.awt.event.ItemListener;
19import java.awt.event.WindowAdapter;
20import java.awt.event.WindowEvent;
21import java.beans.PropertyChangeEvent;
22import java.beans.PropertyChangeListener;
23import java.util.concurrent.Executor;
24
25import javax.swing.AbstractAction;
26import javax.swing.BorderFactory;
27import javax.swing.JButton;
28import javax.swing.JDialog;
29import javax.swing.JLabel;
30import javax.swing.JPanel;
31import javax.swing.JScrollPane;
32import javax.swing.UIManager;
33import javax.swing.event.HyperlinkEvent;
34import javax.swing.event.HyperlinkListener;
35import javax.swing.text.html.HTMLEditorKit;
36
37import org.openstreetmap.josm.Main;
38import org.openstreetmap.josm.data.Preferences;
39import org.openstreetmap.josm.data.oauth.OAuthParameters;
40import org.openstreetmap.josm.data.oauth.OAuthToken;
41import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
42import org.openstreetmap.josm.gui.help.HelpUtil;
43import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
44import org.openstreetmap.josm.gui.util.GuiHelper;
45import org.openstreetmap.josm.gui.widgets.HtmlPanel;
46import org.openstreetmap.josm.io.OsmApi;
47import org.openstreetmap.josm.tools.CheckParameterUtil;
48import org.openstreetmap.josm.tools.ImageProvider;
49import org.openstreetmap.josm.tools.InputMapUtils;
50import org.openstreetmap.josm.tools.OpenBrowser;
51import org.openstreetmap.josm.tools.UserCancelException;
52import org.openstreetmap.josm.tools.WindowGeometry;
53
54/**
55 * This wizard walks the user to the necessary steps to retrieve an OAuth Access Token which
56 * allows JOSM to access the OSM API on the users behalf.
57 * @since 2746
58 */
59public class OAuthAuthorizationWizard extends JDialog {
60 private boolean canceled;
61 private final String apiUrl;
62
63 private final AuthorizationProcedureComboBox cbAuthorisationProcedure = new AuthorizationProcedureComboBox();
64 private FullyAutomaticAuthorizationUI pnlFullyAutomaticAuthorisationUI;
65 private SemiAutomaticAuthorizationUI pnlSemiAutomaticAuthorisationUI;
66 private ManualAuthorizationUI pnlManualAuthorisationUI;
67 private JScrollPane spAuthorisationProcedureUI;
68 private final transient Executor executor;
69
70 /**
71 * Launches the wizard, {@link OAuthAccessTokenHolder#setAccessToken(OAuthToken) sets the token}
72 * and {@link OAuthAccessTokenHolder#setSaveToPreferences(boolean) saves to preferences}.
73 * @throws UserCancelException if user cancels the operation
74 */
75 public void showDialog() throws UserCancelException {
76 setVisible(true);
77 if (isCanceled()) {
78 throw new UserCancelException();
79 }
80 OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance();
81 holder.setAccessToken(getAccessToken());
82 holder.setSaveToPreferences(isSaveAccessTokenToPreferences());
83 }
84
85 /**
86 * Builds the row with the action buttons
87 *
88 * @return panel with buttons
89 */
90 protected JPanel buildButtonRow() {
91 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER));
92
93 AcceptAccessTokenAction actAcceptAccessToken = new AcceptAccessTokenAction();
94 pnlFullyAutomaticAuthorisationUI.addPropertyChangeListener(actAcceptAccessToken);
95 pnlSemiAutomaticAuthorisationUI.addPropertyChangeListener(actAcceptAccessToken);
96 pnlManualAuthorisationUI.addPropertyChangeListener(actAcceptAccessToken);
97
98 pnl.add(new JButton(actAcceptAccessToken));
99 pnl.add(new JButton(new CancelAction()));
100 pnl.add(new JButton(new ContextSensitiveHelpAction(HelpUtil.ht("/Dialog/OAuthAuthorisationWizard"))));
101
102 return pnl;
103 }
104
105 /**
106 * Builds the panel with general information in the header
107 *
108 * @return panel with information display
109 */
110 protected JPanel buildHeaderInfoPanel() {
111 JPanel pnl = new JPanel(new GridBagLayout());
112 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
113 GridBagConstraints gc = new GridBagConstraints();
114
115 // the oauth logo in the header
116 gc.anchor = GridBagConstraints.NORTHWEST;
117 gc.fill = GridBagConstraints.HORIZONTAL;
118 gc.weightx = 1.0;
119 gc.gridwidth = 2;
120 ImageProvider logoProv = new ImageProvider("oauth", "oauth-logo").setMaxHeight(100);
121 JLabel lbl = new JLabel(logoProv.get());
122 lbl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
123 lbl.setOpaque(true);
124 pnl.add(lbl, gc);
125
126 // OAuth in a nutshell ...
127 gc.gridy = 1;
128 gc.insets = new Insets(5, 0, 0, 5);
129 HtmlPanel pnlMessage = new HtmlPanel();
130 pnlMessage.setText("<html><body>"
131 + tr("With OAuth you grant JOSM the right to upload map data and GPS tracks "
132 + "on your behalf (<a href=\"{0}\">more info...</a>).", "http://oauth.net/")
133 + "</body></html>"
134 );
135 pnlMessage.getEditorPane().addHyperlinkListener(new ExternalBrowserLauncher());
136 pnl.add(pnlMessage, gc);
137
138 // the authorisation procedure
139 gc.gridy = 2;
140 gc.gridwidth = 1;
141 gc.weightx = 0.0;
142 lbl = new JLabel(tr("Please select an authorization procedure: "));
143 lbl.setFont(lbl.getFont().deriveFont(Font.PLAIN));
144 pnl.add(lbl, gc);
145
146 gc.gridx = 1;
147 gc.gridwidth = 1;
148 gc.weightx = 1.0;
149 pnl.add(cbAuthorisationProcedure, gc);
150 cbAuthorisationProcedure.addItemListener(new AuthorisationProcedureChangeListener());
151 lbl.setLabelFor(cbAuthorisationProcedure);
152
153 if (!OsmApi.DEFAULT_API_URL.equals(apiUrl)) {
154 gc.gridy = 3;
155 gc.gridwidth = 2;
156 gc.gridx = 0;
157 final HtmlPanel pnlWarning = new HtmlPanel();
158 final HTMLEditorKit kit = (HTMLEditorKit) pnlWarning.getEditorPane().getEditorKit();
159 kit.getStyleSheet().addRule(".warning-body {"
160 + "background-color:rgb(253,255,221);padding: 10pt; "
161 + "border-color:rgb(128,128,128);border-style: solid;border-width: 1px;}");
162 kit.getStyleSheet().addRule("ol {margin-left: 1cm}");
163 pnlWarning.setText("<html><body>"
164 + "<p class=\"warning-body\">"
165 + tr("<strong>Warning:</strong> Since you are using not the default OSM API, " +
166 "make sure to set an OAuth consumer key and secret in the <i>Advanced OAuth parameters</i>.")
167 + "</p>"
168 + "</body></html>");
169 pnl.add(pnlWarning, gc);
170 }
171
172 return pnl;
173 }
174
175 /**
176 * Refreshes the view of the authorisation panel, depending on the authorisation procedure
177 * currently selected
178 */
179 protected void refreshAuthorisationProcedurePanel() {
180 AuthorizationProcedure procedure = (AuthorizationProcedure) cbAuthorisationProcedure.getSelectedItem();
181 switch(procedure) {
182 case FULLY_AUTOMATIC:
183 spAuthorisationProcedureUI.getViewport().setView(pnlFullyAutomaticAuthorisationUI);
184 pnlFullyAutomaticAuthorisationUI.revalidate();
185 break;
186 case SEMI_AUTOMATIC:
187 spAuthorisationProcedureUI.getViewport().setView(pnlSemiAutomaticAuthorisationUI);
188 pnlSemiAutomaticAuthorisationUI.revalidate();
189 break;
190 case MANUALLY:
191 spAuthorisationProcedureUI.getViewport().setView(pnlManualAuthorisationUI);
192 pnlManualAuthorisationUI.revalidate();
193 break;
194 }
195 validate();
196 repaint();
197 }
198
199 /**
200 * builds the UI
201 */
202 protected final void build() {
203 getContentPane().setLayout(new BorderLayout());
204 getContentPane().add(buildHeaderInfoPanel(), BorderLayout.NORTH);
205
206 setTitle(tr("Get an Access Token for ''{0}''", apiUrl));
207 this.setMinimumSize(new Dimension(600, 420));
208
209 pnlFullyAutomaticAuthorisationUI = new FullyAutomaticAuthorizationUI(apiUrl, executor);
210 pnlSemiAutomaticAuthorisationUI = new SemiAutomaticAuthorizationUI(apiUrl, executor);
211 pnlManualAuthorisationUI = new ManualAuthorizationUI(apiUrl, executor);
212
213 spAuthorisationProcedureUI = GuiHelper.embedInVerticalScrollPane(new JPanel());
214 spAuthorisationProcedureUI.getVerticalScrollBar().addComponentListener(
215 new ComponentAdapter() {
216 @Override
217 public void componentShown(ComponentEvent e) {
218 spAuthorisationProcedureUI.setBorder(UIManager.getBorder("ScrollPane.border"));
219 }
220
221 @Override
222 public void componentHidden(ComponentEvent e) {
223 spAuthorisationProcedureUI.setBorder(null);
224 }
225 }
226 );
227 getContentPane().add(spAuthorisationProcedureUI, BorderLayout.CENTER);
228 getContentPane().add(buildButtonRow(), BorderLayout.SOUTH);
229
230 addWindowListener(new WindowEventHandler());
231 InputMapUtils.addEscapeAction(getRootPane(), new CancelAction());
232
233 refreshAuthorisationProcedurePanel();
234
235 HelpUtil.setHelpContext(getRootPane(), HelpUtil.ht("/Dialog/OAuthAuthorisationWizard"));
236 }
237
238 /**
239 * Creates the wizard.
240 *
241 * @param parent the component relative to which the dialog is displayed
242 * @param apiUrl the API URL. Must not be null.
243 * @param executor the executor used for running the HTTP requests for the authorization
244 * @throws IllegalArgumentException if apiUrl is null
245 */
246 public OAuthAuthorizationWizard(Component parent, String apiUrl, Executor executor) {
247 super(GuiHelper.getFrameForComponent(parent), ModalityType.DOCUMENT_MODAL);
248 CheckParameterUtil.ensureParameterNotNull(apiUrl, "apiUrl");
249 this.apiUrl = apiUrl;
250 this.executor = executor;
251 build();
252 }
253
254 /**
255 * Replies true if the dialog was canceled
256 *
257 * @return true if the dialog was canceled
258 */
259 public boolean isCanceled() {
260 return canceled;
261 }
262
263 protected AbstractAuthorizationUI getCurrentAuthorisationUI() {
264 switch((AuthorizationProcedure) cbAuthorisationProcedure.getSelectedItem()) {
265 case FULLY_AUTOMATIC: return pnlFullyAutomaticAuthorisationUI;
266 case MANUALLY: return pnlManualAuthorisationUI;
267 case SEMI_AUTOMATIC: return pnlSemiAutomaticAuthorisationUI;
268 default: return null;
269 }
270 }
271
272 /**
273 * Replies the Access Token entered using the wizard
274 *
275 * @return the access token. May be null if the wizard was canceled.
276 */
277 public OAuthToken getAccessToken() {
278 return getCurrentAuthorisationUI().getAccessToken();
279 }
280
281 /**
282 * Replies the current OAuth parameters.
283 *
284 * @return the current OAuth parameters.
285 */
286 public OAuthParameters getOAuthParameters() {
287 return getCurrentAuthorisationUI().getOAuthParameters();
288 }
289
290 /**
291 * Replies true if the currently selected Access Token shall be saved to
292 * the preferences.
293 *
294 * @return true if the currently selected Access Token shall be saved to
295 * the preferences
296 */
297 public boolean isSaveAccessTokenToPreferences() {
298 return getCurrentAuthorisationUI().isSaveAccessTokenToPreferences();
299 }
300
301 /**
302 * Initializes the dialog with values from the preferences
303 *
304 */
305 public void initFromPreferences() {
306 // Copy current JOSM preferences to update API url with the one used in this wizard
307 Preferences copyPref = new Preferences(Main.pref);
308 copyPref.put("osm-server.url", apiUrl);
309 pnlFullyAutomaticAuthorisationUI.initFromPreferences(copyPref);
310 pnlSemiAutomaticAuthorisationUI.initFromPreferences(copyPref);
311 pnlManualAuthorisationUI.initFromPreferences(copyPref);
312 }
313
314 @Override
315 public void setVisible(boolean visible) {
316 if (visible) {
317 new WindowGeometry(
318 getClass().getName() + ".geometry",
319 WindowGeometry.centerInWindow(
320 Main.parent,
321 new Dimension(450, 540)
322 )
323 ).applySafe(this);
324 initFromPreferences();
325 } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
326 new WindowGeometry(this).remember(getClass().getName() + ".geometry");
327 }
328 super.setVisible(visible);
329 }
330
331 protected void setCanceled(boolean canceled) {
332 this.canceled = canceled;
333 }
334
335 class AuthorisationProcedureChangeListener implements ItemListener {
336 @Override
337 public void itemStateChanged(ItemEvent arg0) {
338 refreshAuthorisationProcedurePanel();
339 }
340 }
341
342 class CancelAction extends AbstractAction {
343
344 /**
345 * Constructs a new {@code CancelAction}.
346 */
347 CancelAction() {
348 putValue(NAME, tr("Cancel"));
349 new ImageProvider("cancel").getResource().attachImageIcon(this);
350 putValue(SHORT_DESCRIPTION, tr("Close the dialog and cancel authorization"));
351 }
352
353 public void cancel() {
354 setCanceled(true);
355 setVisible(false);
356 }
357
358 @Override
359 public void actionPerformed(ActionEvent evt) {
360 cancel();
361 }
362 }
363
364 class AcceptAccessTokenAction extends AbstractAction implements PropertyChangeListener {
365
366 /**
367 * Constructs a new {@code AcceptAccessTokenAction}.
368 */
369 AcceptAccessTokenAction() {
370 putValue(NAME, tr("Accept Access Token"));
371 new ImageProvider("ok").getResource().attachImageIcon(this);
372 putValue(SHORT_DESCRIPTION, tr("Close the dialog and accept the Access Token"));
373 updateEnabledState(null);
374 }
375
376 @Override
377 public void actionPerformed(ActionEvent evt) {
378 setCanceled(false);
379 setVisible(false);
380 }
381
382 public final void updateEnabledState(OAuthToken token) {
383 setEnabled(token != null);
384 }
385
386 @Override
387 public void propertyChange(PropertyChangeEvent evt) {
388 if (!evt.getPropertyName().equals(AbstractAuthorizationUI.ACCESS_TOKEN_PROP))
389 return;
390 updateEnabledState((OAuthToken) evt.getNewValue());
391 }
392 }
393
394 class WindowEventHandler extends WindowAdapter {
395 @Override
396 public void windowClosing(WindowEvent e) {
397 new CancelAction().cancel();
398 }
399 }
400
401 static class ExternalBrowserLauncher implements HyperlinkListener {
402 @Override
403 public void hyperlinkUpdate(HyperlinkEvent e) {
404 if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
405 OpenBrowser.displayUrl(e.getDescription());
406 }
407 }
408 }
409}
Note: See TracBrowser for help on using the repository browser.