Creating An IScriptedContentFragmentExtension For Use In Telligent Enterprise 3.0 Widgets

As a Telligent Partner we have been working with the Release Candidate for Telligent Enterprise 3.0 for a while now and have been getting to grips with the new Widget Architecture.

In this blog post I am going to take you through how to create an IScriptedContentFragmentExtension and use it in a custom widget. Please note this will also work with Telligent Community 6.0 too.

This is a long (but hopefully helpful) post so brace yourself :-)...

What's an IScriptedContentFragmentExtension?

In simple terms; an IScriptedContentFragmentExtension is a way of exposing your own methods within the new Telligent Enterprise 3.0 widget templates. You can use them to do things like return custom data sets and objects, perform custom formatting or call your own API or service.

Sounds Good, Tell me more...

In this example we are going to demonstrate this technique by creating a custom widget that lists recent Twitter posts for users in a Telligent Enterprise site. The widget will also contain a number of configuration options to allow the administrator to choose how many tweets to show for each user and the number of tweets to show per page.

Here's a sneak preview of what it will look like:

Step 1 - Creating a New Custom Widget

First of all we need to create a new widget, the widget will be used to access our IScriptedContentFragmentExtension, which will in turn get the tweets back from Twitter.

This is much easier to do now than it was in previous versions of Telligent Enterprise. In Telligent Enterprise 2.0 > 2.6 you had to create a C# class and compile it into a dll in order to create a custom widget, In Telligent Enterprise 3.0 widgets can just be created in the control panel.

Lets Create a Widget...

  1. Go to the control panel and navigate to: Dashboard » Administration » Site Administration » Manage Widgets
  2. Click the "Create New Widget" link on the right-hand-side of the page.
  3. Enter a Title (I entered Recent Tweets) and give the widget a description also (I entered 'A Widget to list Recent Staff Tweets')
  4. Save the widget, it should look something like this:

Step 2 - Creating the Extension Class

Ok so we have out widget but it has nothing in it right now. Lets create our IScriptedContentFragmentExtension to get the tweets and return them to our widget.

To do this we need to use Visual Studio and create a C# class. So fire up Visual Studio 2010 and create a new C# class library project called something like "Enterprise3.Extensions" (ASP.NET 4.0). This is where we will create our IScriptedContentFragmentExtension.

I am not going to go into the specifics of how you set up a development project for Telligent Enterprise as it is documented here. The instructions here are slightly out-of-date (at time of writing) so you need to ensure you use Visual Studio 2010 and .NET 4 (Not VS 2008 and .NET 3.5).

Lets Create the IScriptedContentFragmentExtension...

  1. Create a Folder called "Models" in your new Extensions class library
  2. Create a C# class file called "TwitterSCFExtension.cs"
  3. Add References to the following DLL's in you class library:
    Telligent.Evolution.ScriptedContentFragments.dll
    Telligent.Evolution.Components.dll
  4. Add the following using statements to the top of your class file:
    using Telligent.Evolution.ScriptedContentFragments;
    using Telligent.Evolution.Extensibility.UI.Version1;
  5. Add the following code into the class file:

[csharp]
///

/// Extension class to be used in widgets to access Twitter Service
/// Author : Adam Seabridge
/// Date: 19/07/2011
///

 

public class TwitterSCFExtension : Telligent.Evolution.Extensibility.UI.Version1.IScriptedContentFragmentExtension
{

public string Name
{
get { return "Twitter SCF Extension Plugin"; }
}

public string ExtensionName
{
get { return "TwitterSCFExtension"; }
}

public string Description
{
get{ return "Twitter Extension Class For Use In Custom Widgets"; }
}

public void Initialize()
{
//stuff here;
}

public object Extension {
//pass back an instance of the TwitterService which exposes the methods
get { return new TwitterService(); }
}
[/csharp]

What does this all mean?

You have just created your own extension which has properties to define the name and description of the extension, these will show in the Telligent Enterprise control panel when we enable the extension later. The object Extension() method exposes a custom class (which we will create shortly) called "TwitterService".

Step 3 - Creating An Authentication Token & Twitteriser

Before we can create our Twitter Service Class we need to do a couple of things.

Register a Twitter Application

Firstly we need to go to Twitter and register a Twitter Application so that you can access the Twitter API and get users Tweets, you can do this here: https://dev.twitter.com/apps/new.
I am not going to cover in detail how you do this as it is explained here well: http://www.rnel.net/twitter-tutorials/less-than-a-minute-twitter-application-registration.

Once you have registered you application you need to click the "Create Access Token" button to create your authentication token, the Twitter Service class we will create next will use OAuth to authenticate with Twitter using the consumer key and consumer secret.

Ok I have my authentication tokens, what do I do with them?

  1. Open up the web.config in your Community Website and add the following application keys, replacing the X's with your token values:

[xml]




[/xml]

Twitterizer

You could write code from scratch to authenticate with Twitter and get tweets for users. However why re-invent the wheel when others have already made life easier for you?.

This is where Twitterizer comes in, Twitterizer is an open-source .NET library for Twitter, it makes authentication with Twitter Via Open Auth very easy. You can download it here: http://www.twitterizer.net/downloads/
You will only need the Twitterizer2.dll, to add this to your Class library extensions project:

  1. Copy and paste the dll into the References folder in your project in Windows Explorer (like you did with the Telligent dlls)
  2. Add a Reference in Visual Studio to Twitterizer2 in your extensions project by browsing to where you place the dll.
  3. Unfortunately Twitterizer uses the .NET 3.5 version of the Newtonsoft.Json.dll and this will cause and error with the .NET 4.0 version of the Newtonsoft.Json.dll that Telligent Enterprise uses. To resolve this open up the web.config in your Community Website and add the following configuration block just before the closing element:

[xml]









[/xml]

Creating a field for user to store their Twitter Username

In order for us to be able to grab a users Tweets we need to allow them to enter and store their Twitter username into Telligent Enterprise.

This is pretty easy to do:

  1. Navigate to: Dashboard » Administration » Membership Administration »
  2. Create a new text field called "TwitterUsername"
  3. Add this field to one of the field groups that you are already displaying on your user profile page or create a new group and add it to your user profile page (more information on this can be found here)

Step 4 - Creating the Twitter Service Class

The next step is to create the Twitter Service class itself, this is the key class which will get Tweets from Twitter for each user in Telligent Enterprise and return them in a nice list for us to display in our widget.

Lets create the Twitter Service...

  1. Create a new C# class file called "Tweets.cs" in the "Models" folder in your Extensions class library
  2. Add the following code into the class file:

    [csharp]
    ///

    /// Object to store tweets and keep tack of paging
    /// Author : Adam Seabridge
    /// Date: 19/07/2011
    ///

     

    public class Tweets : List
    {
    public int TotalItems { get; set; }
    public int PageIndex { get; set; }
    public int PageSize { get; set; }
    }
    [/csharp]

  3. Create a new C# class file called "TwitterService.cs" in the "Models" folder in your Extensions class library
  4. Include the following using statements at the top of your class:
    using Telligent.Evolution.Components;
    using Telligent.Evolution;
    using Twitterizer;
  5. Add the following code into the class file:

[csharp]
///

/// Class to handle all interaction with Twitter
/// Author : Adam Seabridge
/// Date: 19/07/2011
///

 

public class TwitterService
{
///

/// Method to get all users in Telligent with a Twitter username
/// and get their recent tweets
///

 

///
public Tweets GetRecentUserTweets(int pageIndex, int pageSize, int tweetsPerUser)
{
string cacheKey = "RecentUserTweets";

//try and get tweets from cache
Tweets tweets = CSCache.Get(cacheKey) as Tweets;

int totalItems = 0;

if (tweets == null)
{

lock (CSCache.GetCacheEntryLock(cacheKey))
{
//check the cache for tweets
tweets = CSCache.Get(cacheKey) as Tweets;

if (tweets == null)
{
// if no tweets exist then get the users from Telligent so we can get their tweets
tweets = new Tweets();

UserQuery userQuery = new UserQuery();
userQuery.Status = UserAccountStatus.Approved;
userQuery.PageSize = 99;
userQuery.SortBy = SortUsersBy.RecentPosts;

UserSet userSet = new UserSet();
userSet = Users.GetUsers(userQuery);

foreach (User user in userSet.Users)
{
//get user profile data
IUserProfileService profileService = Telligent.Common.Services.Get();
UserProfileDataBase profileData = profileService.GetProfileData(user);

//get the users twitter name
string twitterUsername = profileData.GetStringValue("TwitterUserName", "");

if (!string.IsNullOrEmpty(twitterUsername))
{
//get this users top x tweets
tweets = GetTweetsForUserOpenAuth(twitterUsername, tweets, tweetsPerUser);
}
}

//add the tweets into the cache
if (tweets.Count > 0)
{
CSCache.Insert(cacheKey, tweets, (int)(2 * CSCache.MinuteFactor));
}
}

}
}

try
{
//order the tweets by date descending
Tweets tweetsOrdered = new Tweets();
tweets.OrderByDescending(x => x.CreatedDate).ToList().ForEach(t => tweetsOrdered.Add(t));

totalItems = tweets.Count;

//page tweets to correct page
Tweets tweetsPaged = new Tweets();
tweetsOrdered.Page(pageIndex, pageSize).ToList().ForEach(t => tweetsPaged.Add(t));

//keep track of total items, page size and page index
tweetsPaged.TotalItems = totalItems;
tweetsPaged.PageSize = pageSize;
tweetsPaged.PageIndex = pageIndex;

tweets = tweetsPaged;
}
catch (Exception ex)
{
//TODO: log error
}

return tweets;
}

///

/// Method to Get Tweets for a user using Open Auth
///

 

/////////
public Tweets GetTweetsForUserOpenAuth(string userName, Tweets tweets, int tweetsPerUser)
{
Tweets newTweets = tweets;

TwitterResponse twitterResponse = new TwitterResponse();

try
{
//get auth token config items
OAuthTokens requestToken = new OAuthTokens();
requestToken.ConsumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
requestToken.ConsumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];
requestToken.AccessToken = ConfigurationManager.AppSettings["AccessToken"];
requestToken.AccessTokenSecret = ConfigurationManager.AppSettings["AccessTokenSecret"];

//create request to get users top x tweets
twitterResponse = TwitterTimeline.UserTimeline(requestToken,
new UserTimelineOptions() { ScreenName = userName, Count = tweetsPerUser });

if (twitterResponse.ResponseObject != null)
{
newTweets.AddRange(twitterResponse.ResponseObject.ToList());
}
}
catch(Exception ex){
//TODO: log error
}

return newTweets;
}

///

/// Method which uses a RegEx to make links in text clickable
///

 

//////
public string ProcessLinks(string txt)
{
Regex regx = new Regex("http://([\w+?\.\w+])+([a-zA-Z0-9\~\!\@\#\$\%\^\&\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?", RegexOptions.IgnoreCase);

MatchCollection mactches = regx.Matches(txt);

foreach (Match match in mactches)
{
txt = txt.Replace(match.Value, "" + match.Value + "");
}

return txt;
}

[/csharp]

Ok I have done that, what does this all mean?

You have just created a Tweet Class which stores the tweets returned from Twitter and allows paging to be performed and a Twitter Service class which has 3 key methods in it, lets look at each of these in more detail:

GetRecentUserTweets()
This method might look complicated but it is actually fairly simple. It gets each user in Telligent Enterprise and then looks to see if they have filled in the custom Twitter Username field we created earlier. If the user has a twitter name then the method will call: GetTweetsForUserOpenAuth() to get the users Tweets. The final list of tweets for all users is ordered, sorted by date descending and paging is created to allow only a small number of records to be shown on each page (more on this later when we create our widget template).

Lastly this method also puts the final list of tweets into the Telligent cache to reduce the number of calls made to the Twitter API (350 requests per hour is the limit for authenticated requests).

GetTweetsForUserOpenAuth()
This method gets the Tweets for a specific user using the access tokens we created earlier and Twitterizer like so:

[csharp]
twitterResponse = TwitterTimeline.UserTimeline(requestToken,
new UserTimelineOptions() { ScreenName = userName, Count = tweetsPerUser });
[/csharp]

You can see from the above code that the request token from the web.config keys, users twitter username and the number of items to bring back are passed into the Twitterizer UserTimeline() method.

If tweets are returned then they are added to the list and passed back to the GetRecentUserTweets() method.

ProcessLinks()
The last method in the TwitterService class gets the text for the tweet and finds any links within the text. If it finds a link it wraps it in an anchor tag to make it click-able, e.g: www.google.com.

Step 5 - Adding Paging

You may have noticed the lines of code in the GetRecentUserTweets() method which page the tweets to the correct page:

[csharp]
//page tweets to correct page
Tweets tweetsPaged = new Tweets();
tweetsOrdered.Page(pageIndex, pageSize).ToList().ForEach(t => tweetsPaged.Add(t));
[/csharp]

This uses an C# 3.0 extension method to add on this functionality to the Listobject, we need to add this to our Class library project else so that this code will work.

Lets create the C# 3.0 extension method...

  1. Create a new C# class file called "Core.cs" in the "Models" folder in your Extensions class library
  2. Add the following code into the class file:

[csharp]
///

/// Class to provide generic methods for use in widgets
/// Author : Adam Seabridge
/// Date: 21/07/2011
///

 

public static class Core
{
///

/// method to providing paging for tweets
///

 

///
////////////
public static IEnumerable Page(this IEnumerable source, int page, int pageSize)
{
return source.Skip((page) * pageSize).Take(pageSize);
}
}
[/csharp]

Step 6 - Creating Our Widget Content & Configuration

Great were nearly there so lets re-cap on what we have done before we start using our new Extension Method in our widgets, we have:

  • Created a custom widget that will show our Tweets (no content yet)
  • Created an IScriptedContentFragmentExtension to expose our Twitter Service class
  • Created an Twitter Service Class which will go and get our Tweets using Twitterizer
  • Created some extra classes to store tweets and handle paging

Lets Add Some Content to our Widget...

  1. Go back to the twitter widget you created at Step 1, here: Dashboard » Administration » Site Administration » Manage Widgets
  2. Under the Widget Tab add the Following template code:

[csharp]
#set($pageSize = $core_v2_widget.GetIntValue('pageSize', 10))
#set ($pageIndex = $core_v2_ui.GetCurrentPageIndex("%{QueryStringProperty = 'tpid'}"))
#set($tweetsPerUser = $core_v2_widget.GetIntValue('tweetsPerUser', 2))
#set($showPager = $core_v2_widget.GetBoolValue('showPager', true))

#set ($tweets = $TwitterSCFExtension.GetRecentUserTweets($pageIndex, $pageSize, $tweetsPerUser))
#foreach($tweet in $tweets)



$tweet.User.ScreenName



$tweet.User.Name
$TwitterSCFExtension.ProcessLinks($tweet.Text)
$tweet.CreatedDate
$core_v2_language.GetResource("TweetList_From") $tweet.Source

#nodata

$core_v2_language.GetResource("TweetList_NoResults")

#end
#if($tweets && $tweets.TotalItems > 0 && $showPager)
$core_v2_ui.Pager($tweets.PageIndex, $tweets.PageSize, $tweets.TotalItems, "%{QueryStringProperty = 'tpid'}")
#end
[/csharp]

  • Save the Widget

 

OK, I've done that, can you explain this further please?

You have just added an new template to your widget which uses NVelocity to perform logic. The template gets the pageSize, pageIndex and how many tweets to show from the widget configuration.

The most important part of the template though is where it gets the Tweets back from our custom Extension method:

[csharp]
#set ($tweets = $TwitterSCFExtension.GetRecentUserTweets($pageIndex, $pageSize, $tweetsPerUser))
#foreach($tweet in $tweets)
[/csharp]

This code gets the tweets and passes in the current page and pagesize. It then loops round each tweet returned outputting the details of the tweet within a HTML template.

Properties of the tweet are handled by using syntax like so, this gets the users username from the tweet object:

[csharp]
$tweet.User.Name
[/csharp]

You will notice that the template also references resource entries like so:

[csharp]
$core_v2_language.GetResource("TweetList_From")
[/csharp]

We need to add these under the widget in resources tab so they are available to our widget.

Lets Add Some Resources to our Widget...

  1. Go to the Resources Tab and add the following resources:
  2. Save the Widget

The final task to carry out here is to add some Configuration Options to the widget to define the widget title, page size, number of tweets to show per user and if the pager should be shown or not.

Lets add some configuration options to our widget...

  1. Go to the Configuration Tab and add the following code to the "Header Script" text box:[csharp]
    $core_v2_widget.ApplyTokens($core_v2_widget.GetStringValue('fragmentHeader', '${resource:TweetList_Header}'))
    [/csharp]
  2. Add the following code to the "Configuration Content" text box:
    [xml] [/xml]
  3. Save the widget

 

Step 7 - Making It Look Pretty (adding some CSS)

In order to style the html elements we created in our widget template we are going to add some CSS. You can do this in a number of ways:

  1. In a custom style sheet included in the theme.config
  2. Attached as a custom css file under the attachments tab on the widget
  3. In the the control panel under the "custom styles" tab here: Dashboard » Administration » Site Administration » Site Theme.

Lets add some CSS...

  1. Copy and Paste the following styles into your style sheet or the custom styles tab in the Control Panel:
    [css]
    .tweet {
    font-size: 15px;
    padding: 10px 20px;
    position: relative;
    border-top: 1px dotted #cccccc;
    }.tweet-image {
    float: left;
    height: 48px;
    margin-top: 3px;
    overflow: hidden;
    width: 48px;
    }

    .tweet-content {
    margin-left: 58px;
    min-height: 48px;
    }

    .tweet-row {
    display: block;
    line-height: 15px;
    position: relative;
    }

    .tweet-username {
    color: #333333 !important;
    font-weight: bold;
    margin: 0 5px 0 0;
    }

    .tweet-text {
    font-family: Arial,"Helvetica Neue",sans-serif;
    line-height: 19px;
    padding: 0;
    word-wrap: break-word;
    }

    .tweet-timestamp {
    color: #999999 !important;
    font-size: 11px;
    margin: 0 5px 0 0;
    }

    .tweet-fullname {
    color: #999999;
    font-size: 12px;
    }

    .tweet-source{
    color: #999999 !important;
    font-size: 11px;
    }
    [/css]

  2. Save the styles

Step 8 - Deploying Our Custom Widget To Telligent Enterprise

The .dll file which contains the Extension method needs to be deployed to the /bin folder in your Telligent Enterprise Community Site so that it gets picked up by the Telligent Enterprise Platform.

Deploying the Enterprise3.Extensions.dll

  1. Publish the class project from Visual Studio in Release mode.
  2. Copy the .dll created into the /bin folder in the root of your Telligent Enterprise Community Site
  3. Also copy the Twitterizer2.dll into the /bin folder in the root of your Telligent Enterprise Community Site
    Before we will be able to use our new IScriptedContentFragmentExtension we need to enable it in the plugins section of the telligent Enterprise Control Panel.

Enabling our IScriptedContentFragmentExtension for use

  1. Navigate to: Dashboard » Administration » Site Administration » Manage Plugins
  2. Locate the Twitter Extension Plugin and tick to enable it
  3. Save the page

You are finally ready to add the widget to your Telligent Site. We are going to add it to the homepage of our Telligent site just like any other widget.

Add the widget to the page

  1. Go to the homepage and enter edit mode
  2. Search for the widget by typing "Recent Tweets".
  3. Drag and drop it onto the page
  4. Click the pen Icon to edit the configuration and change the title and paging option if need be.
  5. Save the page

That's it, you should now have a lovely Twitter Widget displaying tweets on the page.

I hope you found this useful.

Adam

Any questions?

If you need more information or have any questions just get in touch and we'd be happy to answer them for you.