Creating Mashup Applications in SharePoint, Part 2
Integrating video player and ratings functionality
June 24, 2011
This article is the final article in a two-part series about creating mashup applications in SharePoint 2010. In “Creating Mashup Applications In SharePoint – Part 1 of 2”, I described how to create mashup applications which use external data from multiple systems and provide mapping capabilities. In this article, I’ll demonstrate how to include video and rating functionality.
The mashup application in this series of articles (Figure 1) allows users to browse through customer records and associated orders, map a customer’s location, and display and rate videos submitted by customers.
Figure 1: Sample mashup application in SharePoint
Keep in mind; I defined the following goals for how this mashup application will operate. The goals include no page refreshes, sandbox compatibly, and a flexible component-oriented design that allows for easy modification and multiple configuration options. Just like I did in the last article, I’ll point out how each of these goals is achieved.
In this article, I'll enhance the mashup application and demonstrate how to play videos stored in a SharePoint Assets library via the out of the box SharePoint Silverlight Media Player, how to use the SharePoint ratings features to rate the videos, and how to use Silverlight to web page communication to ensure no page refreshes occur.
Integrating Videos
The videos displayed in the mashup application are stored in a SharePoint Assets Library. The Assets Library is a new type of list in SharePoint 2010 that supports storing videos, images, and audio files. When an Assets Library is created it has three content types assigned to it by default; Video, Image, and Audio, which Figure 2 shows. These content types contain the columns which define these asset types.
Figure 2: The default content types assigned to an Assets Library.
To correlate customer videos to customer records, a lookup column is added to the CustomerID column in the Customers external list. See my article, “Creating Mashup Applications In SharePoint – Part 1 of 2” to learn more about the Customer external list. When a lookup column references an external list, the edit form for the parent item, which Figure 3 shows, displays the external data picker button.
Figure 3: Editing a video list item in the Assets Library. Notice the CustomerID lookup column to the Customers external list.
Clicking the external data picker button opens the external data picker dialog (see Figure 4). This dialog provides the ability to browse the external data and select the appropriate record. This functionality is especially helpful because it allows you to see the entire record,not just the customer ID. In this scenario, this makes it easy to correlate a video with a customer because the entire customer record is visible.
Figure 4: Selecting a record in the external data item picker dialog.
Once the videos in the Assets library are tied to customer records with the CustomerID external data lookup column, they are ready for integration in the mashup application. Figure 5 describes the data flow in the mashup application related to customer videos. First, a customer is selected in the Silverlight Customer Orders Inspector application. Then, when the View Customer Videos button is clicked the selected customer’s CustomerID is passed to the Customer Videos Web Part. The Customer Videos Web Part uses the CustomerID to execute a query to retrieve the associated customer videos and related metadata from the Assets Library where the videos are stored. Finally, the Customer Videos Web Part formats the query results and displays the videos and associated metadata.
Figure 5: Data flow in the mashup application related to customer videos
Silverlight to Web Page Communication
As I mentioned earlier, one of the goals for the mashup application is to make sure no page refreshes occur. To meet this goal the Silverlight Customer Order Inspector application passes the CustomerID for the currently selected customer directly to the Customer Videos Web Part. The Customer Videos Web Part uses the SharePoint ECMA Client Side Object Model and JQuery to execute an asynchronous query and update the web page. This eliminates the need to use QueryString variables or other methods that require server-side processing and page refreshes. Using the SharePoint server side object model, or the managed version of the SharePoint Client Object Model would both require page refreshes because they execute on the server.
To pass data between the Silverlight application and the web page, code must be written in both the sending Silverlight application and the receiving web page. The showCustomerVideosButton_Click method in the Customer Orders Inspector Silverlight application (Listing 1) fires when the View Customer Videos button is clicked. First, the method checks to see if a customer is selected in the application. If a customer is selected, the HtmlPage.Window.Invoke method calls the GetVideos JavaScript function and passes in the currently selected customer’s CustomerID. The classes which support this type of communication exist in the System.Windows.Browser namespace.
Displaying Query Results
Listing 2 shows the GetVideos JavaScript function called by the Customer Order Inspector Silverlight application. This function uses the SharePoint ECMA Client Side Object Model to query the Assets Library. First, the method attaches to the clientcontext associated with the SharePoint site where the code executes. Then a CAML query is defined to return the items whose CustomerID column matches the customerID parameter passed in from the Silverlight application. Then the web and site objects are loaded from the clientcontext. These objects are used to attach to the asset library list instance where the customer videos are stored. Then, the GetItems method is used to execute the CAML query against the list and the load method specifies which columns to return from the list. By default, not all columns are returned from a SharePoint list when using the ECMA Client Object Model. This behavior is by design and ensures only the columns you specify are returned which helps reduce the payload size of queries. Finally, the success and failure delegate callback methods are registered.
Upon successful execution, the OnVideoQuerySuccess delegate method (Listing 3) fires. This method processes the results returned from the query and formats the output. First, the getEnumerator method is called to return an enumerator used to loop through the result set. Then, a while loop is used to iterate through the result set and format the output. Notice the id variable used to keep track of each video in the list. As you will see later in the article, tracking this variable and using it to generate the HTML output is essential to wire up the ratings control for each video.
Preparing for Ratings Functionality
Listing 4 demonstrates how the OutPutVideoDetails method generates the HTML used to display the metadata for each video returned by the query. Notice how the id for each video is used to set the ID attribute of the anchor tag that displays the link to the play the video as well as the ID attribute of the of the SPAN which will hold the ratings control for each video. These ID attributess will be used to register the ratings control with each video displayed on the page and display the ratings control. To make the code sample easier to read, TABLE, TR, and TD tags are used. In a production scenario you would most likely use DIVs and CSS to format the output.
Providing Silverlight Video Player Functionality
After the HTML that specifies how the videos are displayed on the page is written to the web page, it is possible to attach the out of the box Silverlight Video Player that comes with SharePoint to the videos.
The mediaplayer.js JavaScript file provides several functions you may use to interact with the Silverlight Video player. This file is located in the _layouts virtual directory on the SharePoint server. Below is the JavaScript used to attach the Silverlight Video Player to the video links on the page. The mediaPlayer.AttachToMediaLinks function, defined in the mediaplayer.js file, takes two arguments:
mediaPlayer.attachToMediaLinks((document.getElementById('CustomerVideosContainer')), ['wmv', 'mp3']);
The first parameter indicates which element in the page contains the links you want to attach the Silverlight Media Player to, and the second parameter indicates the extensions for the links to attach to. These parameters give you full control to define the scope to which you attach the media player. Keep in mind, the Silverlight Media Player can only play file types that Silverlight is compatible with. After you attach the Silverlight Media Player to the videos, the Silverlight Media Player will automatically launch when the links are clicked. It’s not often one line of code does so much work for you, but as you will see, the complexity involved with the ratings controls balances this out.
Wiring up the Rating Controls
As I mentioned earlier, one of the goals for the mashup application is to make sure no page refreshes occur. This makes wiring up the ratings controls more complicated than it would be on the server side. When implementing ratings controls completely inside of client side code, the ratings controls may only be wired up after the HTML that defines the items to be rated are written to the web page.
The ratings.js JavaScript file provides several functions you may use to interact with the ratings controls. This file is located in the _layouts virtual directory on the SharePoint server.
Wiring up the ratings controls is a two-step process. In the first step, the ratings manager control is initialized. The ratings manager control defines how the ratings control will behave, which site and web it is tied to, and configures the UI.
The ExecuteOrDelayUntilScriptLoaded JavaScript function is used to wait until the ratings.js JavaScript file is loaded in the browser before the RatingsManagerLoader function is called:
ExecuteOrDelayUntilScriptLoaded(RatingsManagerLoader, 'ratings.js');
The RatingsManagerLoader function (Listing 5) configures the Ratings Manager and Indicates the Ratings Manager is loaded and available for use.
After the ratings manager is loaded, the second step in the process commences. In this step the ratings control is attached to each video on the page. Listing 6 demonstrates how this is done. First, the numberofVideos variable that was set when the query results were processed is used to loop through the videos on the web page. The variable t starts at 0, just like the id variable did when the query results were written to the page. Concatenating the videoUrl string with the value of the t variable creates a string which is used to locate the URL for each video on the web page. The URL corresponding to each video is used to register the rating control with each video on the web page.
The RatingsControlLoader function (Listing 7) initializes a new ratings control based on the id and VideoUrl parameters. The id parameter is used to locate the SPAN where the ratings control should be placed on the page and the VideoUrl parameter is used to wire the control to the corresponding list item in the Assets Library. After these scripts execute, the ratings controls are wired to the corresponding videos on the page and are ready for use.
Goals Met
In this series of articles I demonstrated how to create mashup applications that pull data from multiple external data sources, provide complimentary mapping functionality and include video player and rating functionality. My mashup goals were also met, as all of this functionality is sandbox compatible and occurs without any page refreshes.
private void showCustomerVideosButton_Click(object sender, RoutedEventArgs e){if (Common.Customer == null){ HtmlPage.Window.Alert("Select a customer.");}else{HtmlPage.Window.Invoke("GetVideos", new string[] { Common.Customer.ID });}}
function GetVideos(customerID) {//Remove anything inside the CustomerVideosContainer DIV$('.CustomerVideosContainer').empty();//Attach to the client contextthis.ClientContext = SP.ClientContext.get_current();//Create a CAML queryvar CamlQuery = new SP.CamlQuery();CamlQuery.set_viewXml("" + customerID + "");//Load the SPWebthis.web = this.ClientContext.get_web();//Load the SPSitethis.site = this.ClientContext.get_site();this.ClientContext.load(this.web);this.ClientContext.load(this.site);//Attach to the digital asset library list instancethis.digitalAssetsLibraryList = this.web.get_lists().getByTitle(digitalAssetsLibraryListName);//Return the list items based on the CAMl querythis.digitalAssetsLibraryListItems = this.digitalAssetsLibraryList.getItems(CamlQuery);//Load the list itemsthis.ClientContext.load(this.digitalAssetsLibraryListItems, 'Include(RatingCount, FileLeafRef, Length_x0020__x0028_seconds_x0029_, AverageRating, AlternateThumbnailUrl, FileSizeDisplay, RatingCount, AverageRating, Title, DisplayName, ContentType, File, Id, Created, Modified, _Comments, CustomerID)');//Execute the batched commandsthis.ClientContext.executeQueryAsync(Function.createDelegate(this, this.OnVideoQuerySuccess), Function.createDelegate(this, this.OnVideoQueryFailed));}
function OnVideoQuerySuccess(sender, args) {//Create the table to display the videosoutput = "";var videosEnumerator = this.digitalAssetsLibraryListItems.getEnumerator();var currentColumn = 0;var id = 0;//Loop through the videos and display themwhile (videosEnumerator.moveNext()) {//Get each videovideoListItem = videosEnumerator.get_current();//Get the content type for the list itemvar videoListItemContentType = videoListItem.get_contentType();//See if the list item is a Videoif (videoListItemContentType.get_name() == "Video") {//Get the file associated with the list itemvideo = videoListItem.get_file();if (video != null && video != undefined) {//Keep track of the column the video is displayed incurrentColumn = currentColumn + 1;//Create the HTML for the video in column 1if (currentColumn == 1) {output += "";}//Create the HTML for the video in column 2else {output += "";currentColumn = currentColumn - 2;}}id++;}}this.numberOfVideos = id;output += "";OutPutVideoDetails(id);output += "";OutPutVideoDetails(id);output += "";//Write the HTML to the page with JQuery. The DIVs referenced in the JavaScript code are //defined in the Customer Videos Web Part.$('.CustomerVideosContainer').append(output);$('.CustomerVideosContainer').fadeIn("slow");//Silverlight video player code goes here. See later in the article for more details.//Ratings code goes here. See later in the article for more details.}
function OutPutVideoDetails(id) {output += "";output += "";output += "";var avgRating = videoListItem.get_item('AverageRating');output += "";output += "";output += "";var videoFileSize = videoListItem.get_item('FileSizeDisplay');output += "";output += "";output += "";output += "" + videoListItem.get_item('Title') + " ";var timeCreated = video.get_timeCreated();var day = timeCreated.getDate();var month = timeCreated.getMonth();var year = timeCreated.getFullYear();output += " - " + month + "/" + day + "/" + year + "";output += "";output += "";output += "";output += "";output += "";output += "";output += "";output += "";output += "";output += "";output += "" + videoListItem.get_item('RatingCount') + " RatingsLength (sec): " + videoListItem.get_item('Length_x0020__x0028_seconds_x0029_') + "Size: " + parseInt(videoFileSize / 1024) + "K" + videoListItem.get_item('_Comments') + "";}
function RatingsManagerLoader() {this.ratingsData = new RatingsCommonData(' Loading...','Average Rating','Number of Ratings','My Rating','Thank you. Your rating has been submitted and will be processed soon.','Not available','Not rated yet','Click to assign a rating (0-5)','', // session idthis.web.get_id(),this.web.get_serverRelativeUrl(),this.site.get_id(),'u002f_layoutsu002fImagesu002fRatingsNew.png','u002f_layoutsu002fImagesu002fRatingsEmpty.png');ratingsManager = new RatingsManager(ratingsData);//Indicate this step is done so dependent events fireNotifyEventAndExecuteWaitingJobs('RatingsManagerLoaded');}
for (t = 0; t < numberOfVideos; t++){var videoElementID = "videoUrl" + t;var videoUrl = document.getElementById(videoElementID).href;ExecuteOrDelayUntilEventNotified(RatingsControlLoader(t, decode(videoUrl)), RatingsManagerLoaded');}
function RatingsControlLoader(id, videoUrl) {var ratingsControl = new RatingsControl(videoUrl,'', // Item title"RatingsCtrl" + id,this.ratingsData);ratingsControl.Initialize();}
About the Author
You May Also Like