Due to my positive experience with the Composite C1 CMS system, I’m probably going to implement my next version of my e-learning courses with the system.

The system does support video right out of the box, e.g.:   YouTube and a SilverLight player.  However using YouTube for materials in an e-learning course is out of the question, as it can’t be public.  Silverlight is  a no-go, as it has to be cross-platform.   For streaming I’m using a Wowza streaming server, to perform real streaming (in contrast to pseudo streaming) using the RTMP protocol.

For the player I’m using the excellent  free video player called FlowPlayer (actually I’m using the commercial version, but there’s no big difference).   But just to play around with Composite C1 and see how hard it was to implement the video player, I decided to try to implement a new function exposing the basic FlowPlayer functionality.   This was implemented in less than 10 minutes.  So let me show you how I did that.

The FlowPlayer requirements

1. The page has to include the necessary JavaScript code for the FlowPlayer (eg. placed in the <head> section).
2. A simple container has to be defined, where the video will reside.
3. JavaScript invoking the FlowPlayer within the container must be added (after the container).

See the simple non-Composite example here.

Implementing the FlowPlayer in Composite C1

There are different ways to go about this, but I chose the Razor way, as it seemed as the quickest route.  Log into Composite administration and select “System” from the navigation bar:

Now expand the root folder “/”, locate the “Razor/Examples” folder.   Right click it and select “New file”:

The new file dialog appears and enter the name of the function with the “.cshtml” extension as shown below:

Click OK and an empty FlowPlayer.cshtml file is created and shown in the tree:

Double clicking the file, will open it for editing, now paste the following code (hold your horses, I’ll explain what it does):

 

@using CompositeC1Contrib.RazorFunctions;
@using CompositeC1Contrib.RazorFunctions.FunctionProvider;
@using CompositeC1Contrib.RazorFunctions.Html;

@inherits CompositeC1WebPage

@functions {
     [FunctionParameter("Video width", "Width of video eg. 425px", "425px")]
    public string VideoWidth { get; set; }

       [FunctionParameter("Video height", "Height of video eg. 300px", "300px")]
    public string VideoHeight { get; set; }

      [FunctionParameter("Video URL", "Url containing video file", "http://pseudo01.hddn.com/vod/demo.flowplayervod/flowplayer-700.flv")]
    public string VideoURL { get; set; }

    [FunctionParameter("Auto Play", "Should video automatically start (default: False)", false)]
    public bool AutoPlay { get; set; }

       [FunctionParameter("Auto buffer", "Should video automatically buffer (default: True)", true)]
    public bool AutoBuffer { get; set; }}
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
               <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%22text%2Fjavascript%22%20src%3D%22http%3A%2F%2Fstatic.flowplayer.org%2Fjs%2Fflowplayer-3.2.6.min.js%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
     </head>
  <body>
  <a href="@VideoURL" style="display:block;width:@VideoWidth;height:@VideoHeight" id="@PlayerId"></a>
       <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20language%3D%22JavaScript%22%3E%0A%20%20%20%20%20%20%20%20%20flowplayer(%22%40PlayerId%22%2C%20%22http%3A%2F%2Freleases.flowplayer.org%2Fswf%2Fflowplayer-3.2.7.swf%22%2C%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20clip%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20autoPlay%3A%20%40AutoPlay.ToString().ToLower()%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20autoBuffering%3A%20%40AutoBuffer.ToString().ToLower()%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%20%20%20%20%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" /> 
   </body>
</html>

@functions {
  private string _playerId;
  private string PlayerId { 
     get
        {
          if(_playerId==null)
                _playerId = String.Format("flowPlayer{0}", Guid.NewGuid().GetHashCode());
            return _playerId;
      } 
 }
}

Li. 7-21
Here the parameters are defined for the function.  This is standard C# properties (read/write) enriched with a Composite attribute named [FunctionParameter].  In this way Composite knows, that the property is going to be exposed as a parameter for the function.
I’ve chosen to expose five parameters and setting their default values (so when you use the function in a page, you don’t have to specify anything … though, you probably would … if you’d want to show another video :)).

Li. 23-25:
Here I specify the content of the <header>, this will be merged into the <header> of the page where you insert the function.
It contains a reference to the FlowPlayer javascript necessary to invoke the video player.

Li. 27
First line in the <body> is the container tag, that will be used for the video.   Here I’d like the href to point to the location filled out by the user, that is the parameter VideoURL.  This is simply done by using the Razor syntax @VideoURL.
The width/height is set in the style, again using the Razor syntax (you’d probably want to use a stylesheet instead) and last I set the id of the tag.

If I hardcoded the id, it wouldn’t work if you inserted more than one player on the same page.  So each id has to be unique.  I don’t want to enter the id each time, I insert a video – because I really don’t care.   So I here generate a unique id, by calling the private property named “PlayerId” located in li. 39-49.   The first time the property is called, I generate a new id by using the GUID feature (ooh, you mainframe people you envy this … I just know it! :)).     The second time it is called, it returns the original create unique id.   Here this is important, as it is called the first time in li. 27 (@PlayerId).   And the second time in li. 29, where the JavaScript that performs the “binding” of the container with the FlowPlayer, needs to know the id of the container.

Li. 31-32
Here the autoPlay and autoBuffer functionality of the FlowPlayer is set (transferred from the two properties).   These are defined as boolean’s where I convert it to a string and do a ToLower() on the string as a ToString on a boolean value will return a capitalized version, whereas the JavaScript needs a e.g. “true” instead of “True”.

Click “Save” and you’re done!

Testing the video player in a page

Open the page where you want the video player to be placed, and put the cursor in the content where you want to insert it:

Now from the toolbar select the “Insert” and choose “Function …”:

Then you just select the function you want to insert in our case … big surprise … the FlowPlayer and click OK:

Then you are presented with a dialog, where you can specify the parameters for the video player:

As you can see the five parameters are listed in the left side, all with green arrows.  This means that you don’t have to specify anything (this is because I defined the parameters with default values).  If you choose not to specify default values in the Razor function, such parameters will be shown with a red arrow.

At this point you can press OK, but you would probably like to select another video file.  Do that by clicking the Video URL:

Then the right side changes.  Just after selection the Parameter Type is “Default”, if you click “Constant” as shown above, you’ll be able to specifiy the parameter value – the url for video file to display.   Now click OK and the video player is inserted into the page:

The “window” with the lightning that has been inserted into the page, indicates that the function named “FlowPlayer” is rendered here.  Any parameters different from default parameters is also shown here (how clever it is :).   Save the page and you are now ready to test it – click the “Preview” tab:

Conclusion

You gotta’ admit this is pretty easy … right?!   The function in itself is straight forward and the defined function can be used by any user without any kind of coding experience.

My next step will be trying to implement Extranet-like functionality – so I can use Composite for my e-learning.  But as I can understand the comment by Marcus Wendt (Composite) on my other article, the current version of Extranet has some bugs that are currently being fixed.   Further more I’m not sure, if the Extranet module will be enough for my needs – I will be needing automatic expiration on logins (maybe I can implement it on top of Extranet – if the next version works better).