Twitter

daniel dixon asked on March 4, 2015 18:09

Please for the love of god can some help me with a twitter feed for kentico 8.2, I have tried the OAuth webpart but it doesnt work. I really dont want a widget as they look rubbish

Correct Answer

Brenden Kehren answered on March 7, 2015 17:57

Think the issue is with the ValidationHelper, it's not part of System.Net, its part of CMS.Helpers. I used that webpart and customized it for v8, see the code here:

using CMS.Helpers;
using CMS.PortalControls;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;

#region Custom Twitter objects

public class Tweet
{
    public string created_at { get; set; }
    public string id { get; set; }
    public string id_str { get; set; }
    public string text { get; set; }
}

#endregion

public partial class CMSWebParts_KehrenDev_OAuthTwitterFeed : CMSAbstractWebPart
{
    #region Constants

    const string OAuthVersion = "1.0";
    const string OAuthSignatureMethod = "HMAC-SHA1";

    #endregion

    #region Public Properties

    /// <summary>
    /// OAuth Token
    /// </summary>
    public string OAuthAccessToken
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("OAuthAccessToken"), "");
        }
        set
        {
            this.SetValue("OAuthAccessToken", value);
        }
    }

    /// <summary>
    /// OAuth Token Secret
    /// </summary>
    public string OAuthAccessTokenSecret
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("OAuthAccessTokenSecret"), "");
        }
        set
        {
            this.SetValue("OAuthAccessTokenSecret", value);
        }
    }

    /// <summary>
    /// OAuth Consumer Key
    /// </summary>
    public string OAuthConsumerKey
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("OAuthConsumerKey"), "");
        }
        set
        {
            this.SetValue("OAuthConsumerKey", value);
        }
    }

    /// <summary>
    /// OAuth Consumer Secret
    /// </summary>
    public string OAuthConsumerSecret
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("OAuthConsumerSecret"), "");
        }
        set
        {
            this.SetValue("OAuthConsumerSecret", value);
        }
    }

    /// <summary>
    /// Transformation Name
    /// </summary>
    public string TransformationName
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("TransformationName"), "");
        }
        set
        {
            this.SetValue("TransformationName", value);
        }
    }

    /// <summary>
    /// Twitter screen name
    /// </summary>
    public string TwitterScreenName
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("TwitterScreenName"), "");
        }
        set
        {
            this.SetValue("TwitterScreenName", value);
        }
    }

    /// <summary>
    /// Number of tweets to display
    /// </summary>
    public string TweetsCount
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("TweetsCount"), "");
        }
        set
        {
            this.SetValue("TweetsCount", value);
        }
    }

    /// <summary>
    /// Title text of Twitter feed
    /// </summary>
    public string TitleText
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("TitleText"), "");
        }
        set
        {
            this.SetValue("TitleText", value);
        }
    }

    /// <summary>
    /// Date format
    /// </summary>
    public string DateFormat
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("DateFormat"), "");
        }
        set
        {
            this.SetValue("DateFormat", value);
        }
    }

    /// <summary>
    /// Display the "Follow me..." link?
    /// </summary>
    public string DisplayFollowMeLink
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("DisplayFollowMeLink"), "");
        }
        set
        {
            this.SetValue("DisplayFollowMeLink", value);
        }
    }

    /// <summary>
    /// Text for the "Follow me..." link
    /// </summary>
    public string FollowMeLinkText
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("FollowMeLinkText"), "");
        }
        set
        {
            this.SetValue("FollowMeLinkText", value);
        }
    }

    /// <summary>
    /// Custom error message when Twitter becomes inaccessible
    /// </summary>
    public string CustomErrorMessage
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("CustomErrorMessage"), "");
        }
        set
        {
            this.SetValue("CustomErrorMessage", value);
        }
    }

    #endregion


    /// <summary>
    /// Content loaded event handler
    /// </summary>
    public override void OnContentLoaded()
    {
        base.OnContentLoaded();
        SetupControl();
    }

    /// <summary>
    /// Initializes the control properties
    /// </summary>
    protected void SetupControl()
    {
        if (this.StopProcessing)
        {
            // Do not process
        }
        else
        {
            // Retrieve all user tweets and populate the repeater
            List<Tweet> UserTweets = GetUserTweets();
            rptTweets.TransformationName = TransformationName;
            rptTweets.DataSource = UserTweets;
            rptTweets.DataBind();

            string str = CurrentDocument.NodeAliasPath;

            // Check if user has specified a title, if so display it
            if(!String.IsNullOrEmpty(TitleText))
            {
                pnlTwitterHeader.Visible = true;
                ltlTitleText.Text = TitleText;
            }

            // Check if user wants to show the "Follow me..." link; if so, set it up
            if (Convert.ToBoolean(DisplayFollowMeLink) == true)
            {
                lnkFollowMe.Visible = true;
                if (!String.IsNullOrEmpty(FollowMeLinkText))
                {
                    lnkFollowMe.Text = FollowMeLinkText;
                }
            }            
        }
    }

    /// <summary>
    /// Reloads the control data
    /// </summary>
    public override void ReloadData()
    {
        base.ReloadData();
        SetupControl();
    }

    /// <summary>
    /// Retrieves a list of specified user's tweets
    /// </summary>
    public List<Tweet> GetUserTweets()
    {

        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 an encrypted oAuth signature which Twitter will use to validate the request. 
        // The formatting of this string is very specific, even the order of the variables matters. Modify with caution.

        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,
            OAuthConsumerKey,
            OAuthNonce,
            OAuthSignatureMethod,
            OAuthTimestamp,
            OAuthAccessToken,
            OAuthVersion,
            Uri.EscapeDataString(TwitterScreenName),
            Uri.EscapeDataString(TweetsCount)
        );

        baseString = string.Concat("GET&", Uri.EscapeDataString(ResourceUrl), "&", Uri.EscapeDataString(baseString));

        // Use the base string to generate the OAuth signature
        var compositeKey = string.Concat(Uri.EscapeDataString(OAuthConsumerSecret), "&", Uri.EscapeDataString(OAuthAccessTokenSecret));
        string OAuthSignature;
        using (HMACSHA1 hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(compositeKey)))
        {
            OAuthSignature = Convert.ToBase64String(hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes(baseString)));
        }

        // Now build the OAuth Authentication header. Again, the header format is very specific, even minor changes like re-ordering varilables
        // will result in 401 Unauthorized errors.

        var HeaderFormat = "OAuth " +
            "oauth_consumer_key=\"{0}\", " +
            "oauth_nonce=\"{1}\", " +
            "oauth_signature=\"{2}\", " +
            "oauth_signature_method=\"{3}\", " +
            "oauth_timestamp=\"{4}\", " + 
            "oauth_token=\"{5}\", " + 
            "oauth_version=\"{6}\"";

        var authHeader = string.Format(HeaderFormat,
            Uri.EscapeDataString(OAuthConsumerKey),
            Uri.EscapeDataString(OAuthNonce),
            Uri.EscapeDataString(OAuthSignature),
            Uri.EscapeDataString(OAuthSignatureMethod),
            Uri.EscapeDataString(OAuthTimestamp),
            Uri.EscapeDataString(OAuthAccessToken),
            Uri.EscapeDataString(OAuthVersion)
        );

        // Finally, create the Twitter API request
        var postBody = string.Format("screen_name={0}&count={1}", Uri.EscapeDataString(TwitterScreenName), Uri.EscapeDataString(TweetsCount));
        ResourceUrl += "?" + postBody;
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(ResourceUrl);
        request.Headers.Add("Authorization", authHeader);
        request.Method = "GET";
        request.ContentType = "application/x-www-form-urlencoded";

        // Note, we must also disable the Expect: 100-Continue header using the ServicePointManager. Without this code, .NET sends the header by default, which is not supported by Twitter.
        ServicePointManager.Expect100Continue = false;

        // Send the request, and deserialize the response. Use a try/catch to gracefully handle Twitter errors.
        List<Tweet> UserTweets = new List<Tweet>();

        try
        {
            WebResponse response = request.GetResponse();
            string responseData = new StreamReader(response.GetResponseStream()).ReadToEnd();
            UserTweets = new JavaScriptSerializer().Deserialize<List<Tweet>>(responseData);

            // Iterate through the list of tweets and parse their text
            for (int tweet = 0; tweet < UserTweets.Count; tweet++)
            {
                UserTweets[tweet].text = ParseTweet(UserTweets[tweet].text);
                DateTime TweetDate = ParseDateTime(UserTweets[tweet].created_at.ToString());
                TweetDate = TweetDate.ToLocalTime();
                UserTweets[tweet].created_at = TweetDate.ToString(DateFormat);
            }
        }
        catch (Exception ex)
        {
            // If the Twitter feed fails, display an error message.
            if (!String.IsNullOrEmpty(CustomErrorMessage))
            {
                ltlErrorMessage.Text = CustomErrorMessage;
            }
            else
            {
                if (ex.Message.Contains("(401) Unauthorized"))
                {
                    ltlErrorMessage.Text = "Twitter returned a <strong>(401) Unauthorized</strong> error. Please verify your OAuth credentials and try again.";
                }
                else
                {
                    ltlErrorMessage.Text = "Twitter feed failed with the following error: <strong>" + ex.Message + "</strong>";
                }
            }
            pnlErrorMessage.Visible = true;
        }

        // All done!
        return UserTweets;
    }

    /// <summary>
    /// Parse a tweet by hyperlinking mentions, hashtags
    /// Code stolen from: http://csharpmentor.blogspot.ca/2011/12/parsing-twit-with-regex-in-c.html
    /// </summary>
    /// <param name="_originalText"></param>
    /// <returns></returns>
    private string ParseTweet(string rawTweet)
    {
        Regex link = new Regex(@"http(s)?://([\w+?\.\w+])+([a-zA-Z0-9\~\!\@\#\$\%\^\&amp;\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?");
        Regex screenName = new Regex(@"@\w+");
        Regex hashTag = new Regex(@"#\w+");

        string formattedTweet = link.Replace(rawTweet, delegate(Match m)
        {
            string val = m.Value;
            return "<a target='blank' href='" + val + "'>" + val + "</a>";
        });

        formattedTweet = screenName.Replace(formattedTweet, delegate(Match m)
        {
            string val = m.Value.Trim('@');
            return string.Format("@<a target='blank' href='http://twitter.com/{0}'>{1}</a>", val, val);
        });

        formattedTweet = hashTag.Replace(formattedTweet, delegate(Match m)
        {
            string val = m.Value;
            return string.Format("<a target='blank' href='http://twitter.com/#search?q=%23{0}'>{1}</a>", val, val);
        });

        return formattedTweet;
    }

    /// <summary>
    /// This function just parses the date which in the XML to a DateTime. It facilitate the work with DateTime later.
    /// Code stolen from the r42 Twitter Feed webpart: http://devnet.kentico.com/Marketplace/Community/Twitter-Feed.aspx
    /// </summary>
    /// <param name="date"></param>
    /// <returns></returns>
    public 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;
    }
}
2 votesVote for this answer Unmark Correct answer

Recent Answers


Brenden Kehren answered on March 4, 2015 19:11

What part doesn't work?

Are you getting errors? Nothing?

Have you setup the application within Twitter?

Is your Twitter application setup properly/completely?

Have you entered the proper token, secret, keys in the fields in the webpart from Twitter?

Simply submitting a question and stating "it doesn't work" is very frustrating for everyone including people who answer. At least provide what you've done or what your setup is and what's not working. Since this involves many other pieces, there could be a lot of speculation as to what is or isn't working.

0 votesVote for this answer Mark as a Correct answer

daniel dixon answered on March 4, 2015 19:36

My apologies it's just been a really frustrating afternoon with twitters api.

My app is setup ok and all the tokens are fine

The error I get is [error loading the webpart 'oauthtwittrrfeed' of type 'oauthtwitterfeed'] Could this been down to it being compatible with 7.0 and me running 8.2

0 votesVote for this answer Mark as a Correct answer

Brenden Kehren answered on March 4, 2015 21:52

There was a change back a while ago that twitter changed their api and I believe the Kentico OAuth webpart needed modifications. You might look into that code and see if the what the changes are for the rest service. I think it was a simple URL change if I remember right. Here's a DevNet article on it. Might be a simple URL change in the code. I have the OAuth webpart running on my site and I did verify it is customized, just don't remember where.

0 votesVote for this answer Mark as a Correct answer

daniel dixon answered on March 6, 2015 11:25

heres the error I am getting through event log

Message: c:\inetpub\wwwroot\UC_Global\CMS\CMSWebParts\ThePixelShop\OAuthTwitterFeed.ascx.cs(50): error CS0122: 'System.Net.ValidationHelper' is inaccessible due to its protection level

Exception type: System.Web.HttpCompileException Stack Trace: at System.Web.Compilation.BuildManager.PostProcessFoundBuildResult(BuildResult result, Boolean keyFromVPP, VirtualPath virtualPath) at System.Web.Compilation.BuildManager.GetBuildResultFromCacheInternal(String cacheKey, Boolean keyFromVPP, VirtualPath virtualPath, Int64 hashCode, Boolean ensureIsUpToDate) at System.Web.Compilation.BuildManager.GetVPathBuildResultFromCacheInternal(VirtualPath virtualPath, Boolean ensureIsUpToDate) at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate) at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate) at System.Web.Compilation.BuildManager.GetVPathBuildResult(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean ensureIsUpToDate) at System.Web.UI.TemplateControl.LoadControl(VirtualPath virtualPath) at CMS.PortalControls.CMSWebPartZone.LoadWebPart(Control container, CMSWebPartZone zone, WebPartInstance part, Boolean reloadData, Boolean isVariant)

0 votesVote for this answer Mark as a Correct answer

daniel dixon answered on March 9, 2015 18:12

Hey Brenden thanks for you help, I copied the code you gave me above into the file but now I get a error below. I am not great with the code as I am more a front end developer so any help you can give me is very much appreciated

Message: c:\inetpub\wwwroot\UC_Global\CMS\CMSWebParts\ThePixelShop\OAuthTwitterFeed.ascx.cs(24): error ASPNET: Make sure that the class defined in this code file matches the 'inherits' attribute, and that it extends the correct base class (e.g. Page or UserControl).

Exception type: System.Web.HttpCompileException Stack Trace: at System.Web.Compilation.AssemblyBuilder.Compile() at System.Web.Compilation.BuildProvidersCompiler.PerformBuild() at System.Web.Compilation.BuildManager.CompileWebFile(VirtualPath virtualPath) at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate) at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate) at System.Web.Compilation.BuildManager.GetVPathBuildResult(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean ensureIsUpToDate) at System.Web.UI.TemplateControl.LoadControl(VirtualPath virtualPath) at CMS.PortalControls.CMSWebPartZone.LoadWebPart(Control container, CMSWebPartZone zone, WebPartInstance part, Boolean reloadData, Boolean isVariant)

Message: c:\inetpub\wwwroot\UC_Global\CMS\CMSWebParts\ThePixelShop\OAuthTwitterFeed.ascx.cs(24): error ASPNET: Make sure that the class defined in this code file matches the 'inherits' attribute, and that it extends the correct base class (e.g. Page or UserControl).

Exception type: System.Web.HttpCompileException Stack Trace: at System.Web.Compilation.AssemblyBuilder.Compile() at System.Web.Compilation.BuildProvidersCompiler.PerformBuild() at System.Web.Compilation.BuildManager.CompileWebFile(VirtualPath virtualPath) at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate) at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate) at System.Web.Compilation.BuildManager.GetVPathBuildResult(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean ensureIsUpToDate) at System.Web.UI.TemplateControl.LoadControl(VirtualPath virtualPath) at CMS.PortalControls.CMSWebPartZone.LoadWebPart(Control container, CMSWebPartZone zone, WebPartInstance part, Boolean reloadData, Boolean isVariant)

0 votesVote for this answer Mark as a Correct answer

daniel dixon answered on March 9, 2015 18:40

Hi Brenden,

Sorry about the last post I read it again and realised the issue and have now fixed this, the class was pointing to a different file.

This is now working and I am very grateful for your help.

Regards Dan Dixon

0 votesVote for this answer Mark as a Correct answer

   Please, sign in to be able to submit a new answer.