Update tweeting api in study plugins
authorValentina Kiryushkina <valentina.kiryushkina@jetbrains.com>
Mon, 21 Mar 2016 12:14:48 +0000 (15:14 +0300)
committerliana.bakradze <liana.bakradze@jetbrains.com>
Fri, 8 Apr 2016 11:22:48 +0000 (14:22 +0300)
* Create separate extension point to configure twitter
* Show tweet panel before asking to authorize
* Add input verifier to twitter panel
(cherry picked from commit f7f46eb)

python/educational-core/student/resources/META-INF/plugin.xml
python/educational-core/student/src/com/jetbrains/edu/learning/StudyBasePluginConfigurator.java
python/educational-core/student/src/com/jetbrains/edu/learning/StudyPluginConfigurator.java
python/educational-core/student/src/com/jetbrains/edu/learning/StudyTwitterPluginConfigurator.java [new file with mode: 0644]
python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java
python/educational-core/student/src/com/jetbrains/edu/learning/twitter/StudyTwitterAction.java
python/educational-core/student/src/com/jetbrains/edu/learning/twitter/StudyTwitterUtils.java

index 68aca8e54232447e342b9cf920957c50a13b1f2e..c413fdbf17bef54a9df83d3095672393a6dc3cf5 100644 (file)
@@ -37,6 +37,7 @@
     </extensionPoint>
     <extensionPoint qualifiedName="Edu.studyPluginConfigurator" interface="com.jetbrains.edu.learning.StudyPluginConfigurator"/>
     <extensionPoint qualifiedName="Edu.studyActionsProvider" interface="com.jetbrains.edu.learning.StudyActionsProvider"/>
+    <extensionPoint qualifiedName="Edu.studyTwitterPluginConfigurator" interface="com.jetbrains.edu.learning.StudyTwitterPluginConfigurator"/>
   </extensionPoints>
 
   <actions>
index cd204d70c63c8c46ac404ba2d4b65adcab0a6124..4fde0a889748b22bb30b9322bfe80f859445f2b1 100644 (file)
@@ -7,10 +7,8 @@ import com.intellij.openapi.fileEditor.FileEditorManagerListener;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.jetbrains.edu.learning.actions.*;
-import com.jetbrains.edu.learning.courseFormat.StudyStatus;
 import com.jetbrains.edu.learning.courseFormat.Task;
 import com.jetbrains.edu.learning.courseFormat.TaskFile;
-import com.jetbrains.edu.learning.twitter.StudyTwitterUtils;
 import com.jetbrains.edu.learning.ui.StudyToolWindow;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -97,44 +95,4 @@ public abstract class StudyBasePluginConfigurator implements StudyPluginConfigur
   public StudyAfterCheckAction[] getAfterCheckActions() {
     return null;
   }
-
-  @NotNull
-  @Override
-  public String getConsumerKey(@NotNull Project project) {
-    return "";
-  }
-
-  @NotNull
-  @Override
-  public String getConsumerSecret(@NotNull Project project) {
-    return "";
-  }
-
-  @Override
-  public void storeTwitterTokens(@NotNull Project project, @NotNull String accessToken, @NotNull String tokenSecret) {
-    // do nothing
-  }
-  
-  @NotNull
-  @Override
-  public String getTwitterTokenSecret(@NotNull Project project) {
-    return "";
-  }
-
-  @NotNull
-  @Override
-  public String getTwitterAccessToken(@NotNull Project project) {
-    return "";
-  }
-
-  @Override
-  public boolean askToTweet(@NotNull Project project, Task solvedTask, StudyStatus statusBeforeCheck) {
-    return false;
-  }
-
-  @Nullable
-  @Override
-  public StudyTwitterUtils.TwitterDialogPanel getTweetDialogPanel(@NotNull Task solvedTask) {
-    return null;
-  }
 }
index 4aa1f272acb539cd826e74840c496b168ad9c118..09cddf9b8e28b39acaf51a5159d0d5a094ed990a 100644 (file)
@@ -5,10 +5,7 @@ import com.intellij.openapi.extensions.ExtensionPointName;
 import com.intellij.openapi.fileEditor.FileEditorManagerListener;
 import com.intellij.openapi.project.Project;
 import com.jetbrains.edu.learning.actions.StudyAfterCheckAction;
-import com.jetbrains.edu.learning.courseFormat.StudyStatus;
-import com.jetbrains.edu.learning.courseFormat.Task;
 import com.jetbrains.edu.learning.settings.ModifiableSettingsPanel;
-import com.jetbrains.edu.learning.twitter.StudyTwitterUtils;
 import com.jetbrains.edu.learning.ui.StudyToolWindow;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -21,7 +18,6 @@ public interface StudyPluginConfigurator {
 
   /**
    * Provide action group that should be placed on the tool window toolbar.
-   * @return
    */
   @NotNull
   DefaultActionGroup getActionGroup(Project project);
@@ -37,7 +33,6 @@ public interface StudyPluginConfigurator {
   FileEditorManagerListener getFileEditorManagerListener(@NotNull final Project project, @NotNull final StudyToolWindow toolWindow);
 
   /**
-   *
    * @return parameter for CodeMirror script. Available languages: @see <@linktourl http://codemirror.net/mode/>
    */
   @NotNull String getDefaultHighlightingMode();
@@ -49,47 +44,6 @@ public interface StudyPluginConfigurator {
 
   @Nullable
   ModifiableSettingsPanel getSettingsPanel();
-
-  /**
-   * To implement tweeting you should register you app in twitter. For registered application twitter provide
-   * consumer key and consumer secret, that are used for authorize by OAuth.
-   * @return consumer key for current educational plugin
-   */
-  @NotNull String getConsumerKey(@NotNull final Project project);
-
-  /**
-   * To implement tweeting you should register you app in twitter. For registered application twitter provide
-   * consumer key and consumer secret, that are used for authorize by OAuth.
-   * @return consumer secret for current educational plugin
-   */
-  @NotNull String getConsumerSecret(@NotNull final Project project);
-
-  /**
-   * The plugin implemented tweeting should define policy when user will be asked to tweet.
-   *@param statusBeforeCheck @return 
-   */
-  boolean askToTweet(@NotNull final Project project, Task solvedTask, StudyStatus statusBeforeCheck);
-  
-  /**
-   * Stores access token and token secret, obtained by authorizing PyCharm.
-   */
-  void storeTwitterTokens(@NotNull final Project project, @NotNull final String accessToken, @NotNull final String tokenSecret);
-
-  /**
-   * @return stored access token
-   */
-  @NotNull String getTwitterAccessToken(@NotNull Project project);
-
-  /**
-   * @return stored token secret
-   */
-  @NotNull String getTwitterTokenSecret(@NotNull Project project);
-
-  /**
-   * @return panel that will be shown to user in ask to tweet dialog. 
-   */
-  @Nullable
-  StudyTwitterUtils.TwitterDialogPanel getTweetDialogPanel(@NotNull Task solvedTask);
   
   boolean accept(@NotNull final Project project);
 }
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTwitterPluginConfigurator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyTwitterPluginConfigurator.java
new file mode 100644 (file)
index 0000000..ea9b8b4
--- /dev/null
@@ -0,0 +1,59 @@
+package com.jetbrains.edu.learning;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import com.jetbrains.edu.learning.courseFormat.StudyStatus;
+import com.jetbrains.edu.learning.courseFormat.Task;
+import com.jetbrains.edu.learning.twitter.StudyTwitterUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public interface StudyTwitterPluginConfigurator {
+  ExtensionPointName<StudyTwitterPluginConfigurator> EP_NAME = ExtensionPointName.create("Edu.studyTwitterPluginConfigurator");
+
+  /**
+   * To implement tweeting you should register you app in twitter. For registered application twitter provide
+   * consumer key and consumer secret, that are used for authorize by OAuth.
+   * @return consumer key for current educational plugin
+   */
+  @NotNull
+  String getConsumerKey(@NotNull final Project project);
+
+  /**
+   * To implement tweeting you should register you app in twitter. For registered application twitter provide
+   * consumer key and consumer secret, that are used for authorize by OAuth.
+   * @return consumer secret for current educational plugin
+   */
+  @NotNull String getConsumerSecret(@NotNull final Project project);
+
+  /**
+   * The plugin implemented tweeting should define policy when user will be asked to tweet.
+   *@param statusBeforeCheck @return 
+   */
+  boolean askToTweet(@NotNull final Project project, Task solvedTask, StudyStatus statusBeforeCheck);
+
+  /**
+   * Stores access token and token secret, obtained by authorizing PyCharm.
+   */
+  void storeTwitterTokens(@NotNull final Project project, @NotNull final String accessToken, @NotNull final String tokenSecret);
+
+  /**
+   * @return stored access token
+   */
+  @NotNull String getTwitterAccessToken(@NotNull Project project);
+
+  /**
+   * @return stored token secret
+   */
+  @NotNull String getTwitterTokenSecret(@NotNull Project project);
+
+  /**
+   * @return panel that will be shown to user in ask to tweet dialog. 
+   */
+  @Nullable
+  StudyTwitterUtils.TwitterDialogPanel getTweetDialogPanel(@NotNull Task solvedTask);
+  
+  void setAskToTweet(@NotNull Project project, boolean askToTweet);
+
+  boolean accept(@NotNull final Project project);
+}
index 80fbb6a339b4f6213a8de795ef3a43985be5427f..f0d68010a76dcc1d64867aea7d81e1852377c0dd 100644 (file)
@@ -36,12 +36,12 @@ import com.intellij.ui.JBColor;
 import com.intellij.ui.awt.RelativePoint;
 import com.intellij.ui.content.Content;
 import com.intellij.util.ui.UIUtil;
+import com.jetbrains.edu.learning.checker.StudyExecutor;
+import com.jetbrains.edu.learning.checker.StudyTestRunner;
 import com.jetbrains.edu.learning.core.EduAnswerPlaceholderDeleteHandler;
 import com.jetbrains.edu.learning.core.EduAnswerPlaceholderPainter;
 import com.jetbrains.edu.learning.core.EduNames;
 import com.jetbrains.edu.learning.core.EduUtils;
-import com.jetbrains.edu.learning.checker.StudyExecutor;
-import com.jetbrains.edu.learning.checker.StudyTestRunner;
 import com.jetbrains.edu.learning.courseFormat.*;
 import com.jetbrains.edu.learning.editor.StudyEditor;
 import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory;
@@ -464,6 +464,18 @@ public class StudyUtils {
     return null;
   }
 
+  @Nullable
+  public static StudyTwitterPluginConfigurator getTwitterConfigurator(@NotNull final Project project) {
+    StudyTwitterPluginConfigurator[] extensions = StudyTwitterPluginConfigurator.EP_NAME.getExtensions();
+    for (StudyTwitterPluginConfigurator extension: extensions) {
+      if (extension.accept(project)) {
+        return extension;
+      }
+    }
+    return null;
+  }
+
+
   public static String getTaskText(@NotNull final Project project) {
     TaskFile taskFile = getSelectedTaskFile(project);
     if (taskFile == null) {
index dd74afa0d273c4ea6eda9bf0c22a8b1c4810cf78..b07a411d0b56c8654b9d0d33eed9bfd66c86c810 100644 (file)
@@ -2,14 +2,11 @@ package com.jetbrains.edu.learning.twitter;
 
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.project.Project;
-import com.jetbrains.edu.learning.StudyPluginConfigurator;
-import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.StudyTwitterPluginConfigurator;
 import com.jetbrains.edu.learning.actions.StudyAfterCheckAction;
 import com.jetbrains.edu.learning.courseFormat.StudyStatus;
 import com.jetbrains.edu.learning.courseFormat.Task;
 import org.jetbrains.annotations.NotNull;
-import twitter4j.Twitter;
-import twitter4j.TwitterException;
 
 /**
  * Action that provide tweeting functionality to plugin.
@@ -20,30 +17,17 @@ import twitter4j.TwitterException;
  */
 public class StudyTwitterAction extends StudyAfterCheckAction {
   Logger LOG = Logger.getInstance(StudyTwitterAction.class);
+  private StudyTwitterPluginConfigurator myConfigurator;
+
+  public StudyTwitterAction(@NotNull final StudyTwitterPluginConfigurator configurator) {
+    myConfigurator = configurator;
+  }
+
   @Override
   public void run(@NotNull Project project, @NotNull Task solvedTask, StudyStatus statusBeforeCheck) {
-    try {
-      StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
-      if (configurator == null) {
-        LOG.warn("Plugin configurator not found");
-        return;
-      }
-      
-      if (configurator.askToTweet(project, solvedTask, statusBeforeCheck)) {
-        boolean isAuthorized = !configurator.getTwitterAccessToken(project).isEmpty();
-        Twitter twitter = StudyTwitterUtils.getTwitter(configurator.getConsumerKey(project), configurator.getConsumerSecret(project));
-        StudyTwitterUtils.configureTwitter(twitter, project, isAuthorized);
-        StudyTwitterUtils.TwitterDialogPanel panel = configurator.getTweetDialogPanel(solvedTask);
-        if (panel != null) {
-          StudyTwitterUtils.showPostTweetDialogAndPostTweet(twitter, panel);
-        }
-        else {
-          LOG.warn("Plugin didn't provide twitter panel");          
-        }
-      } 
-    }
-    catch (TwitterException e) {
-      LOG.warn(e.getMessage());
+
+    if (myConfigurator.askToTweet(project, solvedTask, statusBeforeCheck)) {
+      StudyTwitterUtils.createTwitterDialogAndShow(project, myConfigurator, solvedTask);
     }
   }
 }
index 3fdab6b6a7618f4774b465f1aa4bb8eb8a74bf73..1192e7bea612011bdf569cd97c2c73a7f3da8d18 100644 (file)
@@ -4,13 +4,17 @@ import com.intellij.ide.BrowserUtil;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.DialogBuilder;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.InputValidatorEx;
 import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.ui.DocumentAdapter;
 import com.intellij.ui.components.JBScrollPane;
-import com.jetbrains.edu.learning.StudyPluginConfigurator;
+import com.jetbrains.edu.learning.StudyTwitterPluginConfigurator;
 import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.courseFormat.Task;
+import org.apache.http.HttpStatus;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import twitter4j.StatusUpdate;
@@ -24,6 +28,7 @@ import twitter4j.conf.ConfigurationBuilder;
 import javax.swing.*;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
+import java.awt.*;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -32,32 +37,10 @@ import java.io.InputStream;
 
 public class StudyTwitterUtils {
   private static final Logger LOG = Logger.getInstance(StudyTwitterUtils.class);
-
-  /**
-   * Configure twitter instance: authorize if needed or set access token and token secret provided by configurator.
-   * @param twitter
-   * @param project
-   * @param isAuthorized
-   * @throws TwitterException
-   */
-  public static void configureTwitter(@NotNull final Twitter twitter, @NotNull final Project project,
-                                         final boolean isAuthorized) throws TwitterException {
-    if (!isAuthorized) {
-      authorize(project, twitter);
-    }
-    else {
-      StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
-      if (configurator != null) {
-        getTwitterForAuthorizedApp(twitter, configurator.getTwitterAccessToken(project), configurator.getTwitterTokenSecret(project));
-      }
-    }
-  }
-
+  
   /**
    * Set consumer key and secret. 
-   * @param consumerKey
-   * @param consumerSecret
-   * @return
+   * @return Twitter instance with consumer key and secret set.
    */
   @NotNull
   public static Twitter getTwitter(@NotNull final String consumerKey, @NotNull final String consumerSecret) {
@@ -69,116 +52,222 @@ public class StudyTwitterUtils {
 
   /**
    * Set access token and token secret in Twitter instance
-   * @param twitter
-   * @param accessToken
-   * @param tokenSecret
    */
-  private static void getTwitterForAuthorizedApp(Twitter twitter, @NotNull String accessToken,
-                                                @NotNull String tokenSecret) {
+  private static void setAuthInfoInTwitter(Twitter twitter, @NotNull String accessToken,
+                                           @NotNull String tokenSecret) {
     AccessToken token = new AccessToken(accessToken, tokenSecret);
     twitter.setOAuthAccessToken(token);
   }
 
-  /**
-   * Authorize user and save tokens by StudyPluginConfigurator#storeTwitterTokens
-   * @param project
-   * @param twitter
-   * @throws TwitterException
-   */
-  public static void authorize(@NotNull final Project project, @NotNull final Twitter twitter) throws TwitterException {
-    RequestToken requestToken = twitter.getOAuthRequestToken();
-    BrowserUtil.browse(requestToken.getAuthorizationURL());
-
+  public static void createTwitterDialogAndShow(@NotNull Project project, 
+                                                @NotNull final StudyTwitterPluginConfigurator configurator,
+                                                @NotNull Task task) {
     ApplicationManager.getApplication().invokeLater(() -> {
-      String pin = Messages.showInputDialog("Twitter PIN:", "Twitter Authorization", null, "", null);
-      try {
-        AccessToken token;
-        if (pin != null && pin.length() > 0) {
-          token = twitter.getOAuthAccessToken(requestToken, pin);
-        }
-        else {
-          token = twitter.getOAuthAccessToken();
-        }
-        StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
-        if (configurator != null) {
-          configurator.storeTwitterTokens(project, token.getToken(), token.getTokenSecret());
+      DialogWrapper.DoNotAskOption doNotAskOption = createDoNotAskOption(project, configurator);
+      StudyTwitterUtils.TwitterDialogPanel panel = configurator.getTweetDialogPanel(task);
+      if (panel != null) {
+        TwitterDialogWrapper wrapper = new TwitterDialogWrapper(project, panel, doNotAskOption);
+        wrapper.setDoNotAskOption(doNotAskOption);
+        panel.addTextFieldVerifier(createTextFieldLengthDocumentListener(wrapper, panel));
+
+        if (wrapper.showAndGet()) {
+          try {
+            boolean isAuthorized = !configurator.getTwitterAccessToken(project).isEmpty();
+            Twitter twitter = getTwitter(configurator.getConsumerKey(project), configurator.getConsumerSecret(project));
+            if (!isAuthorized) {
+              authorizeAndUpdateStatus(project, twitter, panel);
+            }
+            else {
+              setAuthInfoInTwitter(twitter, configurator.getTwitterAccessToken(project), configurator.getTwitterTokenSecret(project));
+              updateStatus(panel, twitter);
+            }
+          }
+          catch (TwitterException | IOException e) {
+            LOG.warn(e.getMessage());
+            Messages.showErrorDialog("Status wasn\'t updated. Please, check internet connection and try again", "Twitter");
+          }
         }
         else {
-          LOG.warn("Plugin configurator not found");
+          LOG.warn("Panel is null");
         }
       }
-      catch (TwitterException e) {
-        if (401 == e.getStatusCode()) {
-          LOG.warn("Unable to get the access token.");
-        }
-        else {
-          LOG.warn(e.getMessage());
+    });
+  }
+
+
+  private static DialogWrapper.DoNotAskOption createDoNotAskOption(@NotNull final Project project,
+                                                                   @NotNull final StudyTwitterPluginConfigurator configurator) {
+    return new DialogWrapper.DoNotAskOption() {
+      @Override
+      public boolean isToBeShown() {
+        return true;
+      }
+
+      @Override
+      public void setToBeShown(boolean toBeShown, int exitCode) {
+        if (exitCode == DialogWrapper.CANCEL_EXIT_CODE || exitCode == DialogWrapper.OK_EXIT_CODE) {
+          configurator.setAskToTweet(project, toBeShown);
         }
       }
-    });
+
+      @Override
+      public boolean canBeHidden() {
+        return true;
+      }
+
+      @Override
+      public boolean shouldSaveOptionsOnCancel() {
+        return true;
+      }
+
+      @NotNull
+      @Override
+      public String getDoNotShowMessage() {
+        return "Never ask me to tweet";
+      }
+    };
+  }
+
+  /**
+   * Post on twitter media and text from panel
+   * @param panel shown to user and used to provide data to post 
+   */
+  public static void updateStatus(StudyTwitterUtils.TwitterDialogPanel panel, Twitter twitter) throws IOException, TwitterException {
+    StatusUpdate update = new StatusUpdate(panel.getMessage());
+    InputStream e = panel.getMediaSource();
+    if (e != null) {
+      File imageFile = FileUtil.createTempFile("twitter_media", panel.getMediaExtension());
+      FileUtil.copy(e, new FileOutputStream(imageFile));
+      update.media(imageFile);
+    }
+
+    twitter.updateStatus(update);
+    BrowserUtil.browse("https://twitter.com/");
   }
 
   /**
    * Show twitter dialog, asking user to tweet about his achievements. Post tweet with provided by panel
    * media and text. 
    * As a result of succeeded tweet twitter website is opened in default browser.
-   * @param twitter 
-   * @param twitterDialogPanel 
    */
-  public static void showPostTweetDialogAndPostTweet(@NotNull Twitter twitter, @NotNull final TwitterDialogPanel twitterDialogPanel) {
+  public static void authorizeAndUpdateStatus(@NotNull final Project project, @NotNull final Twitter twitter,
+                                              @NotNull final StudyTwitterUtils.TwitterDialogPanel panel) throws TwitterException {
+    RequestToken requestToken = twitter.getOAuthRequestToken();
+    BrowserUtil.browse(requestToken.getAuthorizationURL());
+
     ApplicationManager.getApplication().invokeLater(() -> {
-      DialogBuilder builder = new DialogBuilder();
-      twitterDialogPanel.addTextFieldVerifier(createTextFieldLengthDocumentListener(builder, twitterDialogPanel));
-      builder.title("Twitter");
-      builder.addOkAction().setText("Tweet");
-      builder.addCancelAction();
-      builder.setCenterPanel(new JBScrollPane(twitterDialogPanel));
-      builder.resizable(true);
-      if (builder.showAndGet()) {
-        StatusUpdate update = new StatusUpdate(twitterDialogPanel.getMessage());
+      String pin = createAndShowPinDialog();
+      if (pin != null) {
         try {
-          InputStream inputStream = twitterDialogPanel.getMediaSource();
-          if (inputStream != null) {
-            File imageFile = FileUtil.createTempFile("twitter_media", "gif");
-            
-            FileUtil.copy(inputStream, new FileOutputStream(imageFile));
-            update.media(imageFile);
+          AccessToken token = twitter.getOAuthAccessToken(requestToken, pin);
+          StudyTwitterPluginConfigurator configurator = StudyUtils.getTwitterConfigurator(project);
+          if (configurator != null) {
+            configurator.storeTwitterTokens(project, token.getToken(), token.getTokenSecret());
+            updateStatus(panel, twitter);
+          }
+          else {
+            LOG.warn("No twitter configurator is provided for the plugin");
           }
-          twitter.updateStatus(update);
-          BrowserUtil.browse("https://twitter.com/");
         }
-        catch (IOException | TwitterException e) {
+        catch (TwitterException e) {
+          if (e.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
+            LOG.warn("Unable to get the access token.");
+            LOG.warn(e.getMessage());
+          }
+        }
+        catch (IOException e) {
           LOG.warn(e.getMessage());
-          Messages.showErrorDialog("Status wasn't updated. Please, check internet connection and try again", "Twitter");
         }
       }
     });
   }
 
+  public static String createAndShowPinDialog() {
+    return Messages.showInputDialog("Twitter PIN:", "Twitter Authorization", null, "", new InputValidatorEx() {
+      @Nullable
+      @Override
+      public String getErrorText(String inputString) {
+        inputString = inputString.trim();
+        if (inputString.isEmpty()) {
+          return "PIN shouldn't be empty.";
+        }
+        if (!isNumeric(inputString)) {
+          return "PIN should be numeric.";
+        }
+        return null;
+      }
+
+      @Override
+      public boolean checkInput(String inputString) {
+        return getErrorText(inputString) == null;
+      }
+
+      @Override
+      public boolean canClose(String inputString) {
+        return true;
+      }
+      
+      private boolean isNumeric(@NotNull final String string) {
+        for (char c: string.toCharArray()) {
+          if (!StringUtil.isDecimalDigit(c)) {
+            return false;
+          }
+        }
+        return true;
+      }
+    });
+  }
+
   /**
    * Listener updates label indicating remaining symbols number like in twitter.
-   * @param builder
-   * @param panel
-   * @return
    */
-  public static DocumentListener createTextFieldLengthDocumentListener(@NotNull DialogBuilder builder, @NotNull final TwitterDialogPanel panel) {
+  private static DocumentListener createTextFieldLengthDocumentListener(@NotNull TwitterDialogWrapper builder, @NotNull final StudyTwitterUtils.TwitterDialogPanel panel) {
     return new DocumentAdapter() {
       @Override
       protected void textChanged(DocumentEvent e) {
         int length = e.getDocument().getLength();
         if (length > 140 || length == 0) {
-          builder.setOkActionEnabled(false);
+          builder.setOKActionEnabled(false);
           panel.getRemainSymbolsLabel().setText("<html><font color='red'>" + String.valueOf(140 - length) + "</font></html>");
-        }
-        else {
-          builder.setOkActionEnabled(true);
+        } else {
+          builder.setOKActionEnabled(true);
           panel.getRemainSymbolsLabel().setText(String.valueOf(140 - length));
         }
-        
+
       }
     };
   }
 
+  /**
+   * Dialog wrapper class with DoNotAsl option for asking user to tweet.
+   * */
+  private static class TwitterDialogWrapper extends DialogWrapper {
+    private StudyTwitterUtils.TwitterDialogPanel myPanel;
+
+    TwitterDialogWrapper(@Nullable Project project, @NotNull StudyTwitterUtils.TwitterDialogPanel panel, DoNotAskOption doNotAskOption) {
+      super(project);
+      setTitle("Twitter");
+      setDoNotAskOption(doNotAskOption);
+      setOKButtonText("Tweet");
+      setCancelButtonText("No");
+      setResizable(true);
+      Dimension preferredSize = panel.getPreferredSize();
+      setSize((int) preferredSize.getHeight(), (int) preferredSize.getWidth());
+      myPanel = panel;
+      init();
+    }
+
+    public void setOKActionEnabled(boolean isEnabled) {
+      super.setOKActionEnabled(isEnabled);
+    }
+
+    @Nullable
+    @Override
+    protected JComponent createCenterPanel() {
+      return new JBScrollPane(myPanel);
+    }
+  }
+
   /**
    * Class provides structure for twitter dialog panel
    */
@@ -186,25 +275,23 @@ public class StudyTwitterUtils {
 
     /**
      * Provides tweet text
-     * @return 
      */
     @NotNull public abstract String getMessage();
 
     /**
-     * 
      * @return Input stream of media should be posted or null if there's nothing to post 
      */
     @Nullable public abstract InputStream getMediaSource();
+    
+    @Nullable public abstract String getMediaExtension();
 
     /**
-     * 
      * @return label that will be used to show remained symbol number
      */
     @NotNull public abstract JLabel getRemainSymbolsLabel();
 
     /**
      * Api to add document listener to field containing tweet text
-     * @param documentListener
      */
     public abstract void addTextFieldVerifier(@NotNull final DocumentListener documentListener);