Source Code
The source code is now on GitHub.
Introduction
I've been exploring the LinkedIn.NET library (Nuget here, Source Forge here) because, quite frankly, I find most web-based applications klunky and LinkedIn's user group forum web design is no exception. What I present in this article leverages LinkedIn.NET to provide basic viewing of groups, posts within groups, comments within posts, and to add your own comments. As primitive as it is, I find it much more usable than the web-based UI!
The UI
The UI consists of three panels:
- A tree which shows the hierarchy of groups, posts, and comments
- Information about the group, post, or comment.
- An area for you to enter a comment to a post.
Code Walk-Through
The most interesting part of the code is the use of Task.Run and async/await. Rather than using LinkedIn.NET's asynchronous calls, which require implementing a callback routine, I opted to use the synchronous version and wrap them in Task.Run calls. This makes updating the UI a lot simpler!
Initialization
The application expects a linkedin.config file, with the following format:
- line 1: your consumer key
- line 2: our consumer secret
These are obtained from LinkedIn's developer site - in the Documentation menu, select "Getting Started." The first two lines of the config file must be set with your "API Key" and "Secret Key".
Authentication
Your authorization web page will display YOUR name, not mine.
The access token, once obtained, is stored in the third line (I know, not very secure!) The access token is obtain only through OAuth redirection, which makes the process rather klunky. The LinkedIn.NET demo code has an interesting solution -- create a web browser with the OAuth URL and a redirect URI, then intercept the redirect in the browser control to obtain the access token.
- The redirect URL used in the application must match the redirect URL you specified when registering for the API key.
- Similarly, the "State" value must match what you specified when registering for the API key as well.
protected void Authenticate()
{
if (linkedInClient != null)
{
var options = new LinkedInAuthorizationOptions
{
RedirectUrl = REDIRECT_URL,
Permissions = LinkedInPermissions.Connections | LinkedInPermissions.ContactsInfo |
LinkedInPermissions.EmailAddress | LinkedInPermissions.FullProfile |
LinkedInPermissions.GroupDiscussions | LinkedInPermissions.Messages |
LinkedInPermissions.Updates,
State = STATE
};
var dlgAuth = new DlgAuthorization(linkedInClient.GetAuthorizationUrl(options));
if (dlgAuth.ShowDialog(this) == DialogResult.OK)
{
var response = linkedInClient.GetAccessToken(dlgAuth.AuthorizationCode, REDIRECT_URL);
if (response.Result != null && response.Status == LinkedInResponseStatus.OK)
{
accessToken = response.Result.AccessToken;
SaveConfiguration();
}
}
else
{
MessageBox.Show(dlgAuth.OauthErrorDescription, dlgAuth.OauthError);
}
}
}
Once authenticated, the config file is saved with the access token.
Loading Groups
Using the LinkedIn.NET API is very simple. First we load groups to which the user (you) belong to:
protected async void LoadGroups()
{
if (haveAccessToken)
{
tvGroups.Nodes.Clear();
tvGroups.Nodes.Add("Loading...");
LinkedInGetGroupOptions options = new LinkedInGetGroupOptions();
options.GroupOptions.SelectAll();
LinkedInResponse<IEnumerable<LinkedInGroup>> result = await Task.Run(() => linkedInClient.GetMemberGroups(options));
if (result.Result != null && result.Status == LinkedInResponseStatus.OK)
{
ShowMemberGroups(result);
}
else
{
ReRun(result.Status, result.Message);
}
}
}
And add them to the tree:
protected void ShowMemberGroups(LinkedInResponse<IEnumerable<LinkedInGroup>> result)
{
tvGroups.Nodes.Clear();
foreach (LinkedInGroup group in result.Result)
{
TreeNode node = tvGroups.Nodes.Add(group.Name);
node.Tag = group;
}
}
Loading Group Posts
When the user double-clicks on a group, the posts are loaded for that group:
protected async void LoadPostsForGroup(TreeNode node, LinkedInGroup group)
{
LinkedInGetGroupPostsOptions options = new LinkedInGetGroupPostsOptions();
options.PostOptions.SelectAll();
options.GroupId = group.Id;
ShowLoading(node);
await Task.Run(() => group.LoadPosts(options));
ShowGroupPosts(node, group);
node.ExpandAll();
}
protected void ShowGroupPosts(TreeNode node, LinkedInGroup group)
{
node.Nodes.Clear();
foreach (LinkedInGroupPost post in group.Posts)
{
TreeNode childNode = node.Nodes.Add(post.Title);
childNode.Tag = post;
}
}
Loading Post Comments
Finally, when the user double-clicks on a post, the comments are loaded:
protected async void LoadCommentsForPost(TreeNode node, LinkedInGroupPost post)
{
LinkedInGetGroupPostCommentsOptions options = new LinkedInGetGroupPostCommentsOptions();
options.CommentOptions.SelectAll();
options.PostId = post.Id;
ShowLoading(node);
await Task.Run(() => post.LoadComments(options));
ShowGroupPostComments(node, post);
node.ExpandAll();
}
protected void ShowGroupPostComments(TreeNode node, LinkedInGroupPost post)
{
node.Nodes.Clear();
foreach (LinkedInGroupComment comment in post.Comments)
{
TreeNode childNode = node.Nodes.Add(comment.Text.LimitLength(64));
childNode.Tag = comment;
}
}
Adding a Comment
Comments are added to posts, so once a post has been selected, you are able to add a comment to that post. Here, in addition to posting the comment, we manually add the comment to tree:
protected void AddComment(LinkedInGroupPost post, string comment)
{
post.Comment(comment);
tbComment.Text = "(posted)";
TreeNode childNode = selectedPostNode.Nodes.Add(comment.LimitLength(64));
tvGroups.SelectedNode = childNode;
tbInfo.Text = comment;
}
LinkedIn can be a bit slow in updating the post with your comment, so if you double-click on the post to refresh the comments but don't see your comment, be patient.
Also, be careful that you are posting to the correct group -- the currently selected post or the currently selected comment in a post determines the post.
Conclusion
There's a LOT of bells and whistles that could be added to this demo, but then it wouldn't be a "bare-bones" demo!