From 099ce4433792fca72a7cf32abb364f00344c8b4b Mon Sep 17 00:00:00 2001 From: Kimura Youichi Date: Fri, 28 Feb 2014 23:12:05 +0900 Subject: [PATCH] =?utf8?q?TweetFormatter=E3=81=AB=E3=82=88=E3=81=A3?= =?utf8?q?=E3=81=A6=E5=87=BA=E5=8A=9B=E3=81=95=E3=82=8C=E3=82=8BHTML?= =?utf8?q?=E3=81=AE=E5=A4=89=E5=8C=96=E3=81=ABCreateRetweetUnofficial?= =?utf8?q?=E3=83=A1=E3=82=BD=E3=83=83=E3=83=89=E3=81=8C=E8=BF=BD=E5=BE=93?= =?utf8?q?=E3=81=A7=E3=81=8D=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84=E5=95=8F?= =?utf8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- OpenTween.Tests/TweenMainText.cs | 83 +++++++++++++++++++++++++++++++++++++++ OpenTween/Resources/ChangeLog.txt | 1 + OpenTween/Tween.cs | 53 +++++++++++++++---------- 3 files changed, 116 insertions(+), 21 deletions(-) diff --git a/OpenTween.Tests/TweenMainText.cs b/OpenTween.Tests/TweenMainText.cs index cb6dd3d1..6106067c 100644 --- a/OpenTween.Tests/TweenMainText.cs +++ b/OpenTween.Tests/TweenMainText.cs @@ -81,5 +81,88 @@ namespace OpenTween Assert.Throws(() => TweenMain.GetUrlFromDataObject(data)); } } + + [Fact] + public void CreateRetweetUnofficial_UrlTest() + { + var statusText = "twitter.com"; + + Assert.Equal("http://twitter.com/", TweenMain.CreateRetweetUnofficial(statusText, false)); + } + + [Fact] + public void CreateRetweetUnofficial_MentionTest() + { + var statusText = "@TwitterAPI"; + + Assert.Equal("@TwitterAPI", TweenMain.CreateRetweetUnofficial(statusText, false)); + } + + [Fact] + public void CreateRetweetUnofficial_HashtagTest() + { + var statusText = "#OpenTween"; + + Assert.Equal("#OpenTween", TweenMain.CreateRetweetUnofficial(statusText, false)); + } + + [Fact] + public void CreateRetweetUnofficial_SingleLineTest() + { + var statusText = "123
456
789"; + + Assert.Equal("123 456 789", TweenMain.CreateRetweetUnofficial(statusText, false)); + } + + [Fact] + public void CreateRetweetUnofficial_MultiLineTest() + { + var statusText = "123
456
789"; + + Assert.Equal("123" + Environment.NewLine + "456" + Environment.NewLine + "789", TweenMain.CreateRetweetUnofficial(statusText, true)); + } + + [Fact] + public void CreateRetweetUnofficial_DecodeTest() + { + var statusText = "<>"' "; + + Assert.Equal("<>\"' ", TweenMain.CreateRetweetUnofficial(statusText, false)); + } + + [Fact] + public void CreateRetweetUnofficial_WithFormatterTest() + { + // TweetFormatterでHTMLに整形 → CreateRetweetUnofficialで復元 までの動作が正しく行えているか + + var text = "#てすと @TwitterAPI \n http://t.co/KYi7vMZzRt"; + var entities = new TwitterDataModel.Entity[] + { + new TwitterDataModel.Hashtags + { + Indices = new[] { 0, 4 }, + Text = "てすと", + }, + new TwitterDataModel.UserMentions + { + Indices = new[] { 5, 16 }, + Id = 6253282L, + Name = "Twitter API", + ScreenName = "twitterapi", + }, + new TwitterDataModel.Urls + { + Indices = new[] { 19, 41 }, + DisplayUrl = "twitter.com", + ExpandedUrl = "http://twitter.com/", + Url = "http://t.co/KYi7vMZzRt", + }, + }; + + var html = TweetFormatter.AutoLinkHtml(text, entities); + + var expected = "#てすと @TwitterAPI " + Environment.NewLine + " http://twitter.com/"; + Assert.Equal(expected, TweenMain.CreateRetweetUnofficial(html, true)); + } } } diff --git a/OpenTween/Resources/ChangeLog.txt b/OpenTween/Resources/ChangeLog.txt index b24b5620..f6649e65 100644 --- a/OpenTween/Resources/ChangeLog.txt +++ b/OpenTween/Resources/ChangeLog.txt @@ -4,6 +4,7 @@ * CHG: ブラウザからのD&Dで入力されるテキストの動作を変更しました - 投稿欄に既にテキストが入力されている場合は間に半角スペースを挿入、空であればD&DされたページのタイトルとURLのみを挿入します * FIX: 高DPI設定の環境下で発言詳細部のプロフィール画像に余白が表示されてしまう問題を修正 (thx @5px!) + * FIX: URLやハッシュタグ等を含むツイートを非公式RTする際に、HTMLの断片が投稿欄に入力されてしまう問題を修正 ==== Ver 1.1.8(2014/02/23) * NEW: ファイルメニューに「ツイートURLを開く」を追加 diff --git a/OpenTween/Tween.cs b/OpenTween/Tween.cs index a76dd4f7..0c605c0d 100644 --- a/OpenTween/Tween.cs +++ b/OpenTween/Tween.cs @@ -10812,9 +10812,12 @@ namespace OpenTween return; } string rtdata = _curPost.Text; - rtdata = CreateRetweetUnofficial(rtdata); + rtdata = CreateRetweetUnofficial(rtdata, this.StatusText.Multiline); - StatusText.Text = "RT @" + _curPost.ScreenName + ": " + WebUtility.HtmlDecode(rtdata); + this._reply_to_id = null; + this._reply_to_name = null; + + StatusText.Text = "RT @" + _curPost.ScreenName + ": " + rtdata; StatusText.SelectionStart = 0; StatusText.Focus(); @@ -10918,26 +10921,34 @@ namespace OpenTween } } - private string CreateRetweetUnofficial(string status) - { - // Twitterにより省略されているURLを含むaタグをキャプチャしてリンク先URLへ置き換える - status = Regex.Replace(status, @"[^""]+)"" title=""(?[^""]+)""[^>]*>(?<link>[^<]+)</a>", "${title}"); - - //その他のリンク(@IDなど)を置き換える - status = Regex.Replace(status, @"@<a target=""_self"" href=""https?://twitter.com/(#!/)?(?<url>[^""]+)""[^>]*>(?<link>[^<]+)</a>", "@${url}"); - //ハッシュタグ - status = Regex.Replace(status, @"<a target=""_self"" href=""(?<url>[^""]+)""[^>]*>(?<link>[^<]+)</a>", "${link}"); - //<br>タグ除去 - if (StatusText.Multiline) - status = Regex.Replace(status, @"(\r\n|\n|\r)?<br>", "\r\n", RegexOptions.IgnoreCase | RegexOptions.Multiline); + /// <summary> + /// TweetFormatterクラスによって整形された状態のHTMLを、非公式RT用に元のツイートに復元します + /// </summary> + /// <param name="statusHtml">TweetFormatterによって整形された状態のHTML</param> + /// <param name="multiline">trueであればBRタグを改行に、falseであればスペースに変換します</param> + /// <returns>復元されたツイート本文</returns> + internal static string CreateRetweetUnofficial(string statusHtml, bool multiline) + { + // TweetFormatterクラスによって整形された状態のHTMLを元のツイートに復元します + + // 通常の URL + statusHtml = Regex.Replace(statusHtml, "<a href=\"(?<href>.+?)\" title=\"(?<title>.+?)\">(?<text>.+?)</a>", "${title}"); + // メンション + statusHtml = Regex.Replace(statusHtml, "<a class=\"mention\" href=\"(?<href>.+?)\">(?<text>.+?)</a>", "${text}"); + // ハッシュタグ + statusHtml = Regex.Replace(statusHtml, "<a class=\"hashtag\" href=\"(?<href>.+?)\">(?<text>.+?)</a>", "${text}"); + + // <br> 除去 + if (multiline) + statusHtml = statusHtml.Replace("<br>", Environment.NewLine); else - status = Regex.Replace(status, @"(\r\n|\n|\r)?<br>", " ", RegexOptions.IgnoreCase | RegexOptions.Multiline); + statusHtml = statusHtml.Replace("<br>", " "); - _reply_to_id = null; - _reply_to_name = null; - status = status.Replace(" ", " "); + //   は本来であれば U+00A0 (NON-BREAK SPACE) に置換すべきですが、 + // 現状では半角スペースの代用として   を使用しているため U+0020 に置換します + statusHtml = statusHtml.Replace(" ", " "); - return status; + return WebUtility.HtmlDecode(statusHtml); } private void DumpPostClassToolStripMenuItem_Click(object sender, EventArgs e) @@ -11406,9 +11417,9 @@ namespace OpenTween return; } string rtdata = _curPost.Text; - rtdata = CreateRetweetUnofficial(rtdata); + rtdata = CreateRetweetUnofficial(rtdata, this.StatusText.Multiline); - StatusText.Text = " QT @" + _curPost.ScreenName + ": " + WebUtility.HtmlDecode(rtdata); + StatusText.Text = " QT @" + _curPost.ScreenName + ": " + rtdata; if (_curPost.RetweetedId == null) { _reply_to_id = _curPost.StatusId; -- 2.11.0