Blog

Categorised by 'Social API's & Integration'.

  • Don’t you love the good ol’ days back when querying API’s provided by social platforms was a straight forward process and at a time when it felt like there were fewer barriers for entry. This thought came to mind when I had to use Instagram’s API feed again recently to output a list of photos for one of my personal projects.

    The last time I carried out any interaction with Instagram's API was back in 2013 where it was a much more simpler affair. All that was required is to generate an access token that never expired, which could then be used against any API endpoint to get data back from Instagram.

    Reading Jamie Maguires really useful three-part blog post on tapping into Instagrams API gave me a good foundation into how the API has changed since I used it last. But the example he used required the profile you wanted to interact with had to be set up as a business user. However, interacting with Instagram’s API requires a lot more effort if you (like most users) do not have a business profile.

    As far as I understand it, if you plan on making any interaction with Instagrams API as a non-business user the process is:

    1. Create Facebook Developer App
    2. Authenticate application by logging into the Instagram account. This will then generate a short-lived token valid for 1 hour.
    3. Exchange the short-lived token for a long-lived token that is valid for 60 days. This token will need to be stored somewhere at the application level.
    4. Ensure the long-lived token is refreshed before it expires within the 60-day window.

    It is very important that we are using the long-lived token and to continually renew it by having some form of background process that carries out this check, whether this is at application or Azure level. We can then use this token to make queries to Instagram API endpoints.

    In this post, I am going to perform very simple integration to output a list of images from an Instagram profile. By demonstrating this, we should then get a fair idea on how to interact with the other API endpoints. Personally, setting everything up the process to acquire an access-token is the part that requires the most effort. So let's get to it!

    Create a Facebook Developer Application

    Since Facebook has taken over Instagram, naturally the application process starts within Facebook's developer site, which can be found at: https://developers.facebook.com/apps/. Once you have logged in using your Facebook credentials, the following steps will need to be carried out:

    • Select "Add New App". In the popup, enter the application name and contact email.
    • You will be presented with a list of products. Select "Instagram" by pressing the "Setup" button.
    • From the left-hand navigation, go to: Settings > Basic. Scroll to the bottom and click on the "Add platform" button. From the popup, select "Website".
    • Enter the website URL. For testing, this will be the URL we set up in the previous section. Ensure this URL is prefixed with https://. Click the "Save" button.
    • Again, from the left-hand navigation (under Products > Instagram), select: "Basic Display". At the bottom of the page, click the "Create New App" button.
    • Enter the following fields (based on our test domain):
    • Valid OAuth Redirect URIs: https://myinstagramapp.surinderbhomra.com/Instagram/Auth
    • Deauthorize Callback URL: https://myinstagramapp.surinderbhomra.com
    • Data Deletion Requests: https://myinstagramapp.surinderbhomra.com
    • Add an Instagram Test user. This can be the clients Instagram profile name.
    • Add instagram_graph_user_media permission, so we can read the profile images.
    • Click "Save Changes" button.

    You will have noticed I have added website URL’s for the OAuth Redirect, Deauthorize Redirect and Deletion Request fields. As these fields are required, you can enter the dummy website URL for local development purposes. Just remember to change this when you move the application to the live domain. In the meantime to utilise those dummy URL’s, your local host file will need to be updated. Please refer to a post I wrote in 2012 for further information. It might be over 8 years old, but the process is the same even if the Facebook Developer interface has changed.

    Once the Developer Application has been set up, grab the App ID and App Secret to be used in our demo application.

    Photo Feed Application

    The Photo Feed application will provide a very simple demonstration of the authentication process and interacting with the API to get back our media objects. From here, you will have the tools to improve and expand on this to delve deeper into other API endpoints Instagram has to offer.

    To start, we have two helper classes that the application will rely on:

    1. InstagramAuthProvider
    2. InstagramMediaProvider

    InstagramAuthProvider

    The InstagramAuthProvider carries out all authentication processes for acquiring both short and long-lived tokens.

    public class InstagramAuthProvider
    {
        #region Json Response Objects
    
        public class AuthenticateRequest
        {
            [JsonProperty("error_type")]
            public string ErrorType { get; set; }
    
            [JsonProperty("code")]
            public int StatusCode { get; set; }
    
            [JsonProperty("error_message")]
            public string ErrorMessage { get; set; }
    
            [JsonProperty("access_token")]
            public string AccessToken { get; set; }
    
            [JsonProperty("user_id")]
            public long UserId { get; set; }
        }
    
        public class LongLivedTokenRequest
        {
            [JsonProperty("access_token")]
            public string AccessToken { get; set; }
    
            [JsonProperty("token_type")]
            public string TokenType { get; set; }
    
            [JsonProperty("expires_in")]
            public long ExpiresInSeconds { get; set; }
        }
    
        #endregion
    
        /// <summary>
        /// Carries out initial authentication approach after user had approved app to Instagram account link.
        /// Returns a short-lived token valid for 1 hour.
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public static async Task<AuthenticateRequest> GetAccessTokenAsync(string code)
        {
            string authResponse = string.Empty;
    
            if (!string.IsNullOrEmpty(code))
            {
                Dictionary<string, string> parameters = new Dictionary<string, string>
                    {
                        { "client_id", ConfigurationManager.AppSettings["Instagram.ClientID"].ToString() },
                        { "client_secret", ConfigurationManager.AppSettings["Instagram.AppSecret"].ToString() },
                        { "grant_type", "authorization_code" },
                        { "redirect_uri", $"{ConfigurationManager.AppSettings[“Site.Domain"]}{ConfigurationManager.AppSettings["Instagram.AuthRedirectPath"]}" },
                        { "code", code }
                    };
    
                FormUrlEncodedContent encodedParameters = new FormUrlEncodedContent(parameters);
    
                HttpClient client = new HttpClient();
    
                HttpResponseMessage response = await client.PostAsync("https://api.instagram.com/oauth/access_token", encodedParameters);
                authResponse = await response.Content.ReadAsStringAsync();
            }
    
            return JsonConvert.DeserializeObject<AuthenticateRequest>(authResponse);
        }
    
        /// <summary>
        /// Exchanges a short-lived token for a long-lived token that are valid for 60 days.
        /// </summary>
        /// <param name="shortliveAccessToken"></param>
        /// <returns></returns>
        public static async Task<LongLivedTokenRequest> GetLongLifeTokenAsync(string shortliveAccessToken)
        {
            string authResponse = string.Empty;
    
            if (!string.IsNullOrEmpty(shortliveAccessToken))
            {
                HttpClient client = new HttpClient();
    
                HttpResponseMessage response = await client.GetAsync($"https://graph.instagram.com/access_token?client_secret={ConfigurationManager.AppSettings["Instagram.AppSecret"].ToString()}&grant_type=ig_exchange_token&access_token={shortliveAccessToken}");
                authResponse = await response.Content.ReadAsStringAsync();
            }
    
            return JsonConvert.DeserializeObject<LongLivedTokenRequest>(authResponse);
        }
    
        /// <summary>
        /// Refresh a long-lived Instagram User Access Token that is at least 24 hours old but has not expired.
        /// </summary>
        /// <param name="longLivedAccessToken"></param>
        /// <returns></returns>
        public static async Task<LongLivedTokenRequest> RefreshTokenAsync(string longLivedAccessToken)
        {
            string authResponse = string.Empty;
    
            if (!string.IsNullOrEmpty(longLivedAccessToken))
            {
                HttpClient client = new HttpClient();
    
                HttpResponseMessage response = await client.GetAsync($"https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token={longLivedAccessToken}");
                authResponse = await response.Content.ReadAsStringAsync();
            }
    
            return JsonConvert.DeserializeObject<LongLivedTokenRequest>(authResponse);
        }
    }
    

    InstagramMediaProvider

    The InstagramMediaProvider returns media information based on the authentication token.

    public class InstagramMediaProvider
    {
        #region Json Response Objects
    
        public class MediaCollection
        {
            [JsonProperty("data")]
            public List<MediaInfo> Data { get; set; }
        }
    
        public class MediaInfo
        {
            [JsonProperty("id")]
            public string Id { get; set; }
    
            [JsonProperty("caption")]
            public string Caption { get; set; }
    
            [JsonProperty("permalink")]
            public string InstagramUrl { get; set; }
    
            [JsonProperty("media_type")]
            public string Type { get; set; }
    
            [JsonProperty("thumbnail_url")]
            public string VideoThumbnailUrl { get; set; }
    
            [JsonProperty("media_url")]
            public string Url { get; set; }
        }
    
        #endregion
    
        private string _accessToken;
    
        public InstagramMediaProvider(string accessToken)
        {
            _accessToken = accessToken;
        }
    
        /// <summary>
        /// Gets list of all user images.
        /// </summary>
        /// <returns></returns>
        public async Task<List<MediaInfo>> GetUserMedia()
        {
            var mediaInfo = await GetAllMediaAsync();
    
            if (mediaInfo?.Data.Count > 0)
                return mediaInfo.Data;
            else
                return new List<MediaInfo>();
        }
    
        /// <summary>
        /// Outputs information about a single media item.
        /// </summary>
        /// <returns></returns>
        private async Task<MediaCollection> GetAllMediaAsync()
        {
            string mediaResponse = string.Empty;
    
            HttpClient client = new HttpClient();
    
            HttpResponseMessage response = await client.GetAsync($"https://graph.instagram.com/me/media?fields=id,media_type,media_url,thumbnail_url,permalink,caption,timestamp&access_token={_accessToken}");
    
            mediaResponse = await response.Content.ReadAsStringAsync();
    
            if (response.StatusCode != HttpStatusCode.OK)
                return null;
    
            return JsonConvert.DeserializeObject<MediaCollection>(mediaResponse);
        }
    }
    

    Authorisation and Authentication

    The authorisation and authentication functionality will be performed in the InstagramController.

    public class InstagramController : Controller
    {
         /// <summary>
         /// Authorises application with Instagram.
         /// </summary>
         /// <returns></returns>
        public ActionResult Authorise()
        {
            return Redirect($"https://www.instagram.com/oauth/authorize?client_id={ConfigurationManager.AppSettings["Instagram.AppID"].ToString()}&redirect_uri={ConfigurationManager.AppSettings[“Site.Domain"]}{ConfigurationManager.AppSettings["Instagram.AuthRedirectPath"]}&scope=user_profile,user_media&response_type=code");
        }
    
        /// <summary>
        /// Makes authentication request to create access token.
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public async Task<ActionResult> Auth(string code)
        {
            InstagramAuthProvider.AuthenticateRequest instaAuth = await InstagramAuthProvider.GetAccessTokenAsync(code);
    
            if (!string.IsNullOrEmpty(instaAuth?.AccessToken))
            {
                InstagramAuthProvider.LongLivedTokenRequest longTokenRequest = await InstagramAuthProvider.GetLongLifeTokenAsync(instaAuth.AccessToken);
    
                if (!string.IsNullOrEmpty(longTokenRequest?.AccessToken))
                {                
                   // Storing long-live token in a session for demo purposes. 
                   // Store the token in a more permanent place, such as a database.
                    Session["InstagramAccessToken"] = longTokenRequest.AccessToken;
    
                    return Redirect("/");
                }
            }
    
            return Content("Error authenticating.");
        }
    }
    

    Authorise

    Before we can get any of our tokens, the first step is to get authorisation from Instagram against our web application. So somewhere in the application (preferably not publicly visible), we will need an area that will kick this off. In this case, by navigating to /Instagram/Authorise, will cause the Authorise action in the controller to be fired.

    All the Authorise action does is takes you to Instagrams login page and sends over the App ID and Redirect Path. Remember, the Redirect path needs to be exactly as you've set it in your Facebook Developer Application. Once you have successfully logged in, you’ll be redirected back to the application.

    NOTE: I’ll be honest here and say I am not sure if there is a better way to acquire the access token as it seems very odd to me that you have to log in to Instagram first. If any of you know of a better way, please leave a comment.

    Auth

    If the authorise process was successful, you will be redirected back the application and be given an authorisation code. The authorisation code will be parsed to the InstagramAuthProvider.GetAccessTokenAsync() method to be exchanged for our first access-token valid - short-lived valid for 1 hour.

    The last step of the process is to now send the short-lived access token to the InstagramAuthProvider.GetLongLifeTokenAsync() method that will carry out a final exchange to retrieve our long-life token valid for 60 days. It is this token we need to store somewhere so we can use it against any Instagram API endpoint.

    In my example, the long-life token is stored in a Session for demonstration purposes. In a real-world application, we would want to store this in a database somewhere and have a scheduled task in place that will call the InstagramAuthProvider.RefreshTokenAsync() method every 59 days for the long life token to be renewed for another 60 days.

    Photo Feed

    Now we come onto the easy part - use the long-live access token to return a list of photos.

    public class InstagramMediaController : Controller
    {
        /// <summary>
        /// Outputs all user images from Instagram profile.
        /// </summary>
        /// <returns></returns>
        [OutputCache(Duration = 60)]
        public PartialViewResult PhotoGallery()
        {
            if (Session["InstagramAccessToken"] != null)
            {
                InstagramMediaProvider instaMedia = new InstagramMediaProvider(Session["InstagramAccessToken"].ToString());
    
                return PartialView("_PhotoGallery", Task.Run(() => instaMedia.GetUserMedia()).Result);
            }
    
            return PartialView("_PhotoGallery", new List<InstagramMediaProvider.MediaInfo>());
        }
    }
    

    This controller contains a single piece of functionality - a PhotoGallery partial view. The PhotoGallery partial view uses the InstagramMediaProvider.GetUserMedia() method to return a collection of profile photos.

    Final Thoughts

    Before carrying out any Instagram integration, think about what you are trying to achieve. Look through the documentation to ensure the API's you require are available as some permissions and features may require business or individual verification to access live data.

    Also, you may (or may not) have noticed whilst going through the Facebook Development Application setup that I never submitted the application for review. I would advise you to always submit your application to Facebook as you might find your access is revoked. For personal purposes where you're only outputting your own Instagram profile information, you could just leave it "In development" mode. But again, I do not advise this, especially when working with Business accounts.

  • For a site I'm working on, the Facebook's Comments plugin is being utilised on all our article pages. There was a requirement to pull in the latest comments in a listing page for each of these article pages as well as number of comments. Facebook's JavaScript library provides the ability to display a comments counter but not the ability to pull out x number of comments. So we'll have to go server-side and use Graph API to get the data we want.

    In this post, I will show you how you can get back all comments for a page by it's full URL.

    Prerequisites

    Before we get into the main C# logic methods, you need to make sure we have a few things in place:

    • ApiWebRequestHelper Class
    • Newtonsoft Json
    • Facebook App Settings
    • Class Objects

    ApiWebRequestHelper Class

    Whenever I am making a call to Facebook's Graph API endpoints, I will be making references to a "ApiWebRequestHelper" helper class. This is something I developed last month to make it easier for me to deserialize XML or JSON requests to a strongly-typed class object. You can take a look at the full code here.

    Newtonsoft Json

    The Newtonsoft Json library is a key ingredient to any JSON web requests. I'd be surprised if you've never heard or used it. :-) Nevertheless, you can get it here: http://www.newtonsoft.com/json.

    Facebook App Settings

    I haven't created a Facebook App for quite some time and things have changed very slightly in terms of the interface and options presented. The key things you need to get out of your created App is:

    • Application ID
    • Application Secret
    • Client Token

    I set the security settings with the following modes, which can be found in Settings > Advanced >  Security.

    Facebook App Advanced API Settings

    Class Objects

    The following class objects will be used to deserialize Graph API requests into class objects.

    The FacebookPageInfo, FacebookPage and FacebookPageShare objects will get the core information about the queried page, such as the Title and Description, as well as the comments and share counts.

    namespace Site.BusinessObjects.Facebook
    {
        public class FacebookPageInfo
        {
            [JsonProperty("id")]
            public string Id { get; set; }
    
            [JsonProperty("og_object")]
            public FacebookPage Page { get; set; }
    
            [JsonProperty("share")]
            public FacebookPageShare Share { get; set; }
        }
    
        public class FacebookPage
        {
            [JsonProperty("id")]
            public string Id { get; set; }
    
            [JsonProperty("description")]
            public string Description { get; set; }
    
            [JsonProperty("title")]
            public string Title { get; set; }
    
            [JsonProperty("type")]
            public string Type { get; set; }
    
            [JsonProperty("updated_time")]
            public DateTime UpdatedTime { get; set; }
    
            [JsonProperty("url")]
            public string Url { get; set; }
        }
    }
    
    namespace Site.BusinessObjects.Facebook
    {
        public class FacebookPageShare
        {
            [JsonProperty("comment_count")]
            public int CommentCount { get; set; }
    
            [JsonProperty("share_count")]
            public int ShareCount { get; set; }
        }
    }
    

    All comments for a page will be stored in the following objects:

    namespace Site.BusinessObjects.Facebook
    {
        public class FacebookPageCommentInfo
        {
            public int TotalComments { get; set; }
            public List<FacebookCommentItem> Comments { get; set; }
        }
    }
    
    namespace Site.BusinessObjects.Facebook
    {
        public class FacebookCommentItem
        {
            [JsonProperty("id")]
            public string Id { get; set; }
    
            [JsonProperty("created_time")]
            public DateTime CreatedTime { get; set; }
    
            [JsonProperty("from")]
            public FacebookCommentFrom From { get; set; }
    
            [JsonProperty("message")]
            public string Message { get; set; }
        }
    
        public class FacebookCommentFrom
        {
            [JsonProperty("id")]
            public string Id { get; set; }
    
            [JsonProperty("name")]
            public string Name { get; set; }
        }
    }
    

    Facebook Logic Class

    Now that we have the pre-requisites in place, lets get to the code that will perform the required functions:

    namespace Site.BusinessLogic
    {
        public class FacebookLogic
        {
            private string _accessToken;
    
            /// <summary>
            /// Uses default Client ID and Secret as set in the web.config.
            /// </summary>
            public FacebookLogic()
            {
                GetAccessToken(Config.Facebook.ClientId, Config.Facebook.ClientSecret);
            }
    
            /// <summary>
            /// Requires  Client ID and Secret.
            /// </summary>
            /// <param name="clientId"></param>
            /// <param name="clientSecret"></param>
            public FacebookLogic(string clientId, string clientSecret)
            {
                GetAccessToken(clientId, clientSecret);
            }
    
            /// <summary>
            /// Gets page info that has been shared to Facebook.
            /// </summary>
            /// <param name="pageUrl"></param>
            /// <returns></returns>
            public FacebookPageInfo GetPage(string pageUrl)
            {
                return ApiWebRequestHelper.GetJsonRequest<FacebookPageInfo>($"https://graph.facebook.com/{pageUrl}?access_token={_accessToken}");
            }
    
            /// <summary>
            /// Gets comments for a page based on its absolute URL.
            /// </summary>
            /// <param name="pageUrl"></param>
            /// <param name="maxComments"></param>
            public FacebookPageCommentInfo GetPageComments(string pageUrl, int maxComments)
            {
                try
                {
                    // Get page information in order to retrieve page ID to pass to commenting.
                    FacebookPageInfo facebookPage = GetPage(pageUrl);
    
                    if (facebookPage.Page != null)
                    {
                        return new FacebookPageCommentInfo
                        {
                            TotalComments = facebookPage.Share.CommentCount,
                            Comments = GetCommentsByPageId(facebookPage.Page.Id, maxComments).Comments
                        };
                    }
                    else
                    {
                        return null;
                    }
                }
                catch (Exception ex)
                {
                    // NOTE: Log exception here...
    
                    return null;
                }
            }
    
            /// <summary>
            /// Gets comments by Facebook's Page ID.
            /// </summary>
            /// <param name="fbPageId"></param>
            /// <param name="max"></param>
            /// <returns></returns>
            public FacebookCommentInfo GetCommentsByPageId(string fbPageId, int max = 10)
            {
                return ApiWebRequestHelper.GetJsonRequest<FacebookCommentInfo>($"https://graph.facebook.com/comments?id={fbPageId}&access_token={_accessToken}&limit={max}");
            }
    
            /// <summary>
            /// Retrieves Access Token from Facebook App.
            /// </summary>
            /// <param name="clientId"></param>
            /// <param name="clientSecret"></param>
            private void GetAccessToken(string clientId, string clientSecret)
            {
                UriBuilder builder = new UriBuilder($"https://graph.facebook.com/oauth/access_token?client_id={Config.Facebook.ClientId}&client_secret={Config.Facebook.ClientSecret}&grant_type=client_credentials");
    
                try
                {
                    using (WebClient client = new WebClient())
                    {
                        // Get Access Token from incoming response.
                        string data = client.DownloadString(builder.Uri);
    
                        NameValueCollection parsedQueryString = HttpUtility.ParseQueryString(data);
    
                        _accessToken = parsedQueryString["access_token"];
                    }
                }
                catch (Exception ex)
                {
                    // NOTE: Log exception here...
                }
            }
        }
    }
    

    By default, on initiation of the FacebookLogic class, the Application ID and Secret values will be inherited from the web.config, or you can pass in these values directly with the class overload parameters.

    Out of all the methods used here, we're interested in only using one: GetPageComments(). What you will notice from this method is that we cannot get the comments from one API call alone. We first have to make an extra API call to get the ID of the page. This ID is passed to the GetCommentsByPageId() method, to return all comments.

    Usage

    Comments for a page can be returned by adding the following in your code, where you will then be able to access properties to iterate through the comments:

    FacebookLogic fbl = new FacebookLogic();
    
    // Pass in the page URL and number of comments to be returned.
    var pageComments = fbl.GetPageComments("https://www.surinderbhomra.com/", 2);
    

    Whenever you call this piece of code, I would make sure you cache the results for 5 - 10 minutes, so you do not use up your API request limits.

  • This is something I have been meaning to post for quite some time, ever since I first started working on integrating Instagram's API in web applications from 2013. - The ability to resize an image from Instagram without having to deal with registering for an API key and worrying about request limits.

    This approach is ideal if you have no requirement to get further information about an image, such as description, comments, likes etc.

    Believe it or not, Instagram contains quite a neat (somewhat hidden) feature that gives you the ability to output three different sized images directly into a webpage, by constructing the path to an image as so: https://www.instagram.com/p/&lt;image-id&gt;**/media/?size=&lt;size-parameter&gt;**.

    The supported "size parameters" are:

    • t - for thumbnail (150px x 150px)
    • m - for medium (306px x 306px)
    • l - for large (640px x 640px)

    The great thing about using the media parameter is that the requested image size is served up immediately. For example, we could embed an Instagram image directly into our HTML markup as so:

    <p style="text-align: center;">
      <img alt="Mr. Brown - The Office Dog" src="https://www.instagram.com/p/uz_8x2qW6E/media/?size=m">
    </p>
    

    Which will render the following output:

    Mr. Brown - The Office Dog

    In this case, this is a picture of Mr. Brown (the office dog) from my Instagram profile in medium size.

  • It seems there is going to be a growing trend where apps on our mobile devices will open webpages whilst you are inside the app itself instead of using the devices' native browser. A prime example of this is Facebook. In recent updates during the tail end of last year, both their iOS and Android offerings open webpages from within the application.

    This isn't a bad thing. In fact I quite like having webpages opening within the application, since this creates a nice seamless experience. However, the Facebook in-app browser doesn't seem to render a webpage in the same manner as the devices' own native browser (Safari/Chrome). I started noticing this whilst working on a complex website that was very much custom JavaScript driven.

    The only thing I could do is modify specific mark-up or features that affected my website negatively when opened from within Facebook by detecting the user-agent. In my code (using ASP.NET C#), I was required to carry out additional browser checks:

    //User is within Facebook browser.
    if (Request.UserAgent.IndexOf("FBAN") > -1)
    {
        if (Request.UserAgent.Contains("iPhone OS 8_0_2"))
        {
            //You are using iPhone version 8.0.2.
        }
        
        if (Request.UserAgent.Contains("Chrome"))
        {
            //You are in the Facebook App in Android.
        }
    }
    else
    {
        //You are not in Facebook App.
    }
    

    You can modify the code above to create a nice self-contained method to return an enumeration as I ended up doing to be used when required.

  • I've written some code that outputs images using Instagram's Developer API. The code can either output images based on a user's profile or via search term.

    As you may already know, in order to get any form of information from any external API an access token is required. Before we dive into some code, the first thing that we need to do is register ourselves as an Instagram Developer by going to: http://instagram.com/developer/.

    Next, we need to register a new client specifically for our intended use. In my case, all I want to do is to get all image information from my own Instagram profile.

    Instagram API - Register New Client

    Here, you will be supplied with Client ID and Client Secret codes. But most importantly, you will need to set an OAuth Redirect URL (or Callback URL) for user's to authenticate your application.

    The strange thing I've noticed about the Instagram API is that a callback page is a compulsory requirement. Even if you are planning on carrying out something as simple as listing some images from your own profile where a public users intervention is not required.

    I'm not interested in their images, I'm interested in my own. I hope Instagram changes this soon. If Twitter can allow you to retrieve tweets by simply registering your application, why can't Instagram?

    From what I've read on Instagram's Google Group's is that an access token needs to only be generated once and they don't expire. But of course Instagram have stated:

    "These tokens are unique to a user and should be stored securely. Access tokens may expire at any time in the future."

    Just make sure you have some fail safe's in your code that carries out the re-authentication process within your application on the event access token has expired. In my own implementation, I've kept the callback page secret and new access token requests can be made within an Administration interface.

    So lets get to the code.

    Step 1: Authentication Request Classes

    These strongly-typed classes mirror the exact structure of the JSON returned from our authentication request. Even though we only require the "access_token" property, I've added additional information, such as details on the Instagram user making the request.

    public class AuthToken
    {
        [JsonProperty("access_token")]
        public string AccessToken { get; set; }
    
        [JsonProperty("user")]
        public InstagramUser User { get; set; }
    }
    
    public class InstagramUser
    {
        [JsonProperty("id")]
        public string ID { get; set; }
    
        [JsonProperty("username")]
        public string Username { get; set; }
    
        [JsonProperty("full_name")]
        public string FullName { get; set; }
    
        [JsonProperty("profile_picture")]
        public string ProfilePicture { get; set; }
    }
    

    It's worth noting at this point that I'm using Newtonsoft.Json framework.

    Step 2: Callback Page

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!String.IsNullOrEmpty(Request["code"]) && !Page.IsPostBack)
        {
            try
            {
                string code = Request["code"].ToString();
    
                NameValueCollection parameters = new NameValueCollection();
                parameters.Add("client_id", ConfigurationManager.AppSettings["instagram.clientid"].ToString());
                parameters.Add("client_secret", ConfigurationManager.AppSettings["instagram.clientsecret"].ToString());
                parameters.Add("grant_type", "authorization_code");
                parameters.Add("redirect_uri", ConfigurationManager.AppSettings["instagram.redirecturi"].ToString());
                parameters.Add("code", code);
    
                WebClient client = new WebClient();
                var result = client.UploadValues("https://api.instagram.com/oauth/access_token", "POST", parameters);
    
                var response = System.Text.Encoding.Default.GetString(result);
    
                var jsResult = JsonConvert.DeserializeObject(response);
    
                //Store Access token in database
                InstagramAPI.StoreAccessToken(jsResult.AccessToken);
    
                Response.Redirect("/CallbackSummary.aspx?status=success", false);
            }
            catch (Exception ex)
            {  
                EventLogProvider.LogException("Instagram - Generate Authentication Key", "INSTAGRAM", ex);
    
                Response.Redirect("/CallbackSummary.aspx?status=error");
            }
        }
    }
    

    As you can see, I'm redirecting the user to a "CallbackSummary" page to show if the authentication request was either a success or failure. (Remember, the page is secured within my own Administration interface.)

    If the request is successful, the access token is stored.

    Step 3: Request Callback Page

    The last piece of the puzzle is to actually request our callback page by authorizing ourselves via Instagram API. In this case, I just have a simple page with the following mark up:

    <p>If Instagram fails to output images to the page, this maybe because a new Authorisation key needs to be generated.</p>
    <p>To generate a new key, press the button below and follow the required steps.</p>
    <a onclick="window.open('https://api.instagram.com/oauth/authorize/?client_id=<%=ConfigurationManager.AppSettings["instagram.clientid"].ToString() %>&redirect_uri=<%=ConfigurationManager.AppSettings["instagram.redirecturi"].ToString() %>&response_type=code', 'newwindow', config='height=476,width=641,toolbar=no, menubar=no, scrollbars=no, resizable=no,location=no,directories=no, status=no'); return false;" href="#" target="_parent">Generate</a>
    

    If all goes to plan, you should have successfully recieved the access token.

    I will post more Instagram code in future posts.

  • Ever since Twitter ditched version 1 of their API to version 1.1, an additional hurdle created when attempting to get any data from Twitter. Authentication (using OAuth) is now required on all API request endpoints. I can see why Twitter decided to go down this route but it does add a little headache when carrying out the most simplest requests.

    In all the sites I have worked on, I've always relied on third party libraries to help me interact with Twitter, such as Twitterizer (no v1.1 support) and TweetSharp. But due to the increased complexity on some of the sites I've been working on, third party libraries don't seem to cut the mustard any more. A more scalable solution is required...

    I came across a really simple and expandable class library called OAuthTwitterWrapper that stemmed from a StackOverflow question I found. Out-of-the-box, this library contains calls required to retrieve a user's timeline and return searches which is great to get you up and running quickly.

    The OAuthTwitterWrapper provided me a really good basis to add further Twitter features, for example a list of users favourite tweets.

    If you don't plan on doing anything too complex when interacting with the Twitter API, third party libraries such as TweetSharp will meet your everyday needs. However, if you want more control over how you make requests, the OAuthTwitterWrapper provides a good foundation.

  • It seems that I have a tendency to blog more about YouTube then any other Social API on this site. So here we go again... This time I want to show how to easily integrate a YouTube CMS Form Control within a Custom Table or Document Type within Kentico.

    As far as I'm aware, Kentico only allows you to insert YouTube markup into their HTML Editable Regions via the CKEditor. But what if you wanted to take things a step further and have the ability to return a video Title, Description and Thumbnail within the comfort of the Form tab?

    YouTube Custom CMS Form Control

    As you can see from my custom form control, a user would paste the URL of a YouTube video and press the "Lookup Video" button that will return basic information about that video, ready for the user to carry out any further copy changes they require.

    So let's get to it.

    Step 1: Create A New User Control

    I have created a user control in "/CMSFormControls/Surinder/" of my Kentico installation. I have named the user control: YouTubeLookup.ascx.

    HTML

    <table><tbody>	<tr>		<td class="TextColumn">			<label for="<%=YouTubeUrl.ClientID >">URL:</label> <asp:textbox id="YouTubeUrl" runat="server"></asp:textbox> <asp:button cssclass="ContentButton" id="LookupVideoDetail" onclick="LookupVideoDetail_Click" runat="server" text="Lookup Video"> </asp:button></td>	</tr>	<tr>		<td class="TextColumn">			<label for="<%=YouTubeTitle.ClientID >">Title:</label> <asp:textbox id="YouTubeTitle" runat="server" width="500"></asp:textbox></td>	</tr>	<tr>		<td class="TextColumn">			<label for="<%=YouTubeDescription.ClientID >">Description:</label> <asp:textbox height="100" id="YouTubeDescription" runat="server" textmode="MultiLine" width="500"></asp:textbox></td>	</tr>	<tr>		<td class="TextColumn">			<asp:image id="YouTubeThumbnail" runat="server"></asp:image></td>	</tr></tbody>
    </table>
    

    Code-behind

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using CMS.FormControls;
    using CMS.GlobalHelper;
    using System.Web.Script.Serialization;
    
    public partial class CMSFormControls_Surinder_YouTubeLookup : FormEngineUserControl
    {
        private string _jsonValue;
    
        public override Object Value
        {
            get
            {
                return GetJsonMarkup();
            }
            set
            {
                _jsonValue = System.Convert.ToString(value);
            }
        }
    
        private string GetJsonMarkup()
        {
            //Pass all user entered form values to the YouTubeDetail class for serialization in the JavaScriptSerializer
            if (!String.IsNullOrEmpty(YouTubeUrl.Text))
            {
                YouTubeDetail yt = new YouTubeDetail();
                yt.ID = YouTubeHelper.GetVideoID(YouTubeUrl.Text);
                yt.Title = YouTubeTitle.Text;
                yt.Description = YouTubeDescription.Text;
                yt.Url = YouTubeUrl.Text;
                yt.ImageUrl = YouTubeThumbnail.ImageUrl;
    
                JavaScriptSerializer jsSerialize = new JavaScriptSerializer();
    
                return jsSerialize.Serialize(yt);
            }
            else
            {
                return String.Empty;
            }
        }
    
        public override bool IsValid()
        {
            JavaScriptSerializer jsSerialize = new JavaScriptSerializer();
            var jsResult = jsSerialize.Deserialize<YoutubeDetail>(_jsonValue);
    
            if (jsResult != null && !String.IsNullOrEmpty(jsResult.ToString()))
            {
                if (String.IsNullOrEmpty(jsResult.Url))
                    return false;
                else
                    return true;
            }
            else
            {
                return true;
            }
        }
    
        protected void EnsureItems()
        {
            PopulateControls();
        }
    
        private void PopulateControls()
        {
            JavaScriptSerializer jsSerialize = new JavaScriptSerializer();
            var jsResult = jsSerialize.Deserialize<YoutubeDetail>(_jsonValue);
    
            //Check there if JSON is present to populate form controls
            if (jsResult != null && !String.IsNullOrEmpty(jsResult.ToString()))
            {
                if (!String.IsNullOrEmpty(jsResult.Url))
                    YouTubeUrl.Text = jsResult.Url;
    
                if (!String.IsNullOrEmpty(jsResult.Title))
                    YouTubeTitle.Text = jsResult.Title;
    
                if (!String.IsNullOrEmpty(jsResult.Description))
                    YouTubeDescription.Text = jsResult.Description;
    
                if (!String.IsNullOrEmpty(jsResult.ImageUrl))
                    YouTubeThumbnail.ImageUrl = jsResult.ImageUrl;
            }
        }
    
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
                EnsureItems();
        }
    
        protected void LookupVideoDetail_Click(object sender, EventArgs e)
        {
            //If YouTube URL is present, get the information
            if (!String.IsNullOrEmpty(YouTubeUrl.Text))
            {
                YouTubeDetail yt = YouTubeHelper.GetVideoInformation(YouTubeUrl.Text);
    
                if (yt != null)
                {
                    YouTubeTitle.Text = yt.Title;
                    YouTubeDescription.Text = yt.Description;
                    YouTubeThumbnail.ImageUrl = yt.ImageUrl;
                }
            }
        }
    }
    

    From looking at my code, you've probably noticed I'm actively using a "JavaScriptSerializer" to pass all my form values as JSON. I find this is the most straight-forward way to store multiple form values in a custom control. In this case, our values will be stored within a Kentico table column in the following format:

    {
        "ID":"fLyoog562x4",
        "Title":"How The Dark Knight Rises Should Have Ended",
        "Description":"Check out HISHE\u0027s spin on the epic conclusion to The Dark Knight Trilogy: How The Dark Knight Rises Should Have Ended.",
        "Url":"http://www.youtube.com/watch?v=fLyoog562x4",
        "ImageUrl":"http://i1.ytimg.com/vi/fLyoog562x4/hqdefault.jpg"
    }
    

    Whenever I need to get those values back, all I need to do is call the JavaScriptSerializer.Deserialize method.

    NOTE: If what I have shown doesn't make any sense, it'll be useful to take a look at an in-depth tutorial on how to create a Custom Form Control in Kentico: http://devnet.kentico.com/docs/devguide/index.html?developing_form_controls.htm

    Step 2: Create YouTubeDetail Class

    In order to serialize and deserialize values when using the JavaScriptSerializer, we need to create a class object with a number of properties to interpret the JSON structure.

    public class YouTubeDetail
    {
        public string ID { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string Url { get; set; }
        public string ImageUrl { get; set; }
    }
    

    Step 3: YouTube Methods

    This is the part when we start using Google's YouTube API and in order for this class to work, you will need to download the necessary DLL's. I suggest you take a gander at a post I wrote a while back called "Dynamically Output A List of YouTube Videos In ASP.NET" to get an in-depth introduction into using the YouTube API.

    To get data back from YouTube you will need as a minimum requirement the DLL's and register your application in order to pass an Application Name, Developer Key and Client ID values to your application.

    public class YouTubeHelper
    {
        private static string YouTubeDeveloperKey = WebConfigurationManager.AppSettings["YouTubeDeveloperKey"].ToString();
        private static string YouTubeAppName = WebConfigurationManager.AppSettings["YouTubeAppName"].ToString();
        private static string YouTubeClientID = WebConfigurationManager.AppSettings["YouTubeClientID"].ToString();
     
        //Get YouTube video
        public static Video YouTubeVideoEntry(string videoID)
        {
            YouTubeRequestSettings settings = new YouTubeRequestSettings(YouTubeAppName, YouTubeClientID, YouTubeDeveloperKey);
            YouTubeRequest request = new YouTubeRequest(settings);
     
            //Link to the feed we wish to read from
            string feedUrl = String.Format("http://gdata.youtube.com/feeds/api/videos/{0}", videoID);
     
            Feed<Video> videoFeed = request.Get<Video>(new Uri(feedUrl));
     
            return videoFeed.Entries.SingleOrDefault();
        }
     
        //Extract the YouTube ID from the web address.
        public static string GetVideoID(string videoUrl)
        {
            Uri tempUri = new Uri(videoUrl);
     
            string sQuery = tempUri.Query;
     
            return System.Web.HttpUtility.ParseQueryString(sQuery).Get("v");
        }
     
        //Get required YouTube video information
        public static YouTubeDetail GetVideoInformation(string url)
        {
            Video v = YouTubeVideoEntry(GetVideoID(url));
     
            //Pass required YouTube information to custom class called YouTubeDetail
            YouTubeDetail vDetail = new YouTubeDetail();
            vDetail.ID = v.VideoId;
            vDetail.Title = v.Title;
            vDetail.Description = v.Description;
            vDetail.ImageUrl = v.Thumbnails[2].Url;
    
            return vDetail;
        }
    
        //Output YouTube property within a document by passing the Document ID
        public static YouTubeDetail GetDocumentYouTubeValue(int docID)
        {
            TreeProvider tree = new TreeProvider();
            TreeNode tn = tree.SelectSingleDocument(docID);
    
            if (tn.GetValue("YouTube") != null && !String.IsNullOrEmpty(tn.GetValue("YouTube").ToString()))
            {
                JavaScriptSerializer jsSerialize = new JavaScriptSerializer();
                var jsResult = jsSerialize.Deserialize<YouTubeDetail>(tn.GetValue("YouTube").ToString());
    
                if (jsResult != null && !String.IsNullOrEmpty(jsResult.ToString()))
                {
                    return jsResult;
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }
    }
    

    Step 4: Add New Control Into Kentico

    Please refer to the Kentico Development Guide (as referenced in Step 1). The main thing you need to ensure is that the control can only be used as a "Long text" type.

    YouTube Kentico Control Settings

    Step 5: Outputting The YouTube Values To A Page

    Since we have stored all our YouTube fields in a JSON string, we can get those values out by carrying out a deserialization on our document type property.

    if (CMSContext.CurrentDocument.GetStringValue("YouTubeVideo", String.Empty) != String.Empty)
    {
        JavaScriptSerializer jsSerialize = new JavaScriptSerializer();
        YouTubeDetail yt = jsSerialize.Deserialize<YouTubeDetail>(CMSContext.CurrentDocument.GetStringValue("YouTubeVideo", String.Empty));
    
        YouTubeTitle.Text = yt.Title;
        YouTubeDescription.Text = yt.Description;
        YouTubeUrl.Text = yt.Url;
    }
    

    You may think I have slightly over-engineered the process to store YouTube video's. But if you have a website that is trying to push video content along with its META data, I believe this is the way to go.

  • Ok I’ll admit Part 2 to my “Beginner’s Guide To Using Google Plus .NET API” has been on the back-burner for some time (or maybe it’s because I completely forgot). After getting quite a few email recently on the subject, I thought now would be the best time to continue with Part 2.

    It’s recommended you take a gander at Part 1 before proceeding to this post.

    As the title suggests, I will be showing how to output user’s publicly view posts. The final output of what my code will produce can be seen on my homepage under the “Google+ Posts” section.

    Create Class Object

    We will create a class called “GooglePlusPost" to allow us to easily store each item of post data within a Generic List.

    public class GooglePlusPost
    {
        public string Title { get; set; }
        public string Text { get; set; }
        public string PostType { get; set; }
        public string Url { get; set; }
    }
    

     

    Let’s Get The Posts!

    I have created a method called “GetPosts” that accepts a parameter to select the number of posts of your choice.

    public class GooglePlus
    {
        private static string ProfileID = ConfigurationManager.AppSettings["googleplus.profileid"].ToString();
        
        public static List<GooglePlusPost> GetPosts(int max)
        {
            try
            {
                var service = new PlusService();
                service.Key = GoogleKey;
                var profile = service.People.Get(ProfileID).Fetch();
    
                var posts = service.Activities.List(ProfileID, ActivitiesResource.Collection.Public);
                posts.MaxResults = max;
    
                List<GooglePlusPost> postList = new List<GooglePlusPost>();
    
                foreach (Activity a in posts.Fetch().Items)
                {
                    GooglePlusPost gp = new GooglePlusPost();
    
                    //If the post contains your own text, use this otherwise look for
                    //text contained in the post attachment.
                    if (!String.IsNullOrEmpty(a.Title))
                    {
                        gp.Title = a.Title;
                    }
                    else
                    {
                        //Check if post contains an attachment
                        if (a.Object.Attachments != null)
                        {
                            gp.Title = a.Object.Attachments[0].DisplayName;
                        }
                    }
    
                    gp.PostType = a.Object.ObjectType; //Type of post
                    gp.Text = a.Verb;
                    gp.Url = a.Url; //Post URL
    
                    postList.Add(gp);
                }
    
                return postList;
            }
            catch
            {
                return new List<GooglePlusPost>();
            }
        }
    }
    

    By default, I have ensured that my own post comment takes precedence over the contents of the attachment (see lines 24-35). If I decided to just share an attachment without a comment, the display text from the attachment will be used instead.

    There are quite a few facets of information an attachment contains and this only becomes apparent when you add a breakpoint and debug line 33. For example, if the attachment had an object of type “video”, you will get a wealth of information to embed a YouTube video along with its thumbnails and description.

    Attachment Debug View

    So there is room to make your Google+ feed much more intelligent. You just have to make sure you cater for every event to ensure your feed displays something useful without breaking. I’m in the process myself of displaying redoing my own Google+ feed to allow full access to content directly from my site.

    Recommendation

    It is recommended that you cache your collection of posts so you are not making constantly making request to Google+. You don’t want to exceed your daily request limit now do you.

    I’ve set my cache duration to refresh every three hours.

  • I’ve been working on a .NET library to retrieve all images from a users Twitpic account. I thought it would be quite a useful .NET library to have since there have been some users requesting one (including me) on some websites and forums.

    I will note that this is NOT a completely functioning Twitpic library that makes use of all API requests that have been listed on Twitpic’s developer site. Currently, the library only contains core integration on returning information of a specified user (users/show), enough to create a nice picture gallery.

    My Twitpic .NET library will return the following information:

    • ID
    • Twitter ID
    • Location
    • Website
    • Biography
    • Avatar URL
    • Image Timestamp
    • Photo Count
    • Images

    Code Example:

    private void PopulateGallery()
    {
        var hasMoreRecords = false;
    
        //Twitpic.Get(<username>, <page-number>)
        TwitpicUser tu = Twitpic.Get("sbhomra", 1);
    
        if (tu != null)
        {
            if (tu.PhotoCount > 20)
                hasMoreRecords = true;
    
            if (tu.Images != null && tu.Images.Count > 0)
            {
                //Bind Images to Repeater
                TwitPicImages.DataSource = tu.Images;
                TwitPicImages.DataBind();
            }
            else
            {
                TwitPicImages.Visible = false;
            }
        }
        else
        {
            TwitPicImages.Visible = false;
        }
    }
    

    From using the code above as a basis, I managed to create a simple Photo Gallery of my own: /Photos.aspx

    If you experience any errors or issues, please leave a comment.

    Download: iSurinder.TwitPic.zip (5.15 kb)

  • Facebook ConnectIf I need to login and authenticate a Facebook user in my ASP.NET website, I either use the Facebook Connect's JavaScript library or SocialAuth.NET. Even though these two methods are sufficient for the purpose, I don't think it's the most ideal or efficient way.

    The Facebook Connect JavaScript library is quite basic and doesn't have the flexibility required for full .NET integration through FormsAuthentication. Whereas SocialAuth.NET provides full .NET integration and all authentication is done server-side with minimal development.

    I'd say if you are looking for a straight-forward way to integrate social site authentication, SocialAuth.NET is the way to go. It's API can communicate with other social sites such as Twitter, LinkedIn and Gmail.

    Recently, I found a better and more efficient way to authenticate Facebook users on my site using Graph API and Hammock.

    Hammock is a C# a REST library for .NET that greatly simplifies consuming and wrapping RESTful services. This allows us to embrace the social site’s core technology instead of using varied SDK's or API's. There are many community driven frameworks and API's readily available on the Internet, but they can really cause problems if they evolve too quickly or haven’t been thoroughly tested.

    Suddenelfilio, has written a useful blog post on connecting Facebook using Hammock. You will see by his example that you can interact with Facebook anyway you want.

    The same principle could also be applied to other website API's that use REST based services, such as Twitter.