diff --git a/src/DotNetMashup.Web/Controllers/HomeController.cs b/src/DotNetMashup.Web/Controllers/HomeController.cs index 1ac5f03..4073581 100644 --- a/src/DotNetMashup.Web/Controllers/HomeController.cs +++ b/src/DotNetMashup.Web/Controllers/HomeController.cs @@ -31,6 +31,7 @@ namespace DotNetMashup.Web.Controllers this.setting = setting; } + [ResponseCache(Duration = 3600, NoStore = true, Location = ResponseCacheLocation.Any)] public async Task Index(int page = 1) { var factoryData = (await factory.GetData()); diff --git a/src/DotNetMashup.Web/Extensions/BclExtensions.cs b/src/DotNetMashup.Web/Extensions/BclExtensions.cs index 0ed68fa..9c7f25e 100644 --- a/src/DotNetMashup.Web/Extensions/BclExtensions.cs +++ b/src/DotNetMashup.Web/Extensions/BclExtensions.cs @@ -169,18 +169,11 @@ namespace DotNetMashup.Web.Extensions @"\s+[^\s]+$", string.Empty, RegexOptions.IgnoreCase | RegexOptions.Compiled) + trailingText; } - public static void ForEach(this IEnumerable enumeration, Action action) + public static string TrimAll(this string input) { - foreach(T item in enumeration) - { - action?.Invoke(item); - } - } - - public static IEnumerable> Split(this IEnumerable list, int parts) - { - var i = 0; - return list.GroupBy(a => i++ % parts).AsEnumerable(); + return new string(input.ToCharArray() + .Where(c => !char.IsWhiteSpace(c)) + .ToArray()); } } } \ No newline at end of file diff --git a/src/DotNetMashup.Web/Extensions/LinqExtensions.cs b/src/DotNetMashup.Web/Extensions/LinqExtensions.cs new file mode 100644 index 0000000..f30f290 --- /dev/null +++ b/src/DotNetMashup.Web/Extensions/LinqExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace DotNetMashup.Web.Extensions +{ + public static class LinqExtensions + { + public static void ForEach(this IEnumerable enumeration, Action action) + { + foreach(T item in enumeration) + { + action?.Invoke(item); + } + } + + public static IEnumerable> Split(this IEnumerable list, int parts) + { + var i = 0; + return list.GroupBy(a => i++ % parts).AsEnumerable(); + } + + //public static IEnumerable<> + } +} \ No newline at end of file diff --git a/src/DotNetMashup.Web/Extensions/TwitterExtensions.cs b/src/DotNetMashup.Web/Extensions/TwitterExtensions.cs new file mode 100644 index 0000000..7e83d5c --- /dev/null +++ b/src/DotNetMashup.Web/Extensions/TwitterExtensions.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; +using DotNetMashup.Web.Model; +using Tweetinvi; +using Tweetinvi.Core.Interfaces; +using Tweetinvi.Core.Interfaces.Models; + +namespace DotNetMashup.Web.Extensions +{ + public static class TwitterExtensions + { + public static TwitterData ToTwitterData(this ITweet tweet) + { + return new TwitterData + { + PublishedDate = tweet.CreatedAt, + Content = tweet.Text, + tweet = new Lazy>(() => tweet.GenerateOEmbedTweetAsync()), + Author = new Author + { + Name = tweet.CreatedBy.Name, + AuthorUrl = tweet.CreatedBy.Url, + Email = string.Empty, + ImageUrl = tweet.CreatedBy.ProfileImageUrl400x400 + } + }; + } + } +} \ No newline at end of file diff --git a/src/DotNetMashup.Web/Factory/RepositoryFactory.cs b/src/DotNetMashup.Web/Factory/RepositoryFactory.cs index b52e3a6..4c15fe8 100644 --- a/src/DotNetMashup.Web/Factory/RepositoryFactory.cs +++ b/src/DotNetMashup.Web/Factory/RepositoryFactory.cs @@ -33,7 +33,8 @@ namespace DotNetMashup.Web.Factory Repos = new List() { new GitHubRepository(config), - new BlogPostRepository(data, setting) + new BlogPostRepository(data, setting), + new TwitterRepository(setting, config) }; this.cache = cache; } diff --git a/src/DotNetMashup.Web/Global/ISiteSetting.cs b/src/DotNetMashup.Web/Global/ISiteSetting.cs index 13036ba..1de27e5 100644 --- a/src/DotNetMashup.Web/Global/ISiteSetting.cs +++ b/src/DotNetMashup.Web/Global/ISiteSetting.cs @@ -8,5 +8,6 @@ namespace DotNetMashup.Web.Global short AmountPerPage { get; } string Title { get; } string Descriptions { get; } + int TwitterMaxSearch { get; } } } \ No newline at end of file diff --git a/src/DotNetMashup.Web/Global/SiteSettings.cs b/src/DotNetMashup.Web/Global/SiteSettings.cs index 90934b2..8ca48fe 100644 --- a/src/DotNetMashup.Web/Global/SiteSettings.cs +++ b/src/DotNetMashup.Web/Global/SiteSettings.cs @@ -6,8 +6,9 @@ namespace DotNetMashup.Web.Global { public short AmountPerPage { get { return 12; } } - public List Categories { get; } = new List { "c#", "csharp", "cs", "asp.net", "NancyFx", "Nancy", "vNext", "asp.net 5" }; + public List Categories { get; } = new List { "c#", "csharp", "cs", "asp.net", "NancyFx", "vNext", "asp.net 5", "aspnet5" }; public string Title { get { return "DotNet Mashups"; } } public string Descriptions { get { return "Mashup of DotNet News"; } } + public int TwitterMaxSearch { get { return 10; } } } } \ No newline at end of file diff --git a/src/DotNetMashup.Web/Model/TwitterData.cs b/src/DotNetMashup.Web/Model/TwitterData.cs new file mode 100644 index 0000000..0371426 --- /dev/null +++ b/src/DotNetMashup.Web/Model/TwitterData.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Tweetinvi.Core.Interfaces.Models; + +namespace DotNetMashup.Web.Model +{ + public class TwitterData : BaseExternalData + { + public Lazy> tweet { get; set; } + } +} \ No newline at end of file diff --git a/src/DotNetMashup.Web/Repositories/TwitterRepository.cs b/src/DotNetMashup.Web/Repositories/TwitterRepository.cs index 2d7bb34..4c013b0 100644 --- a/src/DotNetMashup.Web/Repositories/TwitterRepository.cs +++ b/src/DotNetMashup.Web/Repositories/TwitterRepository.cs @@ -1,26 +1,63 @@ -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using System.Threading.Tasks; -//using DotNetMashup.Web.Model; -//using Tweetinvi; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using DotNetMashup.Web.Extensions; +using DotNetMashup.Web.Global; +using DotNetMashup.Web.Model; +using Microsoft.Framework.Configuration; +using Tweetinvi; +using Tweetinvi.Core.Credentials; +using Tweetinvi.Core.Enum; +using Tweetinvi.Core.Interfaces; +using Tweetinvi.Core.Parameters; -//namespace DotNetMashup.Web.Repositories -//{ -// public class TwitterRepository : IRepository -// { -// public string FactoryName -// { -// get -// { -// return "Twitter"; -// } -// } +namespace DotNetMashup.Web.Repositories +{ + public class TwitterRepository : IRepository + { + private readonly IConfiguration config; + private readonly ISiteSetting SiteSetting; + private Regex filter = new Regex("(job|Job|JOB|looking for|help|apply|Apply)", RegexOptions.Compiled); -// public Task> GetData() -// { -// throw new NotImplementedException(); -// var matchingTweets = Search.SearchTweets(); -// } -// } -//} + public TwitterRepository(ISiteSetting siteSetting, IConfiguration config) + { + this.SiteSetting = siteSetting; + this.config = config; + } + + public string FactoryName + { + get + { + return "Twitter"; + } + } + + public Task> GetData() + { + ExceptionHandler.SwallowWebExceptions = true; + var creds = new TwitterCredentials(config["twitterkey"], config["twittersecret"], config["twittertokenKey"], config["twittertokenSecret"]); + Auth.SetCredentials(creds); + + var tweets = SiteSetting + .Categories + .Select(a => a.TrimAll()) + .Where(a => !a.Equals("c#")) + .AsParallel() + .Select(a => Search.SearchTweets(new TweetSearchParameters("#" + a) { Lang = Language.English, MaximumNumberOfResults = 100, TweetSearchType = Tweetinvi.Core.Interfaces.Parameters.TweetSearchType.OriginalTweetsOnly })) + .SelectMany(a => a) + .Where(a => !a.IsRetweet && !filter.IsMatch(a.Text) && !a.Hashtags.Any(b => filter.IsMatch(b.Text))) + .GroupBy(a => a.CreatedAt.Day + a.CreatedAt.Year) + .SelectMany(a => a.Distinct().Take(3)) + .Select(a => a.ToTwitterData()) + .OrderByDescending(a => a.PublishedDate) + .Cast() + .ToList() + .AsEnumerable(); + + return Task.FromResult(tweets); + } + } +} \ No newline at end of file diff --git a/src/DotNetMashup.Web/Startup.cs b/src/DotNetMashup.Web/Startup.cs index 3790070..6a2796c 100644 --- a/src/DotNetMashup.Web/Startup.cs +++ b/src/DotNetMashup.Web/Startup.cs @@ -41,6 +41,7 @@ namespace DotNetMashup.Web { return new MemoryCache(new MemoryCacheOptions()); }); + services.AddCaching(); services.AddInstance(_feedData); services.AddSingleton(); // Add MVC services to the services container. diff --git a/src/DotNetMashup.Web/Views/Shared/DisplayTemplates/TwitterData.cshtml b/src/DotNetMashup.Web/Views/Shared/DisplayTemplates/TwitterData.cshtml new file mode 100644 index 0000000..488f911 --- /dev/null +++ b/src/DotNetMashup.Web/Views/Shared/DisplayTemplates/TwitterData.cshtml @@ -0,0 +1,9 @@ +@model DotNetMashup.Web.Model.TwitterData + + +
+
+ @Html.Raw((await Model.tweet.Value)?.HTML) +
+
+
\ No newline at end of file diff --git a/src/DotNetMashup.Web/Views/Shared/_Layout.cshtml b/src/DotNetMashup.Web/Views/Shared/_Layout.cshtml index 154df8e..9f67d50 100644 --- a/src/DotNetMashup.Web/Views/Shared/_Layout.cshtml +++ b/src/DotNetMashup.Web/Views/Shared/_Layout.cshtml @@ -7,7 +7,9 @@ - +
@@ -20,13 +22,30 @@

© 2015 - DotNetMashup.Web

- - - + + + - @RenderSection("scripts", required: false) diff --git a/src/DotNetMashup.Web/bower.json b/src/DotNetMashup.Web/bower.json index 4a41761..7d00d1e 100644 --- a/src/DotNetMashup.Web/bower.json +++ b/src/DotNetMashup.Web/bower.json @@ -2,5 +2,9 @@ "name": "ASP.NET", "private": true, "dependencies": { + "jquery": "~2.1.4", + "bootstrap": "~3.3.5", + "font-awesome": "~4.4.0", + "underscore": "~1.8.3" } } \ No newline at end of file diff --git a/src/DotNetMashup.Web/project.json b/src/DotNetMashup.Web/project.json index cc6461a..63a9feb 100644 --- a/src/DotNetMashup.Web/project.json +++ b/src/DotNetMashup.Web/project.json @@ -16,7 +16,7 @@ "Microsoft.Framework.Logging.Debug": "1.0.0-beta7", "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-beta7", "Newtonsoft.Json": "7.0.1", - "TweetinviAPI": "0.9.9.7", + "TweetinviAPI": "0.9.9.8", "Octokit": "0.16.0", "CommonMark.NET": "0.9.1" }, diff --git a/src/DotNetMashup.Web/wwwroot/_references.js b/src/DotNetMashup.Web/wwwroot/_references.js index 21e8929..ad7d373 100644 --- a/src/DotNetMashup.Web/wwwroot/_references.js +++ b/src/DotNetMashup.Web/wwwroot/_references.js @@ -1,245 +1,3 @@ /// /// /// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// diff --git a/src/DotNetMashup.Web/wwwroot/css/site.css b/src/DotNetMashup.Web/wwwroot/css/site.css index d32098f..d404905 100644 --- a/src/DotNetMashup.Web/wwwroot/css/site.css +++ b/src/DotNetMashup.Web/wwwroot/css/site.css @@ -77,3 +77,37 @@ code, pre { a.headlink, a.headlink:hover, a.headlink:visited{ color: #FFFFFF; text-align:center; } + +blockquote.twitter-tweet { + display: inline-block; + font-family: "Helvetica Neue", Roboto, "Segoe UI", Calibri, sans-serif; + font-size: 12px; + font-weight: bold; + line-height: 16px; + border-color: #eee #ddd #bbb; + border-radius: 5px; + border-style: solid; + border-width: 1px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); + margin: 10px 5px; + padding: 0 16px 16px 16px; + max-width: 468px; +} + +blockquote.twitter-tweet p { + font-size: 16px; + font-weight: normal; + line-height: 20px; +} + + blockquote.twitter-tweet a { + color: inherit; + font-weight: normal; + text-decoration: none; + outline: 0 none; + } + + blockquote.twitter-tweet a:hover, + blockquote.twitter-tweet a:focus { + text-decoration: underline; + } \ No newline at end of file