uservoice

[Update 3] UserVoice WP8 library for user features

It is done. I made my first library for Windows Phone 8. This blog post is about why I did it and how to use it.

Update: I changed some method names of the library to make it easier to use the library. I updated the code in this post to reflect those changes.

Update 2: I added support to load more pages with clients. On top, I also added my EmojiDetector class, as the UserVoice API does not support Emojis. Check the code below.

Update 3: Now the library supports comments on suggestions and mark as helpful for knowledge base items.

Why UserVoice?

We indie developers have a big problem: We do not have a support team to explain our users how to use our apps or how to solve certain problems/issues. Which leads to our next problem: users are customers. Customers want to be satisfied. It is our job to do this with our apps by providing them a high level user experience and feature rich apps. Often users don’t go the extra mile to send us an email to tell us what is wrong. Or they plan it, but forget about it. Or even worse: they get annoyed and uninstall our apps.

As some of you know, I am working at the hardware support team of a German phone carrier. Over the years, I learned how important it is to listen to customers, pick up their ideas and wishes  and work to get them done if possible. And if it is not possible, you need to tell them that – even that is an important part of customer service!

Many of us have set up a twitter account, a separate mail address, maybe an extra online form to catch all requests from users up. But users tend to not use them for one reason: they are not integrated in our apps. So I spend some time “googling with Bing” (Thanks to @robwirving for that awesome phrase!) on possible solutions.

Uservoice as the best value if using a free subscription, and they have an API that we can use (see also this post on how to get started with uservoice). I made it a very slim library and concentrated on the features we really need in our app on the user side.

The Library!

You can get the library easily via NuGet directly into your app. Just add this package to your app’s packages list:

uservoice_lib_nuget

The library also needs RestSharp, which gets automatically added to your project if you install the library.

After you installed it, you need to declare some variables that we need over and over again while using the library:

Urls.subDomain = "<your subdomain>";
Urls.oAuthCallBackUri = "<your callback url>";
Tokens.ConsumerKey = "<your ConsumerKey>";
Tokens.ConsumerSecret = "<your ConsumerSecret>";

You can get this values out of Settings/Integrations in your UserVoice account.

Additionally, you should save these Tokens to not ask the user for login again and again.

Tokens.AccessToken
Tokens.AccessTokenSecret  
Tokens.OwnerAccessToken
Tokens.OwnerAccessTokenSecret

Another important class you should be aware of  is the RequestParamaters class:

Screenshot (309)

It contains the needed variables for all requests, and you can easily use them to save them for TombStoning or anything else you want to save them.

Let’s have a look at the possible requests:

  • Knowledge Base:
KnowledgeBase kb = new KnowledgeBase();`   
//load complete knowledge base  
UservoiceRequests.KnowledgeBase = await kb.getAll();  
//load specific page in your knowledge base
int page = 2;
 UservoiceRequests.KnowledgeBase = await kb.getAll(page);
//load specific topic 
UservoiceRequests.KnowledgeBaseTopic = await kb.getTopic(RequestParameters.topicId);
//load specific page in topic
int page = 2;
UservoiceRequests.KnowledgeBaseTopic = await kb.getTopic(RequestParameters.topicId, page);
//mark article as helpful
var ratingResponse = await kb.markHelpful(RequestParamaeters.articleId);
  •  Suggestions:
Suggestion suggestion = new Suggestion();  
//load all suggestions  
UservoiceRequests.allSuggestions = await suggestion.getAll(RequestParameters.forumId);  
//load specific page in all suggestions
int page = 2;
UservoiceRequests.allSuggestions = await suggestion.getAll(RequestParameters.forumId, page); 
//vote on a suggestion 
UservoiceRequests.voteForSuggestion = await suggestion.vote(RequestParameters.forumId, RequestParameters.suggestionId, RequestParameters.vote);  
//submit new suggestion  
UservoiceRequests.postSuggestion = await suggestion.create(RequestParameters.forumId, RequestParameters.newSuggestionTitle, RequestParameters.newSuggestionText, RequestParameters.newSuggestionReferrer, RequestParameters.newSuggestionVotes); 
//search suggestions  
UservoiceRequests.searchSuggestion = await suggestion.search(RequestParameters.suggestionsSearchQuery);
//get all comments for suggestion:
UservoiceRequests.allCommentsForSuggestion = await suggestion.getComments(RequestParameters.forumId, RequestParameters.suggestionId);
//submit a new comment on suggestion:
UservoiceRequests.postCommentOnSuggestion = await suggestion.comment(RequestParameters.forumId, RequestParameters.suggestionId, RequestParameters.newCommentText);
  •  User data:
User user = new User();  
UservoiceRequests.User = await user.getUser();
  •  Tickets:
//Tickets are not associated with the user from the API side. However, you are able to show all tickets from a user with this:  
ticket = new Ticket();  
UservoiceRequests.AllTicketsFromUser = await ticket.getAll(RequestParameters.userMail); 
//load specific page in all tickets:
int page = 2;
UservoiceRequests.AllTicketsFromUser = await ticket.getAll(RequestParameters.userMail, page);
//submit a new ticket on behalf of the user
UservoiceRequests.newTicket = await ticket.create(RequestParameters.TicketSubject, RequestParameters.TicketMessage);

As you can see, all requests are async.

You don’t need explicitly authenticate a user, because the library is built to detect this automatically. If an authenticated user is required, the user will be redirected to the authentication page of UserVoice.

In the current version, you will need to manual send the request again after the user is authenticated, but I will update the library to make also this automatically soon.

  • EmojiDetector
  string text = "here would be the string from your textboxes that contain emojis";

            //check if Emojis are in text:
            if (EmojiDetector.HasEmojis(text) == true)
            {
                //your code here
            }
            //remove Emojis in text:
            text = EmojiDetector.RemoveEmojis(text);

The UserVoice API does not support Emojis, that’s why I wrote this little helper that you can easily use to remove them with only one line of code or to display a message to your users.

One last point: I don’t use RestSharp’s serializer – all request return the corresponding JSON string. This way, everyone of you can use the serializer of choice (I absolutely recommend JSON.net, though).

Please consider the current version as beta release, and report any issues with that to me via Twitter or mail.

And now enjoy my library & happy coding!

 

 

Posted by msicc in Dev Stories, windev, 9 comments

How to integrate tickets from uservoice into your Windows Phone app

This time, we will have look on how to integrate tickets from uservoice into your Windows Phone app.

Users love to see a history of their tickets they submitted to a customer service. They can review them again, and maybe help also other users by showing or telling them about it.

However, if you want to get a list of all tickets from a specific user, we need to slightly change our request. We need to login as owner of the account to be allowed to search all tickets.

Let’s have a look on how to log in as owner:

        private void LoginAsOwner()
        {
            string loginAsOwnerPath = "/api/v1/users/login_as_owner";
            var client = new RestClient(BaseUrl)
            {
                Authenticator = OAuth1Authenticator.ForRequestToken(ConsumerKey, ConsumerSecret, oAuthCallBackUri)
            };

            //works only with POST!
            var request = new RestRequest(loginAsOwnerPath, Method.POST);
            request.AddHeader("Accept", "application/json");

            var response = client.ExecuteAsync(request, HandleLoginAsOwnerResponse);
        }

        private void HandleLoginAsOwnerResponse(IRestResponse restResponse)
        {
            var response = restResponse;
            var ownerTokens = JsonConvert.DeserializeObject<UserViaMailClass.Token>(response.Content);

            OwnerAccesToken = ownerTokens.oauth_token;
            OwnerAccesTokenSecret = ownerTokens.oauth_token_secret;

        }

The authorization request is pretty similar to the user’s authentication request. However, if we log in as owner, we are getting another AccessToken and another AccessTokenSecret. That’s why we are using the ‘ForRequestToken’-method with our RestClient.

Important to know is that the request itself works only with the HTTP-method ‘POST’, otherwise the login would be denied.

We are getting back the JSON string of our owner account, which can be deserialized with JSON.net to get our AccessToken and AccessTokenSecret. I attached my ‘UserViaMailClass‘ for easy deserialization (yes, it looks similar to the user class from my authentication post, but has some differences in there).

Now that we have our OwnerAccesToken and OwnerAccessTokenSecret, we are able to search for all tickets from a specific user:

        public void GetAllTicketsFromUser()
        {
            string mailaddress = "<usersmailaddress>";
            string getSearchTicketsPath = "/api/v1/tickets/search.json";
            var client = new RestClient(BaseUrl)
            {
                Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, OwnerAccesToken, OwnerAccesTokenSecret)
            };

            var request = new RestRequest(getSearchTicketsPath, Method.GET);

            request.AddParameter("query", mailaddress);

            var response = client.ExecuteAsync(request, HandleGetAllTicketsFromUserResponse);
        }

        private void HandleGetAllTicketsFromUserResponse(IRestResponse restResponse)
        {
            var response = restResponse;

            var tickets = JsonConvert.DeserializeObject<TicketDataClass.TicketData>(response.Content);
        }

This request is again pretty similar to what we did to get a list of all suggestions. Please find attached my ‘TicketDataClass‘ for easy deserialization.

Of course, users want to be able to submit new tickets/support requests from our app, too. I will show you how to do that:

        public void CreateNewTicketAsUser()
        {
            string ticketsPath = "/api/v1/tickets.json";
            var client = new RestClient(BaseUrl)
            {
                Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret)
            };

            var request = new RestRequest(ticketsPath, Method.POST);
            request.AddParameter("ticket[subject]", "testing the uservoice API");
            request.AddParameter("ticket[message]", "hi there, \n\nwe are just testing the creation of a new uservoice ticket.");

            var response = client.ExecuteAsync(request, HandleCreateNewTicketAsUserResponse);
        }

        private void HandleCreateNewTicketAsUserResponse(IRestResponse restResponse)
        {
            var response = restResponse;
        }

To submit a new ticket, we are using the user’s AccessToken and AccessTokenSecret. This way, the ticket gets automatically assigned to the ticket. We then need to pass the ‘ticket[subject]’ and ‘ticket[message]’ parameters to the request to make it being accepted by the uservoice API.

The response is a json string that contains the ticket id, which can be used to fetch the submitted ticket data. The Alternative is to call again the search method we created before to get the updated list.

Answering to already existing tickets as user seems to be not possible with the current API. Normally, if a user responds to the response mail we answer their support ticket with, it will get assigned to the existing ticket. If we create a new ticket with the same subject, it will be a new ticket that creates a new thread. I already reached out to uservoice if there is a way to do the same from the API. As soon as I have a response that enables me to do so, I will update this post.

Now that we have all important functions for our new support system, I am starting to make a small helper library for your Windows Phone 8 apps. I hope to have it finished by this weekend, and will of course blog about here.

As always, I hope this blog post is helpful for some of you. Until the next post,

Happy coding everyone!

Posted by msicc in Dev Stories, windev, 4 comments

How to integrate the feedback forum of uservoice into your Windows Phone app

As I described in my first blog post about uservoice, uservoice has a feedback forum where users are able to submit and vote for ideas.

This post is about how to get those ideas into your app. On the API side, the ideas are called suggestions. Now that we know how to authenticate our users for the API, we are able to get a list of suggestions into our app:

        public void GetSuggestions()
        {

            string suggestionsPath = "api/v1/forums//suggestions.json";
            var client = new RestClient(BaseUrl)
            {
                Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret)
            };

            var request = new RestRequest(suggestionsPath, Method.GET);
            var response = client.ExecuteAsync(request, HandleGetSuggestionResponse);
        }

This is pretty straight forward. We are using the oAuth1Authenticator with our saved tokens as parameter to call the suggestions endpoint of the uservoice API. The result is a JSON string that holds all suggestions as a list.

In our response handler, we are able to deserialize our List of suggestions (best with JSON.net):

private void HandleGetSuggestionResponse(IRestResponse restResponse)
        {
            var response = restResponse;

            var suggestionslist = JsonConvert.DeserializeObject<SuggestionsDataClass.SuggestionsData>(response.Content);
            suggestionsListBox.ItemsSource = suggestionslist.suggestions;
        }

To get the SuggestionsDataClass items, just go to json2csharp.com and pass in the json string we received with response.Content or download it from here: SuggestionsDataClass.

Now let’s have a look on how to submit a new idea on behalf of our user. To submit an idea, we are using the POST method after we authenticated our user again with the uservoice API:

public void PostSuggestion()
        {
            string suggestionsPath = "api/v1/forums//suggestions.json";
            var client = new RestClient(BaseUrl)
            {
                Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret)
            };

            var request = new RestRequest(suggestionsPath, Method.POST);
            request.AddHeader("Accept", "application/json");
            request.Parameters.Clear();
            request.RequestFormat = DataFormat.Json;

            var newSuggestion = new SuggestionsDataClass.Suggestion();
            newSuggestion.title = "test suggestion number 2 from app development";
            newSuggestion.text = "please ignore this suggestion as we are testing integration of ideas into our apps";
            newSuggestion.vote_count = 3;

            request.AddParameter("suggestion[title]", newSuggestion.title);
            request.AddParameter("suggestion[text]", newSuggestion.text);
            request.AddParameter("suggestion[votes]", newSuggestion.vote_count);
            request.AddParameter("suggestion[referrer]", "uservoice test app");            

            var response = client.ExecuteAsync(request, HandlePostSuggestionResponse);

        }

This call is a bit different from the previous one. We need to pass the idea data to as parameters to our request. The parameter “suggestion[title]” is the main one and always required. As you can see, “suggestion[text]” and “suggestion[votes]” are additional parameters that make the idea complete. All other date is generated by the uservoice server (like posted at, connect to the user who posted that, etc..). The parameter “suggestion[referrer]” is only visible in our admin console and can help you to track from where the suggestion was submitted.

In our response handler, we are receiving a complete set of suggestion data as a JSON string that we can use display to our users and enable sharing of this idea for example:

        private void HandlePostSuggestionResponse(IRestResponse restResponse)
        {
            var response = restResponse;

            //tbd: do something with the result (e.g. checking response.StatusCode)
        }

 

Screenshot (305)

The last important point I want to show you is how to let a user vote for an idea.

        public void VoteOnSuggestion()
        {
            string postUserVotesPath = "/api/v1/forums//suggestions/{0}/votes.json";
            string suggestionId = "";
            var client = new RestClient(BaseUrl)
            {
                Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret)
            };

            var request = new RestRequest(string.Format(postUserVotesPath, suggestionId), Method.POST);
            request.AddHeader("Accept", "application/json");
            request.Parameters.Clear();
            request.RequestFormat = DataFormat.Json;

            request.AddParameter("to", 3);

            var response = client.ExecuteAsync(request, HandleVotingResponse);

        }

Of course we need to authenticate our user again. We need to pass the suggestion id to the request path (which is part of the Suggestion class). This call accepts only one parameter (“to”). The value can be between 1 and 3 (tip: only offer this three options from your code already to avoid erroneous responses).

The response handler returns the suggestion’s JSON string the user voted on again:

        private void HandleVotingResponse(IRestResponse restResponse)
        {
            var response = restResponse;

            //tbd: do something with the result (e.g. checking response.StatusCode)
        }

This was all about how to integrate the feedback forum of your uservoice account into your app. As always, I hope this will be useful for some of you.

If you want to explore which additional endpoints uservoice has, you are just a click away: https://developer.uservoice.com/docs/api/reference/

Happy coding, everyone!

Posted by msicc in Dev Stories, windev, 0 comments

How to authenticate users with the uservoice API on Windows Phone

I am really enjoying playing around with the uservoice API. It enables me to integrate a lot of cool new features that take the user experience to a new level, while helping me to improve my apps.

Today, I am going to write about how to authenticate a user with the uservoice API.

uservoice uses the oAuth authentication in version 1.0a. Therefore, we need a consumer key and a consumer secret for our app. Log into <yourname>.uservoice.com, and go to ‘Settings/Integration’, where you create a new API client for your app.

I recommend to set it up as a trusted client, as you will have more rights with a trusted client. After your set up your app, you should have an entry like this under integrations:

uservoice_api_client

Now we have everything set up on the uservoice part. Let’s go to our app. I am using the RestSharp library that makes it a little easier to authenticate users. You should do the same if you want to follow along this post. In Visual Studio, right click the project name and select ‘Manage NuGet packages’, and search for RestSharp in the ‘Online’ section.

After we have integrated the RestSharp library in our app, we are going to set up some objects for authentication:

 const string ConsumerKey = "<yourkey>";
 const string ConsumerSecret = "<yoursecret>";
 static string oAuthToken = "";
 static string oAuthTokenSecret = "";
 static string oAuthVerifier = "";
 static string AccessToken = "";
 static string AccessTokenSecret = "";
 const string BaseUrl = "http://<yourname>.uservoice.com";
 const string oAuthCallBackUri = "<yourcallbackUrl>";
 const string requestTokenPath = "oauth/request_token";
 const string authorizePath = "/oauth/authorize?";
 const string accessTokenPath = "oauth/access_token";

Setting up these static and constant string object are needed for our authentication flow.

Next, add a WebBrowser control in XAML. We need this to let the user authorize our app to communicate with the uservoice servers:

<Grid x:Name="authBrowserGrid" Visibility="Collapsed">
 <phone:WebBrowser x:Name="authBrowser" Height="800" Width="460"></phone:WebBrowser>
 </Grid>

You might have noticed that I set the Visibility property  to collapsed.  With oAuth, we are typically authenticating users by referring them to the web site, that handles the login process for us. After the user has authorized our app, the website transfers the user to our callback url that we define with our API client.

As I told you already above, RestSharp helps us a lot when it comes to authentication.

There are several ways to use the RestSharp API, but for the moment, I am using it directly to authenticate my users.

This is the start method:

public void GetUserAuthenticated()
 {

            authBrowserGrid.Visibility = Visibility.Visible;

            var client = new RestClient(BaseUrl)
 {
 Authenticator = OAuth1Authenticator.ForRequestToken(ConsumerKey, ConsumerSecret, oAuthCallBackUri)
 };

            var request = new RestRequest(requestTokenPath, Method.GET);
 var response = client.ExecuteAsync(request, HandleRequestTokenResponse);
 }

We are creating a new RestClient that calls our BaseUrl which we defined before. In the same call, we are telling our client that we want to use oAuth 1.0(a), passing the parameters ConsumerKey, ConsumerSecret and our callback url with the client. As we want to receive a RequestToken, we are generating a new RestRequest, passing the requestTokenPath and the ‘GET’ method as parameters.

Finally, we want to continue with the response, that should contain our RequestToken. Windows Phone  only supports the ExecuteAsync method, which needs a specific response handler to be executed. That was the first lesson I had to learn with using RestSharp on Windows Phone. To handle the response, we are creating a new handler method that implements ‘IRestResponse’: 

private void HandleRequestTokenResponse(IRestResponse restResponse)
 {
    var response = restResponse;

          //tbd: write an extraction helper for all tokens, verifier, secrets
 oAuthToken = response.Content.Substring((response.Content.IndexOf("oauth_token=") + 12), (response.Content.IndexOf("&oauth_token_secret=") - 12));
 var tempStringForGettingoAuthTokenSecret = response.Content.Substring(response.Content.IndexOf("&oauth_token_secret=") + 20);
 oAuthTokenSecret = tempStringForGettingoAuthTokenSecret.Substring(0,  tempStringForGettingoAuthTokenSecret.IndexOf("&oauth_callback_confirmed"));

var authorizeUrl = string.Format("{0}{1}{2}",BaseUrl, authorizePath, response.Content);

           authBrowser.Navigate(new Uri(authorizeUrl));
 authBrowser.IsScriptEnabled = true;
 authBrowser.Navigating += authBrowser_Navigating;

}

We now have an object that we are able to work with (the variable response). We need to extract the oauth_token and the oauth_token_secret and save them for later use.

After that, we can build our authorization url, that we pass to the WebBrowser control we created earlier. Important: you should set the ‘IsScriptEnabled’ property to true, as authentication websites often use scripts to verify the entered data. Last but not least, we are subscribing to the Navigating event of the browser control. In this event, we are handling the response to the authorization:

void authBrowser_Navigating(object sender, NavigatingEventArgs e)
 {
      if (e.Uri.AbsolutePath == "<yourAbsolutePath>")
      {
           oAuthVerifier = e.Uri.Query.Substring(e.Uri.Query.IndexOf("&oauth_verifier=") + 16);

           GetAccessToken();
      }
 }

We only need the verification string for out next request we have to send, so we are extracting the parameter “oauth_verifier” and going over to get our AccessToken for all further requests:

private void GetAccessToken()
 {
 var client = new RestClient(BaseUrl)
 {
 Authenticator = OAuth1Authenticator.ForAccessToken(ConsumerKey, ConsumerSecret, oAuthToken, oAuthTokenSecret, oAuthVerifier)
 };

            var request = new RestRequest(accessTokenPath, Method.GET);
 var response = client.ExecuteAsync(request, HandleAccessTokenResponse);

        }

This time, we are telling our RestClient that we want to get the AccessToken, and we need also the oAuthToken and the oAuthTokenSecret we saved before we navigated our users to the uservoice authentication site. Of course, we also need a handler for the response to this request:

private void HandleAccessTokenResponse(IRestResponse restResponse)
 {
 var response = restResponse;

            AccessToken = response.Content.Substring(12, response.Content.IndexOf("&oauth_token_secret=") -12);
 AccessTokenSecret = response.Content.Substring(response.Content.IndexOf("&oauth_token_secret=") + 20);

            authBrowserGrid.Visibility = Visibility.Collapsed;

            //continue with your code (new method call etc.)
 }

The only thing we now need to do is to extract the AccessToken and the AccessTokenSecret and save them permanently in our app (as long as the user is authenticated). We need them for a lot of calls to the uservoice API.

Let’s call the uservoice API to get more information about the user that has now authorized our app:

public void GetUser()
 {
 var client = new RestClient(BaseUrl)
 {
 Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret)
 };

           var request = new RestRequest(getUserPath, Method.GET);
 var response = client.ExecuteAsync(request, HandleGetUserResponse);
 }

As you can see, we are now only using the AccessToken and the AccessTokenSecret to call the uservoice API, no additional login is required. To complete this sample, here is again the handler for the response:

        private void HandleGetUserResponse(IRestResponse restResponse)
       {
              var response = restResponse;

            //tbd: do something with the result (e.g. checking response.StatusCode)

       }

We have now received a JSON string that tells us a lot of information about our user and the date we have on uservoice:

Screenshot (304)

 

As you can see, it is relatively easy to get our users authenticated and calling the uservoice API. In my next blog post, I will write about suggestions (that’s the idea forum). I will go into details on getting a list of all suggestions, how to let a user post a new idea and letting a user vote for ideas – all from within  your app!

In the meantime, I hope this post is helpful for some of you.

Happy coding, everyone!

Posted by msicc in Dev Stories, windev, 2 comments

How to fetch articles from your uservoice knowledgebase into a Windows Phone app

As some of you might know, I recently switched to uservoice.com for feedback, support and also FAQ hosting (read more here). Of course I want to integrate all those features into my app(s) to make the user experience as native as possible.

First, you need to generate a new app in uservoice.com. Log into your account, click on ‘Admin Console’, ‘Settings’ and finally ‘Integrations’. Then add your API client, you will have something like this:

uservoice_api_client

Today, we are starting with getting our knowledge base articles into our app.

This is the easiest part besides assigning the support mail address to a button.

Here is how we are doing it:

First, we need to declare some constants:

//consumer key and secret are needed to authorize our requests (oAuth)
 //KB articles only need the ConsumerKey
 const string ConsumerKey = "<youKey>";
 const string ConsumerSecret = "<yourSecret>";
 //all KB articles:
 const string KnowledgebaseString = "http://<yoursubdomain>.uservoice.com/api/v1/articles.json?client={0}";
 //KB topic articles:
 //using sort=oldest ensures that you will get the right order of your articles
 const string KnowledgebaseTopicString = "http://<yousubdomain>.uservoice.com/api/v1/topics/{0}/articles.json?client={1}&sort=oldest";

static string articleJsonString;

As you can see, we have two options to fetch our knowledgebase articles – all articles (if you have only one app, you’re fine with that) or topic based.

To get the needed topic id, just open the topic in your browser. The topic id is part of the url:

uservoice_topic_id

For getting authorized to receive the JSON string of our knowledge base, we need to pass the consumer key as parameter “client” to the base url of our request. To get our list sorted, I am using the sort parameter as well.

To receive the JSON string, we are creating an async Task<string> that fetches our article. To make the result reloadable, add the IfModifiedSince Header (otherwise Windows Phone caches the result during the app’s current lifecycle).

//receiving the JSON string for our KB does not need any advanced requests: 
//a basic HttpClient handles everything for us, as we don't need any authentication here
public async Task<string> GetKBJsonString()
{
      string getKBJSonStringFromUserVoice = string.Empty;

     HttpClient getKBJsonClient = new HttpClient();
     getKBJsonClient.DefaultRequestHeaders.IfModifiedSince = DateTime.Now;

     getKBJSonStringFromUserVoice = await getKBJsonClient.GetStringAsync(new Uri(string.Format(KnowledgebaseTopicString, "47463", ConsumerKey), UriKind.RelativeOrAbsolute));

      return getKBJSonStringFromUserVoice;
 }

Of course we want to have a list shown to our users – to display our JSON string in a ListBox, we need to deserialize it. To be able to deserialize it, we need a data class. You can use json2sharp.com to generate the base class or use this one (download Link). It fits for both all articles or topic based articles.

First, create a ListBox with the corresponding DataTemplate (I am only using question and answer text for this demo).

            <ListBox x:Name="FAQListBox">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"></RowDefinition>
                                <RowDefinition Height="Auto"></RowDefinition>
                            </Grid.RowDefinitions>
                            <TextBlock Grid.Row="0" x:Name="questionTB" Style="{StaticResource PhoneTextTitle2Style}" Text="{Binding question}" TextWrapping="Wrap"></TextBlock>
                            <TextBlock Grid.Row="1" x:Name="answerTB" Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding text}" TextWrapping="Wrap"></TextBlock>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

I am using JSON.net for everything around JSON strings. Here is how to deserialize the JSON string and set the ItemsSource of our Listbox:

            articleJsonString = await GetKBJsonString();

            var articlesList = JsonConvert.DeserializeObject<KBArticleDataClass.KBArticleData>(articleJsonString);

            FAQListBox.ItemsSource = articlesList.articles;

You are now already able to run the project. Here is the result of my test app, displaying the FAQ of my NFC Toolkit app:

uservoice_listbox_testapp_screenshot

As you can see, it takes only about ten minutes to get the knowledge base into your app. Using a remote source has a lot of advantages, the most important one is you don’t need to update your app when you add new answered questions.

I am now starting to work on integrating the feedback forum. It requires an oAuth authentication, and will be a bit more complicated than this one. Of course I will share it with you all here on my blog – stay tuned.

Until then – happy coding!

Posted by msicc in Dev Stories, windev, 3 comments