Add api to provide tweeting in educational plugins
[idea/community.git] / python / educational-core / student / src / com / jetbrains / edu / learning / twitter / StudyTwitterUtils.java
1 package com.jetbrains.edu.learning.twitter;
2
3 import com.intellij.ide.BrowserUtil;
4 import com.intellij.openapi.application.ApplicationManager;
5 import com.intellij.openapi.diagnostic.Logger;
6 import com.intellij.openapi.project.Project;
7 import com.intellij.openapi.ui.DialogBuilder;
8 import com.intellij.openapi.ui.Messages;
9 import com.intellij.openapi.util.io.FileUtil;
10 import com.intellij.ui.DocumentAdapter;
11 import com.intellij.ui.components.JBScrollPane;
12 import com.jetbrains.edu.learning.StudyPluginConfigurator;
13 import com.jetbrains.edu.learning.StudyUtils;
14 import org.jetbrains.annotations.NotNull;
15 import org.jetbrains.annotations.Nullable;
16 import twitter4j.StatusUpdate;
17 import twitter4j.Twitter;
18 import twitter4j.TwitterException;
19 import twitter4j.TwitterFactory;
20 import twitter4j.auth.AccessToken;
21 import twitter4j.auth.RequestToken;
22 import twitter4j.conf.ConfigurationBuilder;
23
24 import javax.swing.*;
25 import javax.swing.event.DocumentEvent;
26 import javax.swing.event.DocumentListener;
27 import java.io.File;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31
32
33 public class StudyTwitterUtils {
34   private static final Logger LOG = Logger.getInstance(StudyTwitterUtils.class);
35
36   /**
37    * Configure twitter instance: authorize if needed or set access token and token secret provided by configurator.
38    * @param twitter
39    * @param project
40    * @param isAuthorized
41    * @throws TwitterException
42    */
43   public static void configureTwitter(@NotNull final Twitter twitter, @NotNull final Project project,
44                                          final boolean isAuthorized) throws TwitterException {
45     if (!isAuthorized) {
46       authorize(project, twitter);
47     }
48     else {
49       StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
50       if (configurator != null) {
51         getTwitterForAuthorizedApp(twitter, configurator.getTwitterAccessToken(project), configurator.getTwitterTokenSecret(project));
52       }
53     }
54   }
55
56   /**
57    * Set consumer key and secret. 
58    * @param consumerKey
59    * @param consumerSecret
60    * @return
61    */
62   @NotNull
63   public static Twitter getTwitter(@NotNull final String consumerKey, @NotNull final String consumerSecret) {
64     ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
65     configurationBuilder.setOAuthConsumerKey(consumerKey);
66     configurationBuilder.setOAuthConsumerSecret(consumerSecret);
67     return new TwitterFactory(configurationBuilder.build()).getInstance();
68   }
69
70   /**
71    * Set access token and token secret in Twitter instance
72    * @param twitter
73    * @param accessToken
74    * @param tokenSecret
75    */
76   private static void getTwitterForAuthorizedApp(Twitter twitter, @NotNull String accessToken,
77                                                 @NotNull String tokenSecret) {
78     AccessToken token = new AccessToken(accessToken, tokenSecret);
79     twitter.setOAuthAccessToken(token);
80   }
81
82   /**
83    * Authorize user and save tokens by StudyPluginConfigurator#storeTwitterTokens
84    * @param project
85    * @param twitter
86    * @throws TwitterException
87    */
88   public static void authorize(@NotNull final Project project, @NotNull final Twitter twitter) throws TwitterException {
89     RequestToken requestToken = twitter.getOAuthRequestToken();
90     BrowserUtil.browse(requestToken.getAuthorizationURL());
91
92     ApplicationManager.getApplication().invokeLater(() -> {
93       String pin = Messages.showInputDialog("Twitter PIN:", "Twitter Authorization", null, "", null);
94       try {
95         AccessToken token;
96         if (pin != null && pin.length() > 0) {
97           token = twitter.getOAuthAccessToken(requestToken, pin);
98         }
99         else {
100           token = twitter.getOAuthAccessToken();
101         }
102         StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
103         if (configurator != null) {
104           configurator.storeTwitterTokens(project, token.getToken(), token.getTokenSecret());
105         }
106         else {
107           LOG.warn("Plugin configurator not found");
108         }
109       }
110       catch (TwitterException e) {
111         if (401 == e.getStatusCode()) {
112           LOG.warn("Unable to get the access token.");
113         }
114         else {
115           LOG.warn(e.getMessage());
116         }
117       }
118     });
119   }
120
121   /**
122    * Show twitter dialog, asking user to tweet about his achievements. Post tweet with provided by panel
123    * media and text. 
124    * As a result of succeeded tweet twitter website is opened in default browser.
125    * @param twitter 
126    * @param twitterDialogPanel 
127    */
128   public static void showPostTweetDialogAndPostTweet(@NotNull Twitter twitter, @NotNull final TwitterDialogPanel twitterDialogPanel) {
129     ApplicationManager.getApplication().invokeLater(() -> {
130       DialogBuilder builder = new DialogBuilder();
131       twitterDialogPanel.addTextFieldVerifier(createTextFieldLengthDocumentListener(builder, twitterDialogPanel));
132       builder.title("Twitter");
133       builder.addOkAction().setText("Tweet");
134       builder.addCancelAction();
135       builder.setCenterPanel(new JBScrollPane(twitterDialogPanel));
136       builder.resizable(true);
137       if (builder.showAndGet()) {
138         StatusUpdate update = new StatusUpdate(twitterDialogPanel.getMessage());
139         try {
140           InputStream inputStream = twitterDialogPanel.getMediaSource();
141           if (inputStream != null) {
142             File imageFile = FileUtil.createTempFile("twitter_media", "gif");
143             
144             FileUtil.copy(inputStream, new FileOutputStream(imageFile));
145             update.media(imageFile);
146           }
147           twitter.updateStatus(update);
148           BrowserUtil.browse("https://twitter.com/");
149         }
150         catch (IOException | TwitterException e) {
151           LOG.warn(e.getMessage());
152           Messages.showErrorDialog("Status wasn't updated. Please, check internet connection and try again", "Twitter");
153         }
154       }
155     });
156   }
157
158   /**
159    * Listener updates label indicating remaining symbols number like in twitter.
160    * @param builder
161    * @param panel
162    * @return
163    */
164   public static DocumentListener createTextFieldLengthDocumentListener(@NotNull DialogBuilder builder, @NotNull final TwitterDialogPanel panel) {
165     return new DocumentAdapter() {
166       @Override
167       protected void textChanged(DocumentEvent e) {
168         int length = e.getDocument().getLength();
169         if (length > 140 || length == 0) {
170           builder.setOkActionEnabled(false);
171           panel.getRemainSymbolsLabel().setText("<html><font color='red'>" + String.valueOf(140 - length) + "</font></html>");
172         }
173         else {
174           builder.setOkActionEnabled(true);
175           panel.getRemainSymbolsLabel().setText(String.valueOf(140 - length));
176         }
177         
178       }
179     };
180   }
181
182   /**
183    * Class provides structure for twitter dialog panel
184    */
185   public abstract static class TwitterDialogPanel extends JPanel {
186
187     /**
188      * Provides tweet text
189      * @return 
190      */
191     @NotNull public abstract String getMessage();
192
193     /**
194      * 
195      * @return Input stream of media should be posted or null if there's nothing to post 
196      */
197     @Nullable public abstract InputStream getMediaSource();
198
199     /**
200      * 
201      * @return label that will be used to show remained symbol number
202      */
203     @NotNull public abstract JLabel getRemainSymbolsLabel();
204
205     /**
206      * Api to add document listener to field containing tweet text
207      * @param documentListener
208      */
209     public abstract void addTextFieldVerifier(@NotNull final DocumentListener documentListener);
210     
211   }
212 }