/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.properties.celleditors.value; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.util.LinkedList; import javax.swing.AbstractButton; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; import com.rapidminer.gui.ApplicationFrame; import com.rapidminer.gui.look.borders.RoundTitledBorder; import com.rapidminer.gui.tools.ProgressThread; import com.rapidminer.gui.tools.ResourceAction; import com.rapidminer.gui.tools.ResourceActionAdapter; import com.rapidminer.gui.tools.components.LinkLocalButton; import com.rapidminer.gui.tools.dialogs.ButtonDialog; import com.rapidminer.parameter.OAuthMechanism; import com.rapidminer.tools.I18N; import com.rapidminer.tools.RMUrlHandler; /** * Dialog which uses an {@link OAuthMechanism} to authenticate RapidMiner via OAuth. * * @author Marcel Michel * */ public class OAuthDialog extends ButtonDialog { private static final long serialVersionUID = -3332118850917625521L; /** displays error and status messages */ private JLabel statusLabel; /** the authorization url textField */ private JTextField authUrlText; /** the code textField */ private JTextField codeText; /** the OAuth mechanism */ private OAuthMechanism oAuth; private JButton confirmButton; private JButton cancelButton; private JButton urlButton; /** * Constructs the dialog and uses an {@link OAuthMechanism} for authentication * * @param oAuthMechanism * @deprecated use {@link #OAuthDialog(Window, OAuthMechanism)} instead */ @Deprecated public OAuthDialog(OAuthMechanism oAuthMechanism) { this(ApplicationFrame.getApplicationFrame(), oAuthMechanism); } /** * Constructs the dialog and uses an {@link OAuthMechanism} for authentication * * @param owner * the owner window in which this dialog should appear * @param oAuthMechanism * the mechanism * @since 6.5.0 */ public OAuthDialog(Window owner, OAuthMechanism oAuthMechanism) { super(owner, "oauth_dialog", ModalityType.MODELESS, new Object[] {}); this.oAuth = oAuthMechanism; initGUI(); startOAuth(); } /** * Basic method to initialize the GUI. */ private void initGUI() { JPanel outerPanel = new JPanel(); outerPanel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1; gbc.fill = GridBagConstraints.BOTH; outerPanel.add(createStep1Panel(), gbc); if (oAuth.isOAuth2()) { gbc.gridy += 1; outerPanel.add(createStep2Panel(), gbc); } statusLabel = new JLabel(); statusLabel.setMinimumSize(new Dimension(400, 25)); statusLabel.setPreferredSize(new Dimension(400, 25)); statusLabel.setHorizontalAlignment(JLabel.RIGHT); gbc.gridy += 1; gbc.insets = new Insets(5, 5, 5, 5); outerPanel.add(statusLabel, gbc); gbc.gridy += 1; Component btPanel = createButtons(); outerPanel.add(btPanel, gbc); add(outerPanel); layoutDefault(outerPanel, confirmButton, cancelButton); if (oAuth.isOAuth2()) { setPreferredSize(new Dimension(375, 430)); } else { setPreferredSize(new Dimension(375, 320)); } setResizable(false); setModal(true); pack(); setLocationRelativeTo(ApplicationFrame.getApplicationFrame()); } /** * Creates and returns the GUI components for step1. * * @return */ private Component createStep1Panel() { JPanel panel = new JPanel(); panel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1; gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(0, 51, 0, 0); urlButton = new JButton(new ResourceAction(false, "oauth_dialog.wait_for_url") { private static final long serialVersionUID = 1154127549553798757L; @Override public void actionPerformed(ActionEvent e) { open(authUrlText.getText()); } }); gbc.gridx += 1; urlButton.setEnabled(false); panel.add(urlButton, gbc); authUrlText = new JTextField(); authUrlText.setEditable(false); authUrlText.setVisible(false); authUrlText.setBackground(Color.WHITE); authUrlText.setMinimumSize(new Dimension(80, 33)); authUrlText.setPreferredSize(new Dimension(80, 33)); authUrlText.addFocusListener(new FocusListener() { @Override public void focusLost(FocusEvent e) { // nothing } @Override public void focusGained(FocusEvent e) { authUrlText.selectAll(); } }); panel.add(authUrlText, gbc); LinkLocalButton showUrlButton = new LinkLocalButton(new ResourceAction(true, "oauth_dialog.show_url") { private static final long serialVersionUID = -5000971936611417944L; @Override public void actionPerformed(ActionEvent e) { urlButton.setVisible(false); authUrlText.setVisible(true); } }); // pretty right alignment method SimpleAttributeSet rightAlignment = new SimpleAttributeSet(); StyledDocument styledDoc = (StyledDocument) showUrlButton.getDocument(); StyleConstants.setAlignment(rightAlignment, StyleConstants.ALIGN_RIGHT); styledDoc.setParagraphAttributes(0, styledDoc.getLength(), rightAlignment, false); gbc.gridx = 1; gbc.gridy += 1; panel.add(showUrlButton, gbc); panel.setBorder(new RoundTitledBorder(1, I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.oauth_dialog.open_url.label"), false)); return panel; } /** * Creates and returns the GUI components for step2. * * @return */ private Component createStep2Panel() { JPanel panel = new JPanel(); panel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1; gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(0, 51, 0, 0); codeText = new JTextField(); codeText.setMinimumSize(new Dimension(80, 33)); codeText.setPreferredSize(new Dimension(80, 33)); panel.add(codeText, gbc); panel.setBorder(new RoundTitledBorder(2, I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.oauth_dialog.copy_code.label"), false)); return panel; } /** * Creates and returns the button component. * * @return */ private Component createButtons() { LinkedList<AbstractButton> buttons = new LinkedList<>(); confirmButton = new JButton(new ResourceActionAdapter(false, "oauth_dialog.ok") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { if (oAuth.isOAuth2() && !validateCode()) { return; } endOAuth(); } }); buttons.add(confirmButton); ResourceAction cancelAction = new ResourceAction(false, "oauth_dialog.cancel") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { OAuthDialog.this.dispose(); } }; cancelButton = new JButton(cancelAction); buttons.add(cancelButton); getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false), "CANCEL"); getRootPane().getActionMap().put("CANCEL", cancelAction); return makeButtonPanel(buttons); } /** * Calls the startOAuth in a {@link ProgressThread} and displays the result in the * {@link #statusLabel} */ private void startOAuth() { ProgressThread actionThread = new ProgressThread("oauth_action_start") { @Override public void run() { // show user we are doing something SwingUtilities.invokeLater(new Runnable() { @Override public void run() { OAuthDialog.this.displayStatus(I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.oauth_dialog.status.start_oauth.message")); } }); final String authorizeUrl = oAuth.startOAuth(); // show user results SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (authorizeUrl != null) { OAuthDialog.this.displayStatus(""); OAuthDialog.this.authUrlText.setText(authorizeUrl); OAuthDialog.this.authUrlText.setCaretPosition(0); OAuthDialog.this.urlButton.setEnabled(true); OAuthDialog.this.urlButton.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.action.oauth_dialog.open_url.label")); OAuthDialog.this.urlButton.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), "gui.action.oauth_dialog.open_url.tip")); } else { OAuthDialog.this.displayError(I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.error.oauth_dialog.start_oauth_fail.message")); OAuthDialog.this.confirmButton.setEnabled(false); } } }); } }; actionThread.start(); } /** * Calls the endOAuth in a {@link ProgressThread} and displays the result in the * {@link #statusLabel} */ private void endOAuth() { ProgressThread actionThread = new ProgressThread("oauth_action_end") { @Override public void run() { // show user we are doing something SwingUtilities.invokeLater(new Runnable() { @Override public void run() { OAuthDialog.this.displayStatus(I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.oauth_dialog.status.end_oauth.message")); OAuthDialog.this.confirmButton.setEnabled(false); } }); String code = null; if (oAuth.isOAuth2()) { code = OAuthDialog.this.codeText.getText().trim(); } final String error = oAuth.endOAuth(code); // show user results SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (error != null) { OAuthDialog.this.displayError(error); OAuthDialog.this.confirmButton.setEnabled(true); } else { OAuthDialog.this.dispose(); } } }); } }; actionThread.start(); } /** * Validates the {@link #codeText} field. * * @return Returns <code>true</code> if no errors detected otherwise <code>false</code> */ private boolean validateCode() { String code = codeText.getText(); if (code == null || "".equals(code.trim())) { displayError(I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.error.oauth_dialog.empty_code.message")); return false; } statusLabel.setText(null); return true; } /** * Displays a message in the {@link #statusLabel} * * @param message */ private void displayStatus(String message) { statusLabel.setForeground(Color.BLACK); statusLabel.setText(message); statusLabel.setToolTipText(message); } /** * Displays an error in the {@link #statusLabel} * * @param errorMessage */ private void displayError(String errorMessage) { statusLabel.setForeground(Color.RED); statusLabel.setText(errorMessage); statusLabel.setToolTipText(errorMessage); } /** * Opens an URL with the default web browser. * * @param urlString */ private void open(String urlString) { RMUrlHandler.openInBrowser(urlString); } }