diff --git a/App_LocalResources/Edit.ascx.resx b/App_LocalResources/Edit.ascx.resx index f24c8a9..61f6d25 100644 --- a/App_LocalResources/Edit.ascx.resx +++ b/App_LocalResources/Edit.ascx.resx @@ -1,6 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Edit Categories - + Define Feeds - + Edit News - + BBNews Administration - + Manage Category Feeds - - Scheduler - + + Scheduler + + + Twitter + \ No newline at end of file diff --git a/App_LocalResources/EditCredentials.ascx.resx b/App_LocalResources/EditCredentials.ascx.resx new file mode 100644 index 0000000..c7d5e51 --- /dev/null +++ b/App_LocalResources/EditCredentials.ascx.resx @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Save + + + If you want to use the twitter functionality, you need to create an application at the <a href="https://dev.twitter.com/apps">Twitter developer area</a> and get your consumer and access tokens and secrets. + + + Please enter the "access token" of your twitter app + + + Access token + + + Please enter the "access token secret" of your twitter app + + + Access token secret + + + Please enter the "consumer key" of your twitter app + + + Consumer Key + + + Please enter the "consumer key" of your twitter app + + + Consumer secret + + \ No newline at end of file diff --git a/BBNews.csproj b/BBNews.csproj index 26cbb5e..5db5c86 100644 --- a/BBNews.csproj +++ b/BBNews.csproj @@ -1,6 +1,7 @@  + Debug AnyCPU @@ -13,7 +14,7 @@ Bitboxx.DNNModules.BBNews - 3.5 + 4.0 v3.5 @@ -139,7 +140,7 @@ ASPXCodeBehind - + TabSelectControl.ascx ASPXCodeBehind @@ -154,6 +155,13 @@ TemplateControl.ascx + + EditCredentials.ascx + ASPXCodeBehind + + + EditCredentials.ascx + EditScheduler.ascx ASPXCodeBehind @@ -234,12 +242,14 @@ + + @@ -298,9 +308,18 @@ - + + + Designer + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + - + + diff --git a/BBNews.sln b/BBNews.sln index 25a86b7..fd24fec 100644 --- a/BBNews.sln +++ b/BBNews.sln @@ -1,6 +1,8 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BBNews", "BBNews.csproj", "{45AE5FFE-8AB5-4319-B311-9A04C5A7F607}" EndProject Global diff --git a/Components/BBNewsController.cs b/Components/BBNewsController.cs index ec45cf7..1f80c0b 100644 --- a/Components/BBNewsController.cs +++ b/Components/BBNewsController.cs @@ -162,6 +162,11 @@ public void ReadFeed(int FeedId) XmlReader rssReader; SyndicationFeed feed; + string oAuthToken = DotNetNuke.Entities.Portals.PortalController.GetPortalSetting("BB_TwitterToken", feedInfo.PortalId, ""); + string oAuthTokenSecret = DotNetNuke.Entities.Portals.PortalController.GetPortalSetting("BB_TwitterTokenSecret", feedInfo.PortalId, ""); + string oAuthConsumerKey = DotNetNuke.Entities.Portals.PortalController.GetPortalSetting("BB_TwitterConsumerKey", feedInfo.PortalId, ""); + string oAuthConsumerSecret = DotNetNuke.Entities.Portals.PortalController.GetPortalSetting("BB_TwitterConsumerSecret", feedInfo.PortalId, ""); + try { switch (feedInfo.FeedType) @@ -169,72 +174,30 @@ public void ReadFeed(int FeedId) case 0: // None return; case 1: // Twitter Search - string url = String.Format("http://search.twitter.com/search.atom?q={0}", feedInfo.FeedUrl); - - wrq = (HttpWebRequest)WebRequest.Create(url); - if (ProxyServer != string.Empty) - wrq.Proxy = new WebProxy(ProxyServer + (ProxyPort != "-1" ? ":" + ProxyPort : "")); - - if (ProxyUserName != string.Empty) - wrq.Proxy.Credentials = new NetworkCredential(ProxyUserName, ProxyPassword); - - // Set UserAgent to avoid prohibited (403) answer - wrq.UserAgent = "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:15.0) Gecko/20100101 Firefox/15.0.1"; - - wrp = wrq.GetResponse(); - rssStream = wrp.GetResponseStream(); - - settings = new XmlReaderSettings(); - - rssReader = XmlReader.Create(rssStream,settings); - - feed = SyndicationFeed.Load(rssReader); - - foreach (var feedItem in feed.Items) - { - NewsInfo news = new NewsInfo(); - news.News = ""; - news.Internal = false; - news.Title = (feedItem.Title != null ? feedItem.Title.Text : ""); - news.Summary = feedItem.Content != null ? ((TextSyndicationContent)feedItem.Content).Text : ""; - //if (feedInfo.StripHtml) - // news.Summary = StripHTML(news.Summary); - news.Link = (feedItem.Links.Count > 0 ? feedItem.Links[0].Uri.OriginalString : ""); - news.Author = ""; - if (feedItem.Authors.Count > 0) - { - string name = feedItem.Authors[0].Name ?? " "; - string nick = ""; - if (name.IndexOf('(') > -1) - { - nick = name.Substring(0, name.IndexOf('(') - 1); - name = name.Substring(name.IndexOf('(') + 1); - name = name.Substring(0, name.Length - 1); - } - string uri = feedItem.Authors[0].Uri ?? " "; - string email = feedItem.Authors[0].Email ?? " "; - - news.Author = name + "|" + uri + "|" + email + "|" + nick; - } - - news.Image = (feedItem.Links.Count > 1 ? feedItem.Links[1].Uri.OriginalString : ""); - - DateTime pubDate = (feedItem.PublishDate.LocalDateTime != DateTime.MinValue ? feedItem.PublishDate.LocalDateTime : (DateTime)SqlDateTime.MinValue); - DateTime lastUpdated = (feedItem.LastUpdatedTime.LocalDateTime != DateTime.MinValue ? feedItem.LastUpdatedTime.LocalDateTime : (DateTime)SqlDateTime.MinValue); - news.Pubdate = (pubDate > lastUpdated ? pubDate : lastUpdated); - news.Pubdate = (news.Pubdate < (DateTime)SqlDateTime.MinValue ? (DateTime)SqlDateTime.MinValue : news.Pubdate); - - if (feedItem.Id != null) - news.GUID = feedItem.Id; - else - news.GUID = string.Format("{0:yyyyMMddHHmmss}", news.Pubdate) + - news.Title.ToUpper().Substring(0, Math.Min(news.Title.Length, 20)); - news.FeedId = feedInfo.FeedId; - this.SaveNewsByGuid(news); - } - feedInfo.LastRetrieve = DateTime.Now; - this.SaveFeed(feedInfo); - break; + + TwitterApi11 twitterApiSearch = new TwitterApi11(oAuthToken, oAuthTokenSecret, oAuthConsumerKey, oAuthConsumerSecret); + List newsListSearch = twitterApiSearch.SearchTweets(feedInfo.FeedUrl, 20); + foreach (NewsInfo news in newsListSearch) + { + news.FeedId = FeedId; + this.SaveNewsByGuid(news); + } + feedInfo.LastRetrieve = DateTime.Now; + this.SaveFeed(feedInfo); + break; + + case 3: // Twitter Timeline + + TwitterApi11 twitterApiUser = new TwitterApi11(oAuthToken, oAuthTokenSecret, oAuthConsumerKey, oAuthConsumerSecret); + List newsListUser = twitterApiUser.GetUserTimeLine(feedInfo.FeedUrl, 20); + foreach (NewsInfo news in newsListUser) + { + news.FeedId = FeedId; + this.SaveNewsByGuid(news); + } + feedInfo.LastRetrieve = DateTime.Now; + this.SaveFeed(feedInfo); + break; case 2: // RSS Uri baseUrl = new Uri(feedInfo.FeedUrl); @@ -261,7 +224,7 @@ public void ReadFeed(int FeedId) rssReader = XmlReader.Create(rssStream, settings); feed = SyndicationFeed.Load(rssReader); } - catch (IOException) + catch (Exception ex) { // zweiter Versuch string xml = string.Empty; @@ -293,9 +256,34 @@ public void ReadFeed(int FeedId) s.Close(); } - rssReader = System.Xml.XmlReader.Create(new StringReader(xml)); - feed = SyndicationFeed.Load(rssReader); - } + // if xml is not valid lets try to repair + try + { + try + { + rssReader = System.Xml.XmlReader.Create(new StringReader(xml)); + feed = SyndicationFeed.Load(rssReader); + } + catch (Exception) + { + xml = xml.Replace(" ", "&nbsp;") + .Replace("<", "&lt;") + .Replace(">", "&gt;") + .Replace("–", "&ndash;") + .Replace("“", "&ldquo;") + .Replace("”", "&rdquo;") + .Replace("’", "&rsquo;"); + rssReader = System.Xml.XmlReader.Create(new StringReader(xml)); + feed = SyndicationFeed.Load(rssReader); + } + } + catch (Exception) + { + xml = AddCDATA(xml); + rssReader = System.Xml.XmlReader.Create(new StringReader(xml)); + feed = SyndicationFeed.Load(rssReader); + } + } foreach (var feedItem in feed.Items) @@ -340,8 +328,7 @@ public void ReadFeed(int FeedId) this.SaveFeed(feedInfo); break; - case 3: - break; + } } catch (Exception ex) @@ -352,9 +339,19 @@ public void ReadFeed(int FeedId) this.SaveFeed(feedInfo); } } + + public string AddCDATA(string xml) + { + if (xml.IndexOf("") > -1 && xml.IndexOf("", "", "]]>"); + } + return xml; + } #endregion - #region "Optional Interfaces" + + #region "Optional Interfaces" /// ----------------------------------------------------------------------------- /// diff --git a/Components/Twitter.cs b/Components/Twitter.cs deleted file mode 100644 index 2531a57..0000000 --- a/Components/Twitter.cs +++ /dev/null @@ -1,127 +0,0 @@ -#region copyright - -// bitboxx - http://www.bitboxx.net -// Copyright (c) 2012 -// by bitboxx solutions Torsten Weggen -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -// documentation files (the "Software"), to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and -// to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or substantial portions -// of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#endregion - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Xml.Linq; -using System.Net; -using System.IO; - - -// http://search.twitter.com/search.atom?q=dnn&rpp=5 mal checken ! - -namespace Bitboxx.DNNModules.BBNews.Components -{ - public class Twitter - { - public string Username { get; set; } - public string Password { get; set; } - - public Twitter (string username, string password) - { - Username = username; - Password = password; - } - - - private string PerformRequest(string method, string url) - { - if (Username == string.Empty || Password == string.Empty) - throw new Exception("Credentials needed to resolve information!"); - - HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); - request.Method = method; - request.Credentials = new NetworkCredential(Username, Password); - WebResponse response = request.GetResponse(); - StreamReader reader = new StreamReader(response.GetResponseStream()); - string responseString = reader.ReadToEnd(); - reader.Close(); - return responseString; - } - - private string Post(string url) - { - return PerformRequest("POST", url); - } - - private string Get(string url) - { - return PerformRequest("GET", url); - } - - public List GetUserTimeLine(string user) - { - - string url = string.Format("http://twitter.com/statuses/user_timeline/{0}.xml", user); - string response = Get(url); - - XDocument document = XDocument.Parse(response, LoadOptions.None); - - var query = from e in document.Root.Descendants("status") - select new NewsInfo - { - Author = e.Element("user").Element("name").Value, - GUID = e.Element("id").Value, - Image = e.Element("user").Element("profile_image_url").Value, - Summary = "", - Link = e.Element("user").Element("url").Value, - Pubdate = ParseDateTime(e.Element("created_at").Value), - News = HttpUtility.HtmlDecode(e.Element("text").Value), - Internal = false, - Hide = false, - Title = HttpUtility.HtmlDecode(e.Element("text").Value) - }; - - List news = (from n in query - where n.News != "" - orderby n.Pubdate descending - select n).ToList(); - foreach (NewsInfo item in news) - { - int pos = item.Title.IndexOf("http://bit.ly"); - if (pos > 0) - { - item.Link = item.Title.Substring(pos); - item.Title = item.Title.Substring(0, pos - 1); - } - } - - return news; - } - - private DateTime ParseDateTime(string date) - { - string dayOfWeek = date.Substring(0, 3).Trim(); - string month = date.Substring(4, 3).Trim(); - string dayInMonth = date.Substring(8, 2).Trim(); - string time = date.Substring(11, 9).Trim(); - string offset = date.Substring(20, 5).Trim(); - string year = date.Substring(25, 5).Trim(); - string dateTime = string.Format("{0}-{1}-{2} {3}", dayInMonth, month, year, time); - DateTime ret = DateTime.Parse(dateTime); - return ret; - } - } -} diff --git a/Components/TwitterApi11.cs b/Components/TwitterApi11.cs new file mode 100644 index 0000000..d030eb7 --- /dev/null +++ b/Components/TwitterApi11.cs @@ -0,0 +1,297 @@ +#region copyright + +// bitboxx - http://www.bitboxx.net +// Copyright (c) 2014 +// by bitboxx solutions Torsten Weggen +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +// to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#endregion + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Web; +using System.Web.Script.Serialization; +using System.Xml.Linq; +using System.Net; +using System.IO; + +namespace Bitboxx.DNNModules.BBNews.Components +{ + public class TwitterApi11 + { + public string TokenKey { get; set; } + public string TokenSecret { get; set; } + public string ConsumerKey { get; set; } + public string ConsumerSecret { get; set; } + + private class User + { + public string name { get; set; } + public string screen_name { get; set; } + public string profile_image_url { get; set; } + public string url { get; set; } + } + + private class Tweet + { + public string created_at { get; set; } + public string id_str { get; set; } + public string text { get; set; } + public User user { get; set; } + } + + private class Tweets + { + public List statuses { get; set; } + } + public TwitterApi11 (string tokenKey, string tokenSecret, string consumerKey, string consumerSecret) + { + TokenKey = tokenKey; + TokenSecret = tokenSecret; + ConsumerKey = consumerKey; + ConsumerSecret = consumerSecret; + } + + + public List GetUserTimeLine(string twitterUsername, int tweetsCount) + { + // Other OAuth connection/authentication variables + string oAuthVersion = "1.0"; + string oAuthSignatureMethod = "HMAC-SHA1"; + string oAuthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())); + TimeSpan timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + string oAuthTimestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString(); + string resourceUrl = "https://api.twitter.com/1.1/statuses/user_timeline.json"; + + // Generate OAuth signature. Note that Twitter is very particular about the format of this string. Even reordering the variables + // will cause authentication errors. + + var baseFormat = "count={7}&oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}" + + "&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}&screen_name={6}"; + + var baseString = string.Format(baseFormat, + ConsumerKey, + oAuthNonce, + oAuthSignatureMethod, + oAuthTimestamp, + TokenKey, + oAuthVersion, + Uri.EscapeDataString(twitterUsername), + Uri.EscapeDataString(tweetsCount.ToString()) + ); + + baseString = string.Concat("GET&", Uri.EscapeDataString(resourceUrl), "&", Uri.EscapeDataString(baseString)); + + // Generate an OAuth signature using the baseString + var compositeKey = string.Concat(Uri.EscapeDataString(ConsumerSecret), "&", Uri.EscapeDataString(TokenSecret)); + string oAuthSignature; + using (HMACSHA1 hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(compositeKey))) + { + oAuthSignature = Convert.ToBase64String(hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes(baseString))); + } + + // Now build the Authentication header. Again, Twitter is very particular about the format. Do not reorder variables. + var headerFormat = "OAuth oauth_nonce=\"{0}\", oauth_signature_method=\"{1}\", " + + "oauth_timestamp=\"{2}\", oauth_consumer_key=\"{3}\", " + + "oauth_token=\"{4}\", oauth_signature=\"{5}\", " + + "oauth_version=\"{6}\""; + + var authHeader = string.Format(headerFormat, + Uri.EscapeDataString(oAuthNonce), + Uri.EscapeDataString(oAuthSignatureMethod), + Uri.EscapeDataString(oAuthTimestamp), + Uri.EscapeDataString(ConsumerKey), + Uri.EscapeDataString(TokenKey), + Uri.EscapeDataString(oAuthSignature), + Uri.EscapeDataString(oAuthVersion) + ); + + // Now build the actual request + + ServicePointManager.Expect100Continue = false; + var postBody = string.Format("screen_name={0}&count={1}", Uri.EscapeDataString(twitterUsername), Uri.EscapeDataString(tweetsCount.ToString())); + resourceUrl += "?" + postBody; + HttpWebRequest request = (HttpWebRequest) WebRequest.Create(resourceUrl); + request.Headers.Add("Authorization", authHeader); + request.Method = "GET"; + request.ContentType = "application/x-www-form-urlencoded"; + + // Retrieve the response data and deserialize the JSON data to a list of Tweet objects + WebResponse response = request.GetResponse(); + string responseData = new StreamReader(response.GetResponseStream()).ReadToEnd(); + List userTweets = new JavaScriptSerializer().Deserialize>(responseData); + + return TweetsToNews(userTweets); + } + + public List SearchTweets(string searchTerm, int tweetsCount) + { + string TokenKey = "55684465-gvmJ81DMpzs7QxjY4BlQM2GzRWE9sUZIVTWMrX9nN"; + string TokenSecret = "nO07e9Vyj4MVJtAYTmYaW2RrEtuJSineAIesWtTTXA"; + string ConsumerKey = "HEc2tiosBZ0U62SLmsYaOA"; + string ConsumerSecret = "i3OGf4Rm3bA5MSV55X2froEjo4TCJtA49msBJ1dW9Fc"; + + // Other OAuth connection/authentication variables + string oAuthVersion = "1.0"; + string oAuthSignatureMethod = "HMAC-SHA1"; + string oAuthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString())); + TimeSpan timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + string oAuthTimestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString(); + string resourceUrl = "https://api.twitter.com/1.1/search/tweets.json"; + + // Generate OAuth signature. Note that Twitter is very particular about the format of this string. Even reordering the variables + // will cause authentication errors. + + var baseFormat = "count={7}&oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}" + + "&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}&q={6}"; + + var baseString = string.Format(baseFormat, + ConsumerKey, + oAuthNonce, + oAuthSignatureMethod, + oAuthTimestamp, + TokenKey, + oAuthVersion, + Uri.EscapeDataString(searchTerm), + Uri.EscapeDataString(tweetsCount.ToString()) + ); + + baseString = string.Concat("GET&", Uri.EscapeDataString(resourceUrl), "&", Uri.EscapeDataString(baseString)); + + // Generate an OAuth signature using the baseString + var compositeKey = string.Concat(Uri.EscapeDataString(ConsumerSecret), "&", Uri.EscapeDataString(TokenSecret)); + string oAuthSignature; + using (HMACSHA1 hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(compositeKey))) + { + oAuthSignature = Convert.ToBase64String(hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes(baseString))); + } + + // Now build the Authentication header. Again, Twitter is very particular about the format. Do not reorder variables. + var headerFormat = "OAuth oauth_nonce=\"{0}\", oauth_signature_method=\"{1}\", " + + "oauth_timestamp=\"{2}\", oauth_consumer_key=\"{3}\", " + + "oauth_token=\"{4}\", oauth_signature=\"{5}\", " + + "oauth_version=\"{6}\""; + + var authHeader = string.Format(headerFormat, + Uri.EscapeDataString(oAuthNonce), + Uri.EscapeDataString(oAuthSignatureMethod), + Uri.EscapeDataString(oAuthTimestamp), + Uri.EscapeDataString(ConsumerKey), + Uri.EscapeDataString(TokenKey), + Uri.EscapeDataString(oAuthSignature), + Uri.EscapeDataString(oAuthVersion) + ); + + // Now build the actual request + + ServicePointManager.Expect100Continue = false; + var postBody = string.Format("q={0}&count={1}", Uri.EscapeDataString(searchTerm), Uri.EscapeDataString(tweetsCount.ToString())); + resourceUrl += "?" + postBody; + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(resourceUrl); + request.Headers.Add("Authorization", authHeader); + request.Method = "GET"; + request.ContentType = "application/x-www-form-urlencoded"; + + // Retrieve the response data and deserialize the JSON data to a list of Tweet objects + WebResponse response = request.GetResponse(); + string responseData = new StreamReader(response.GetResponseStream()).ReadToEnd(); + Tweets userTweets = new JavaScriptSerializer().Deserialize(responseData); + + return TweetsToNews(userTweets.statuses); + } + + private List TweetsToNews(List tweets) + { + List news = new List(); + foreach (Tweet userTweet in tweets) + { + NewsInfo tweet = new NewsInfo(); + tweet.Author = userTweet.user.name + "|" + userTweet.user.url + "||" + userTweet.user.screen_name; + tweet.GUID = userTweet.id_str; + tweet.Image = userTweet.user.profile_image_url; + tweet.Summary = ""; + tweet.Link = "https://twitter.com/" + userTweet.user.screen_name; + tweet.Pubdate = ParseDateTime(userTweet.created_at); + tweet.News = HttpUtility.HtmlDecode(userTweet.text); + tweet.Internal = false; + tweet.Hide = false; + tweet.Title = HttpUtility.HtmlDecode(userTweet.text); + + int pos = tweet.Title.IndexOf("http://t.co"); + if (pos > -1) + { + string start = tweet.Title.Substring(0, pos - 1); + string rest = tweet.Title.Substring(pos); + int posEnde = rest.IndexOf(" "); + + if (posEnde > -1) + { + tweet.Link = rest.Substring(0, posEnde - 1); + rest = rest.Substring(posEnde); + tweet.News = start + " " + tweet.Link + " " + rest; + tweet.Title = start + " " + tweet.Link + rest; + } + else + { + tweet.Link = rest; + tweet.News = start + " " + tweet.Link + " "; + tweet.Title = start + " " + tweet.Link; + } + } + pos = tweet.Title.IndexOf("https://t.co"); + if (pos > -1) + { + string start = tweet.Title.Substring(0, pos - 1); + string rest = tweet.Title.Substring(pos); + int posEnde = rest.IndexOf(" "); + + if (posEnde > -1) + { + tweet.Link = rest.Substring(0, posEnde - 1); + rest = rest.Substring(posEnde); + tweet.News = start + " " + tweet.Link + " " + rest; + tweet.Title = start + " " + tweet.Link + rest; + } + else + { + tweet.Link = rest; + tweet.News = start + " " + tweet.Link + " "; + tweet.Title = start + " " + tweet.Link; + } + } + news.Add(tweet); + } + return (from l in news where l.News != String.Empty orderby l.Pubdate descending select l).ToList(); + } + + private DateTime ParseDateTime(string date) + { + string dayOfWeek = date.Substring(0, 3).Trim(); + string month = date.Substring(4, 3).Trim(); + string dayInMonth = date.Substring(8, 2).Trim(); + string time = date.Substring(11, 9).Trim(); + string offset = date.Substring(20, 5).Trim(); + string year = date.Substring(25, 5).Trim(); + string dateTime = string.Format("{0}-{1}-{2} {3}", dayInMonth, month, year, time); + DateTime ret = DateTime.Parse(dateTime); + return ret; + } + } +} diff --git a/Edit.ascx b/Edit.ascx index a4a0471..9320870 100644 --- a/Edit.ascx +++ b/Edit.ascx @@ -23,6 +23,7 @@
  • <%=LocalizeString("bbManageCategoryFeeds")%>
  • <%=LocalizeString("bbEditNews")%>
  • <%=LocalizeString("bbEditScheduler")%>
  • +
  • <%=LocalizeString("bbEditCredentials")%>
  • @@ -36,7 +37,10 @@
    -
    +
    +
    + +
    \ No newline at end of file diff --git a/Edit.ascx.cs b/Edit.ascx.cs index 96f0409..cb0bf34 100644 --- a/Edit.ascx.cs +++ b/Edit.ascx.cs @@ -30,7 +30,9 @@ namespace Bitboxx.DNNModules.BBNews { - [DNNtc.ModuleControlProperties("Edit", "Bitboxx.BBNews Admin", DNNtc.ControlType.Edit, "", true, true)] + [DNNtc.PackageProperties("Bitboxx.BBNews")] + [DNNtc.ModuleProperties("Bitboxx.BBNews")] + [DNNtc.ModuleControlProperties("Edit", "Bitboxx.BBNews Admin", DNNtc.ControlType.Edit, "", true, true)] public partial class Edit : PortalModuleBase { public Hashtable SubModules; @@ -80,6 +82,13 @@ protected void Page_Load(object sender, EventArgs e) ctrlScheduler.MainControl = this; plScheduler.Controls.Add(ctrlScheduler); SubModules.Add("EditScheduler", ctrlScheduler); + + EditCredentials ctrlCredentials = LoadControl("EditCredentials.ascx") as EditCredentials; + ctrlCredentials.ModuleConfiguration = this.ModuleConfiguration; + ctrlCredentials.LocalResourceFile = Localization.GetResourceFile(ctrlNews, ctrlCredentials.GetType().BaseType.Name + ".ascx"); + ctrlCredentials.MainControl = this; + plCredentials.Controls.Add(ctrlCredentials); + SubModules.Add("EditCredentials", ctrlCredentials); } catch (Exception ex) { diff --git a/Edit.ascx.designer.cs b/Edit.ascx.designer.cs index 1f02c95..7b4f19f 100644 --- a/Edit.ascx.designer.cs +++ b/Edit.ascx.designer.cs @@ -56,5 +56,14 @@ public partial class Edit { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.PlaceHolder plScheduler; + + /// + /// plCredentials control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder plCredentials; } } diff --git a/EditCredentials.ascx b/EditCredentials.ascx new file mode 100644 index 0000000..c42a3bf --- /dev/null +++ b/EditCredentials.ascx @@ -0,0 +1,30 @@ +<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="EditCredentials.ascx.cs" Inherits="Bitboxx.DNNModules.BBNews.EditCredentials" %> +<%@ Register TagPrefix="dnn" TagName="Label" Src="~/controls/LabelControl.ascx" %> +
    +
    + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
      +
    • +
    +
    diff --git a/EditCredentials.ascx.cs b/EditCredentials.ascx.cs new file mode 100644 index 0000000..d476814 --- /dev/null +++ b/EditCredentials.ascx.cs @@ -0,0 +1,79 @@ +#region copyright + +// bitboxx - http://www.bitboxx.net +// Copyright (c) 2014 +// by bitboxx solutions Torsten Weggen +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +// to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#endregion + + +// Some blog info about Scheduler +// http://kemmis.info/blog/archive/2008/05/20/programmatically-add-delete-and-update-scheduled-tasks.aspx + + +using System; +using System.Collections; +using System.Web.UI; +using DotNetNuke.Common.Utilities; +using DotNetNuke.Entities.Modules; +using DotNetNuke.Entities.Portals; +using DotNetNuke.Services.Localization; +using DotNetNuke.Services.Scheduling; +using DotNetNuke.UI.Skins; +using DotNetNuke.UI.Skins.Controls; + +namespace Bitboxx.DNNModules.BBNews +{ + public partial class EditCredentials : PortalModuleBase + { + #region Private Members + + private BBNewsController _controller; + + #endregion + + #region Properties + + public BBNewsController Controller + { + get + { + if (_controller == null) + _controller = new BBNewsController(); + return _controller; + } + } + public Control MainControl { get; set; } + #endregion + + protected void Page_Load(object sender, EventArgs e) + { + txtTwitterAccessToken.Text = PortalController.GetPortalSetting("BB_TwitterToken", PortalId, ""); + txtTwitterAccessTokenSecret.Text = PortalController.GetPortalSetting("BB_TwitterTokenSecret", PortalId, ""); + txtTwitterConsumerKey.Text = PortalController.GetPortalSetting("BB_TwitterConsumerKey", PortalId, ""); + txtTwitterConsumerSecret.Text = PortalController.GetPortalSetting("BB_TwitterConsumerSecret", PortalId, ""); + } + + protected void cmdUpdate_OnClick(object sender, EventArgs e) + { + PortalController.UpdatePortalSetting(PortalId, "BB_TwitterToken",txtTwitterAccessToken.Text.Trim()); + PortalController.UpdatePortalSetting(PortalId, "BB_TwitterTokenSecret", txtTwitterAccessTokenSecret.Text.Trim()); + PortalController.UpdatePortalSetting(PortalId, "BB_TwitterConsumerKey", txtTwitterConsumerKey.Text.Trim()); + PortalController.UpdatePortalSetting(PortalId, "BB_TwitterConsumerSecret", txtTwitterConsumerSecret.Text.Trim()); + } + } +} \ No newline at end of file diff --git a/EditCredentials.ascx.designer.cs b/EditCredentials.ascx.designer.cs new file mode 100644 index 0000000..49c7436 --- /dev/null +++ b/EditCredentials.ascx.designer.cs @@ -0,0 +1,105 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Bitboxx.DNNModules.BBNews { + + + public partial class EditCredentials { + + /// + /// lblIntro control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblIntro; + + /// + /// lblTwitterConsumerKey control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.UserControl lblTwitterConsumerKey; + + /// + /// txtTwitterConsumerKey control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtTwitterConsumerKey; + + /// + /// lblTwitterConsumerSecret control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.UserControl lblTwitterConsumerSecret; + + /// + /// txtTwitterConsumerSecret control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtTwitterConsumerSecret; + + /// + /// lblTwitterAccessToken control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.UserControl lblTwitterAccessToken; + + /// + /// txtTwitterAccessToken control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtTwitterAccessToken; + + /// + /// lblTwitterAccessTokenSecret control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.UserControl lblTwitterAccessTokenSecret; + + /// + /// txtTwitterAccessTokenSecret control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtTwitterAccessTokenSecret; + + /// + /// cmdUpdate control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.LinkButton cmdUpdate; + } +} diff --git a/EditFeeds.ascx.cs b/EditFeeds.ascx.cs index 9c42588..4116ce4 100644 --- a/EditFeeds.ascx.cs +++ b/EditFeeds.ascx.cs @@ -173,31 +173,26 @@ protected void cmdCancel_Click(object sender, EventArgs e) protected void cboFeedType_SelectedIndexChanged(object sender, EventArgs e) { - // switch (cboFeedType.SelectedValue) { case "0": // None pnlFeedUrl.Visible = false; pnlCredentials.Visible = false; - //valFeedUrlValid.Enabled = false; break; case "1": // Twitter Search pnlFeedUrl.Visible = true; pnlCredentials.Visible = false; lblFeedUrl.Text = Localization.GetString("lblFeedUrlTwitterSearch.Text", this.LocalResourceFile); - //valFeedUrlValid.Enabled = false; break; case "2": // RSS pnlFeedUrl.Visible = true; pnlCredentials.Visible = false; lblFeedUrl.Text = Localization.GetString("lblFeedUrlRss.Text", this.LocalResourceFile); - //valFeedUrlValid.Enabled = true; break; case "3": // Twitter Usertimeline pnlFeedUrl.Visible = true; - pnlCredentials.Visible = true; + pnlCredentials.Visible = false; lblFeedUrl.Text = Localization.GetString("lblFeedUrlTwitterTimeline.Text", this.LocalResourceFile); - //valFeedUrlValid.Enabled = false; break; } } diff --git a/Installation/01.00.00.SqlDataProvider b/Installation/01.00.00.SqlDataProvider index 104d151..34aa723 100644 --- a/Installation/01.00.00.SqlDataProvider +++ b/Installation/01.00.00.SqlDataProvider @@ -21,7 +21,7 @@ IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwne ) ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_Category] - ADD CONSTRAINT {objectQualifier}PK_BBNews_Category PRIMARY KEY CLUSTERED (CategoryID ASC) + ADD CONSTRAINT PK_{objectQualifier}BBNews_Category PRIMARY KEY CLUSTERED (CategoryID ASC) END GO @@ -38,7 +38,7 @@ IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwne ) ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_Feed] - ADD CONSTRAINT {objectQualifier}PK_BBNews_Feed PRIMARY KEY CLUSTERED (FeedID ASC) + ADD CONSTRAINT PK_{objectQualifier}BBNews_Feed PRIMARY KEY CLUSTERED (FeedID ASC) END GO @@ -57,20 +57,20 @@ IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwne ) ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_News] - ADD CONSTRAINT {objectQualifier}PK_BBNews_News PRIMARY KEY CLUSTERED (NewsID ASC) + ADD CONSTRAINT PK_{objectQualifier}BBNews_News PRIMARY KEY CLUSTERED (NewsID ASC) END GO IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys where object_id = object_id(N'{objectQualifier}FK_Category_Feed') and parent_object_id = object_id(N'{databaseOwner}[{objectQualifier}BBNews_Feed]')) ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_Feed] WITH NOCHECK - ADD CONSTRAINT {objectQualifier}FK_Category_Feed FOREIGN KEY ( CategoryID ) + ADD CONSTRAINT FK_{objectQualifier}Category_Feed FOREIGN KEY ( CategoryID ) REFERENCES {databaseOwner}[{objectQualifier}BBNews_Category] ( CategoryID ) ON DELETE SET DEFAULT GO /*IF NOT EXISTS (SELECT 1 FROM sys.objects where name='{objectQualifier}FK_Category_News' and type='F')*/ IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys where object_id = object_id(N'{objectQualifier}FK_Category_News') and parent_object_id = object_id(N'{databaseOwner}[{objectQualifier}BBNews_News]')) ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_News] WITH NOCHECK - ADD CONSTRAINT {objectQualifier}FK_Category_News FOREIGN KEY ( CategoryID ) + ADD CONSTRAINT FK_{objectQualifier}Category_News FOREIGN KEY ( CategoryID ) REFERENCES {databaseOwner}[{objectQualifier}BBNews_Category] ( CategoryID ) ON DELETE CASCADE GO diff --git a/Installation/01.01.00.SqlDataProvider b/Installation/01.01.00.SqlDataProvider index 5fd0395..49203ee 100644 --- a/Installation/01.01.00.SqlDataProvider +++ b/Installation/01.01.00.SqlDataProvider @@ -15,11 +15,20 @@ ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_Feed] DROP CONSTRAINT [{objectQualifier}FK_Category_Feed] go +IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[FK_{objectQualifier}Category_Feed]') AND parent_object_id = OBJECT_ID(N'{databaseOwner}[{objectQualifier}BBNews_Feed]')) +ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_Feed] +DROP CONSTRAINT [FK_{objectQualifier}Category_Feed] +go + IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[{objectQualifier}FK_Category_News]') AND parent_object_id = OBJECT_ID(N'{databaseOwner}[{objectQualifier}BBNews_News]')) ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_News] DROP CONSTRAINT [{objectQualifier}FK_Category_News] go +IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[FK_{objectQualifier}Category_News]') AND parent_object_id = OBJECT_ID(N'{databaseOwner}[{objectQualifier}BBNews_News]')) +ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_News] +DROP CONSTRAINT [FK_{objectQualifier}Category_News] +go IF EXISTS (SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N'{databaseOwner}[{objectQualifier}TEMP_BBNews_Category]') AND type = 'U') DROP TABLE {databaseOwner}[{objectQualifier}TEMP_BBNews_Category] @@ -41,7 +50,7 @@ IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwne CategoryID INT NOT NULL DEFAULT (0), FeedID INT NOT NULL DEFAULT (0) ) - CREATE CLUSTERED INDEX {objectQualifier}IDX_BBNews_CategoryFeeds_CategoryID ON {databaseOwner}[{objectQualifier}BBNews_CategoryFeeds] (CategoryID ASC, FeedID ASC) + CREATE CLUSTERED INDEX IDX_{objectQualifier}BBNews_CategoryFeeds_CategoryID ON {databaseOwner}[{objectQualifier}BBNews_CategoryFeeds] (CategoryID ASC, FeedID ASC) END GO @@ -54,7 +63,7 @@ CREATE TABLE {databaseOwner}[{objectQualifier}TEMP_BBNews_Category] CategoryDescription NVARCHAR(255) NOT NULL DEFAULT '' ) ALTER TABLE {databaseOwner}[{objectQualifier}TEMP_BBNews_Category] - ADD CONSTRAINT {objectQualifier}PK_TEMP_BBNews_Category PRIMARY KEY CLUSTERED (CategoryID ASC) + ADD CONSTRAINT PK_{objectQualifier}TEMP_BBNews_Category PRIMARY KEY CLUSTERED (CategoryID ASC) go SET IDENTITY_INSERT {databaseOwner}[{objectQualifier}TEMP_BBNews_Category] ON @@ -72,7 +81,7 @@ BEGIN SET IDENTITY_INSERT {databaseOwner}[{objectQualifier}TEMP_BBNews_Category] OFF EXECUTE sp_rename '{databaseOwner}[{objectQualifier}TEMP_BBNews_Category]', '{objectQualifier}BBNews_Category', 'OBJECT' - EXECUTE sp_rename '{objectQualifier}PK_TEMP_BBNews_Category', '{objectQualifier}PK_BBNews_Category', 'OBJECT' + EXECUTE sp_rename 'PK_{objectQualifier}TEMP_BBNews_Category', 'PK_{objectQualifier}BBNews_Category', 'OBJECT' --ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_Category] --ADD CONSTRAINT {objectQualifier}PK_BBNews_Category PRIMARY KEY NONCLUSTERED (CategoryID ASC) @@ -105,7 +114,7 @@ CREATE TABLE {databaseOwner}[{objectQualifier}TEMP_BBNews_Feed] Password NVARCHAR(40) NOT NULL DEFAULT '' ) ALTER TABLE {databaseOwner}[{objectQualifier}TEMP_BBNews_Feed] - ADD CONSTRAINT {objectQualifier}PK_TEMP_BBNews_Feed PRIMARY KEY CLUSTERED (FeedID ASC) + ADD CONSTRAINT PK_{objectQualifier}TEMP_BBNews_Feed PRIMARY KEY CLUSTERED (FeedID ASC) go @@ -133,7 +142,7 @@ BEGIN SET IDENTITY_INSERT {databaseOwner}[{objectQualifier}TEMP_BBNews_Feed] OFF EXECUTE sp_rename '{databaseOwner}[{objectQualifier}TEMP_BBNews_Feed]', '{objectQualifier}BBNews_Feed', 'OBJECT' - EXECUTE sp_rename '{objectQualifier}PK_TEMP_BBNews_Feed', '{objectQualifier}PK_BBNews_Feed', 'OBJECT' + EXECUTE sp_rename 'PK_{objectQualifier}TEMP_BBNews_Feed', 'PK_{objectQualifier}BBNews_Feed', 'OBJECT' --ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_Feed] --ADD CONSTRAINT {objectQualifier}PK_BBNews_Feed PRIMARY KEY NONCLUSTERED (FeedID ASC) @@ -163,7 +172,7 @@ CREATE TABLE {databaseOwner}[{objectQualifier}TEMP_BBNews_News] Internal BIT NOT NULL ) ALTER TABLE {databaseOwner}[{objectQualifier}TEMP_BBNews_News] - ADD CONSTRAINT {objectQualifier}PK_TEMP_BBNews_News PRIMARY KEY CLUSTERED (NewsID ASC) + ADD CONSTRAINT PK_{objectQualifier}TEMP_BBNews_News PRIMARY KEY CLUSTERED (NewsID ASC) go @@ -184,7 +193,7 @@ BEGIN SET IDENTITY_INSERT {databaseOwner}[{objectQualifier}TEMP_BBNews_News] OFF EXECUTE sp_rename '{databaseOwner}[{objectQualifier}TEMP_BBNews_News]', '{objectQualifier}BBNews_News', 'OBJECT' - EXECUTE sp_rename '{objectQualifier}PK_TEMP_BBNews_News', '{objectQualifier}PK_BBNews_News', 'OBJECT' + EXECUTE sp_rename 'PK_{objectQualifier}TEMP_BBNews_News', 'PK_{objectQualifier}BBNews_News', 'OBJECT' --ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_News] --ADD CONSTRAINT {objectQualifier}PK_BBNews_News PRIMARY KEY NONCLUSTERED (NewsID ASC) @@ -198,27 +207,27 @@ BEGIN END go -IF NOT EXISTS (SELECT 1 FROM sys.objects where name='{objectQualifier}FK_CategoryFeedsOfCategory' and type='F') +IF NOT EXISTS (SELECT 1 FROM sys.objects where name='FK_{objectQualifier}CategoryFeedsOfCategory' and type='F') ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_CategoryFeeds] WITH NOCHECK - ADD CONSTRAINT {objectQualifier}FK_CategoryFeedsOfCategory FOREIGN KEY + ADD CONSTRAINT FK_{objectQualifier}CategoryFeedsOfCategory FOREIGN KEY ( CategoryID ) REFERENCES {databaseOwner}[{objectQualifier}BBNews_Category] ( CategoryID ) ON DELETE CASCADE go -IF NOT EXISTS (SELECT 1 FROM sys.objects where name='{objectQualifier}FK_NewsOfFeed' and type='F') +IF NOT EXISTS (SELECT 1 FROM sys.objects where name='FK_{objectQualifier}NewsOfFeed' and type='F') ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_News] WITH NOCHECK - ADD CONSTRAINT {objectQualifier}FK_NewsOfFeed FOREIGN KEY + ADD CONSTRAINT FK_{objectQualifier}NewsOfFeed FOREIGN KEY ( FeedID ) REFERENCES {databaseOwner}[{objectQualifier}BBNews_Feed] ( FeedID ) ON DELETE CASCADE go -IF NOT EXISTS (SELECT 1 FROM sys.objects where name='{objectQualifier}FK_FeedsOfCategoryFeeds' and type='F') +IF NOT EXISTS (SELECT 1 FROM sys.objects where name='FK_{objectQualifier}FeedsOfCategoryFeeds' and type='F') ALTER TABLE {databaseOwner}[{objectQualifier}BBNews_CategoryFeeds] WITH NOCHECK - ADD CONSTRAINT {objectQualifier}FK_FeedsOfCategoryFeeds FOREIGN KEY + ADD CONSTRAINT FK_{objectQualifier}FeedsOfCategoryFeeds FOREIGN KEY ( FeedID ) REFERENCES {databaseOwner}[{objectQualifier}BBNews_Feed] ( FeedID ) diff --git a/Installation/Attributes.cs b/Installation/Attributes.cs index 8813d7e..813de2e 100644 --- a/Installation/Attributes.cs +++ b/Installation/Attributes.cs @@ -4,6 +4,66 @@ namespace DNNtc { #region Attributes + + /// + /// This class is used to set information about the package + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class PackageProperties : Attribute + { + /// + /// Creates a attribute with the right properties to create a package. Use on View element + /// + /// The package name + /// Ordinal number for sorting packages in the manifest + /// The package friendly name + /// The package description + /// The package iconfile name + /// Name of Owner + /// Organization of owner + /// Url of owner + /// Email of owner + /// Flag for Azure Compatibility + public PackageProperties(string name, int viewOrder, string friendlyName, string description, string iconFile, string ownerName, string ownerOrganization, string ownerUrl, string ownerEmail, bool azureCompatible) + { + //Intentially left empty + } + /// + /// Creates a attribute with the right properties to create a package. Use on other elements + /// + /// The package name + public PackageProperties(string name) + { + //Intentially left empty + } + } + + /// + /// This class is used to set information about the module + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class ModuleProperties : Attribute + { + /// + /// Creates a attribute with the right properties to create a module. Use on View element + /// + /// The module name. + /// The module friendlyname. + /// the module default cachetime. + public ModuleProperties(string name, string friendlyname, int defaultCacheTime) + { + //Intentially left empty + } + /// + /// Creates a attribute with the right properties to create a module. Use on other elements + /// + /// The module name. + public ModuleProperties(string name) + { + //Intentially left empty + } + } + /// /// This class is used to indicate which UserControls should be in the install package /// @@ -18,7 +78,7 @@ public class ModuleControlProperties : Attribute /// The help URL. /// if set to true [supports partial rendering]. /// if set to true [supports pop ups]. - public ModuleControlProperties(string key, string title, ControlType userControlType, string helpUrl, bool supportsPartialRendering=false, bool supportsPopUps=false) + public ModuleControlProperties(string key, string title, ControlType userControlType, string helpUrl, bool supportsPartialRendering = false, bool supportsPopUps = false) { //Intentially left empty } diff --git a/Installation/Project.targets b/Installation/Project.targets index 62cb58c..f18604d 100644 --- a/Installation/Project.targets +++ b/Installation/Project.targets @@ -1,4 +1,4 @@ - + @@ -8,18 +8,16 @@ @@ -33,29 +31,26 @@ - - - bitboxx solutions - http://www.bitboxx.net - info@bitboxx.net - + $(MSBuildProjectDirectory)\bin - Bitboxx.BBNews - BBNews - Bitboxx BBNews - + Bitboxx.BBNews + BBNews bbnews.png - A flexible RSS/News reader, provider and aggregator true - -1 - + + + + + @@ -63,7 +58,7 @@ $(MSBuildProjectDirectory)\Installation - $(InstallDir)\$(ModuleName).dnn6 + $(InstallDir)\$(ZipFileNamePrefix).dnn @@ -93,32 +88,42 @@ - + File="%(AssemblyInfoFiles.FullPath)" > + - - - + + + - + - + - + + + + + + + + + + + - + @@ -133,108 +138,30 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + @@ -262,7 +189,7 @@ - + @@ -347,7 +274,8 @@ $(MSBuildProjectDirectory)\*ReSharper*\**; $(MSBuildProjectDirectory)\Installation\*.png; $(MSBuildProjectDirectory)\tmpCompressScript\**; - $(MSBuildProjectDirectory)\tmpCompressCSS\**" + $(MSBuildProjectDirectory)\tmpCompressCSS\**; + @(ProjectsToExclude)" Include="$(MSBuildProjectDirectory)\**\*.ascx; $(MSBuildProjectDirectory)\**\*.aspx; $(MSBuildProjectDirectory)\**\*.css; @@ -358,7 +286,12 @@ $(MSBuildProjectDirectory)\**\*.gif; $(MSBuildProjectDirectory)\**\*.jpg; $(MSBuildProjectDirectory)\**\*.png; - $(MSBuildProjectDirectory)\**\*.js;"> + $(MSBuildProjectDirectory)\**\*.js; + $(MSBuildProjectDirectory)\**\*.template; + $(MSBuildProjectDirectory)\**\*.htm; + $(MSBuildProjectDirectory)\**\*.htp; + + "> @@ -404,7 +337,7 @@ + ZipFileName="$(InstallDir)\$(ZipFileNamePrefix)_$(FileMajor).$(FileMinor).$(FileBuild)_Install.zip" /> @@ -445,8 +378,6 @@ - - - @@ -493,7 +424,7 @@ + ZipFileName="$(InstallDir)\Resources.zip" /> @@ -510,16 +441,14 @@ + ZipFileName="$(InstallDir)\$(ZipFileNamePrefix)_$(FileMajor).$(FileMinor).$(FileBuild)_Source.zip" /> - - + diff --git a/Installation/ReleaseNotes/Release.01.01.04.txt b/Installation/ReleaseNotes/Release.01.01.04.txt index 33c771a..2497c05 100644 --- a/Installation/ReleaseNotes/Release.01.01.04.txt +++ b/Installation/ReleaseNotes/Release.01.01.04.txt @@ -4,10 +4,15 @@
    • Fixed some errors in the UI (DNN Form Pattern)
    • Fixed a bug not showing the correct favicon
    • -
    • Fixed an error which occured while retrieving some RSS feeds (e.g. blogs-feed on dotnetnuke.com).
    • -
    • Fixed an error with ambiguous NewsId when opening "Manage BBNews".
    • +
    • Fixed an error which occured while retrieving some RSS feeds (e.g. blogs-feed on dotnetnuke.com).
    • +
    • Fixed an error with ambiguous NewsId when opening "Manage BBNews".
    • +
    • RSS icon has wrong path if DNN is installed in subdirectory
    • +
    • Added some error tolerance to parsing of RSS feeds
    • +
    • Added default values for settings "NewsInRow" and "RowsPerPage" to avoid error message if these are not set

    ENHANCEMENTS

      -
    • Added a class ("bbNewsCell") to the news area for better styling
    • +
    • Upgraded twitter integration to API 1.1 with oAuth (needs creation of app on https://dev.twitter.com/apps).
    • +
    • Added a class ("bbNewsCell") to the news area for better styling
    • +
    • Upgraded package mechanism to dnntcmsbuild V 2.0
    \ No newline at end of file diff --git a/Installation/bbnews.png b/Installation/bbnews.png new file mode 100644 index 0000000..70dc566 Binary files /dev/null and b/Installation/bbnews.png differ diff --git a/Settings.ascx.cs b/Settings.ascx.cs index 96a2b5c..5829581 100644 --- a/Settings.ascx.cs +++ b/Settings.ascx.cs @@ -40,7 +40,9 @@ namespace Bitboxx.DNNModules.BBNews /// /// /// ----------------------------------------------------------------------------- - [DNNtc.ModuleControlProperties("Settings", "Configure settings", DNNtc.ControlType.Edit, "", true, true)] + [DNNtc.PackageProperties("Bitboxx.BBNews")] + [DNNtc.ModuleProperties("Bitboxx.BBNews")] + [DNNtc.ModuleControlProperties("Settings", "Configure settings", DNNtc.ControlType.Edit, "", true, true)] partial class Settings : ModuleSettingsBase { private BBNewsController _controller; diff --git a/Templates/News/Twitter.htm b/Templates/News/Twitter.htm index 39af816..80b68be 100644 --- a/Templates/News/Twitter.htm +++ b/Templates/News/Twitter.htm @@ -1,5 +1,5 @@
    -
    [BBNEWS:AUTHORNAME][BBNEWS:AUTHORNAME]
    [BBNEWS:PUBDATE|dd.MM.yy HH:mm]

    [BBNEWS:SUMMARY]

    +[BBNEWS:AUTHORNAME]
    [BBNEWS:PUBDATE|dd.MM.yy HH:mm]

    [BBNEWS:NEWS]


    \ No newline at end of file diff --git a/Templates/News/Twitter.jpg b/Templates/News/Twitter.jpg index 766d08b..d4ac500 100644 Binary files a/Templates/News/Twitter.jpg and b/Templates/News/Twitter.jpg differ diff --git a/View.ascx.cs b/View.ascx.cs index 166b8e0..826a771 100644 --- a/View.ascx.cs +++ b/View.ascx.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.Data.SqlTypes; using System.IO; +using System.Linq; using System.ServiceModel.Syndication; using System.Text; using System.Web.UI; @@ -43,7 +44,9 @@ namespace Bitboxx.DNNModules.BBNews { - [DNNtc.ModuleDependencies(DNNtc.ModuleDependency.CoreVersion, "06.01.00")] + [DNNtc.PackageProperties("Bitboxx.BBNews", 1, "Bitboxx BBNews", "A flexible RSS/News reader, provider and aggregator", "bbnews.png", "Torsten Weggen", "bitboxx solutions", "http://www.bitboxx.net", "info@bitboxx.net", true)] + [DNNtc.ModuleProperties("Bitboxx.BBNews", "Bitboxx BBNews", -1)] + [DNNtc.ModuleDependencies(DNNtc.ModuleDependency.CoreVersion, "06.01.00")] [DNNtc.ModuleControlProperties("", "Bitboxx.BBNews", DNNtc.ControlType.View,"", true, false)] partial class View : PortalModuleBase,IActionable { @@ -84,7 +87,7 @@ public int TopN { get { - int _topN = 1; + int _topN = 0; if (Settings["TopN"] != null) Int32.TryParse((string)Settings["TopN"], out _topN); return _topN; @@ -191,25 +194,26 @@ protected void Page_Init(object sender, System.EventArgs e) if (user.IsInRole("Administrator") && IsEditable) MultiView1.Visible = true; - if (Settings["NewsInRow"] != null) - { - if (ViewIndex == 0) - { - newsInRow = Int32.Parse((string)Settings["NewsInRow"]); - rowsPerPage = Int32.Parse((string)Settings["RowsPerPage"]); - lstNews.GroupItemCount = newsInRow; - Pager.PageSize = newsInRow * rowsPerPage; - } - MultiView1.ActiveViewIndex = ViewIndex; - IsConfigured = true; - } - - if (Settings["NewsInRow"] == null) - { - string message = Localization.GetString("Configure.Message", this.LocalResourceFile); - DotNetNuke.UI.Skins.Skin.AddModuleMessage(this, message, ModuleMessage.ModuleMessageType.YellowWarning); - } - if (Settings["TemplateName"] == null && Settings["Template"] != null ) + if (Settings["NewsInRow"] != null) + { + newsInRow = 1; + rowsPerPage = 5; + if (ViewIndex == 0) + { + Int32.TryParse((string) Settings["NewsInRow"], out newsInRow); + Int32.TryParse((string) Settings["RowsPerPage"], out rowsPerPage); + lstNews.GroupItemCount = newsInRow; + Pager.PageSize = newsInRow*rowsPerPage; + } + MultiView1.ActiveViewIndex = ViewIndex; + IsConfigured = true; + } + else + { + string message = Localization.GetString("Configure.Message", this.LocalResourceFile); + DotNetNuke.UI.Skins.Skin.AddModuleMessage(this, message, ModuleMessage.ModuleMessageType.YellowWarning); + } + if (Settings["TemplateName"] == null && Settings["Template"] != null ) { var ctrl = LoadControl("controls\\TemplateControl.ascx") as Controls.TemplateControl; ctrl.Key = "News"; @@ -367,7 +371,7 @@ protected void Page_Prerender(object sender, EventArgs e) { int position = Convert.ToInt32(Settings["ShowRss"]); Control rssIconCtrl = ParseControl(""); + "\">"); switch (position) { case 1: @@ -455,7 +459,7 @@ private SyndicationFeed CreateFeed() //feed.Description = new TextSyndicationContent(category.CategoryDescription); List items = new List(); - foreach (NewsInfo news in AllNews) + foreach (NewsInfo news in AllNews.Take(10).OrderByDescending(n => n.Pubdate)) { if (news.Internal && Settings["NewsPage"] != null) {