Tuesday, November 01, 2011

Video con SharePoint 2010


Hola de nuevo. Lo primero comentar sobre una gran falencia de nuestra querida plataforma, y es el no poder embeber con facilidad vídeo dentro de campos de tipo Rich HTML. Una gran falencia porque debería ser algo natural de hacer, y no se puede. No voy a entrar en detalles, pero por ejemplo les dejo una muestra, de todo lo que toca hacer para embeber video en una entrada de Blog de SharePoint 2010.

http://blogs.msdn.com/b/sharepointdesigner/archive/2009/12/11/video-blogging-with-javascript-and-the-media-web-part.aspx

Así sucesivamente si se navega por internet buscando soluciones, ninguna es simple, y siempre tocará hacer unos cuantos pasos.

Ahora bien, más allá de eso esta entrada quiere mostrarles cómo usar la WebPart de Media que SharePoint Server 2010 nos ofrece como mecanismo para presentar vídeo y audio. Esta WebPart aparentemente tiene una limitación, y es que para el usuario final, solo le permitirá establecer "rígidamente" el vínculo a un vídeo o archivo de audio, sin permitir ningún tipo de interacción dinámica. Recuerden siempre el cliente quiere una presentación de vídeo tipo YouTube, no un único vídeo preestablecido.

Lo comentado al inicio de esta entrada va directamente relacionado con esto, porque lo primero que los clientes quieren es, por ejemplo, embeber vídeo en cuerpos de Noticias, en entradas de Blog, y en general tener canales de vídeo, tipo YouTube.

Entonces, luego de tanta palabra, pongamos manos a la obra:

  • Desarrollando la lógica de negocio: El método a continuación que se explica, puede meterse en un proyecto de biblioteca de clases.

El siguiente método tiene como objetivo conectarse a una biblioteca de Media y desplegar el listado de Vídeos, que se filtran a través de una vista de dicha biblioteca.

La vista es una excelente técnica cuando por ejemplo se quiere limitar a una cantidad de ítems, ordenarlos, agruparlos, y en general poder tener los ítems listos para ser consumidos a través de código .NET.

Lo primero que tenemos a continuación es la rutina en C#, que a través del modelo de objetos administrado retorna la lista genérica con los ítems consultados de la vista de esa biblioteca de media de SharePoint.

public IList<TGVideo> GetVideos(string listName, string viewName)
{
IList<TGVideo> videos = new List<TGVideo>();
TGVideo video = null;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(SPContext.Current.Site.Url))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists[listName];
if (list != null)
{
try
{
SPView view = list.Views[viewName];
SPListItemCollection items = list.GetItems(view);
foreach (SPListItem item in items)
{
video = new TGVideo();
video.Title = item.Name.Split('.')[0];
video.UrlVideo = "/paginas/videos.aspx?video=" + item.Name;
if (item["AlternateThumbnailUrl"] != null)
video.Thumbnail = ((SPFieldUrlValue)item["AlternateThumbnailUrl"]).Url;
else
video.Thumbnail = "/_layouts/images/VideoPreview.png";
videos.Add(video);
}
}
catch (Exception ex)
{
EventLog log = new EventLog();
if (!(log.Source == "Errores WebPart"))
log.Source = "Errores WebPart";
log.WriteEntry("Error en el Método GetVideos: " + ex.Message + "- InnerException: " + ex.InnerException + "- Source: " + ex.Source + "- StackTrace: " + ex.StackTrace,
EventLogEntryType.Error, 1);
throw;
}
}
}
}
});
return videos;
}

Lo primero que se aprecia es que el método retorna una lista genérica de elementos de tipo TGVideo. Dicha clase luce así:

public class TGVideo
{
private string title;
public string Title
{
get
{
return title;
}
set
{
title = value;
}
}
private string thumbnail;
public string Thumbnail
{
get
{
return thumbnail;
}
set
{
thumbnail = value;
}
}
private string urlVideo;
public string UrlVideo
{
get
{
return urlVideo;
}
set
{
urlVideo = value;
}
}
}

El método recibe como parámetros el nombre de la lista e incluso el nombre de la vista que finalmente servirá para consultar los ítems.

Recordemos que una sitio de SharePoint hay usuarios que se loguean y tendrán privilegios de administración, pero otros de solo lectura. Esos usuarios de solo lectura son muy limitados, y si eso no se tiene en cuenta, las WebParts que construimos pueden fallar. Por tal razón es que toda la parte esencial del método se envuelve dentro de la rutina SPSecurity.RunWithElevatedPrivileges, que en términos generales permite ejecutar todo el código que encierra con permisos elevados.

La manera de instanciar objetos SPSite y SPWeb, es la mejor práctica usar USING. En este caso no se aprovecha obtener los objetos del contexto de SharePoint con la clase SPContext, porque SPSecurity.RunWithElevatedPrivileges no funciona así, por tal motivo es que esas instancias se obtienen de ese modo, y por lo tanto se hace DISPOSE formal con la clausula USING.

Luego se puede apreciar como se obtiene la lista a través de su nombre. Muy importante recordar que no es una buena práctica cuando se tiene muchas listas. Lo mejor será instanciar la lista a través de su ID para aumentar el rendimiento. Luego se obtienen los ítems desde la vista y a partir de ahí se itera para recuperar la información deseada a través de la clase de entidad TGVideo.

La página Video.aspx es la que servirá como contenedor o visor de los videos, más adelante se explica su funcionalidad.

Luego si quiere obtener el Thumbnail asociado al video se usa el campo con su nombre interno AlternateThumbnailUrl. En caso que no se haya establecido la ruta una imagen que sirva como thumbnail se usa la imagen por defecto de SharePoint, para tal caso se encuentra en la ruta: /_layouts/images/VideoPreview.png

Finalmente como manejo de errores se escribe una rutina que permita registrar los sucesos en el visor de eventos de Windows, nombrando la entrada como Errores WebPart.

  • Desarrollando la WebPart Visual: Las WebParts visuales son un concepto y técnica esperado desde hace ya bastante tiempo, al menos desde que desarrollamos para SharePoint 2007. En esencia es una separación entre lógica de WebPart (eventos) y presentación (HTML y Codebehind), a través de una clase que hereda de la clase WebPart y otra que es básicamente un control de usuario ASCX. Muy importante recordar que cuando vayan a crear el proyecto de tipo SharePoint 2010 en VS.NET 2010, deben configurarlo como un deploy de tipo FARM. Las WebParts visuales no pueden ser desplegadas como tipo SandBox. En este caso se asume que se va a agregar al proyecto de tipo SharePoint 2010 un nuevo ítem de tipo WebPart visual llamado VideosPart.

En el HTML del control ASCX tendremos lo siguiente:

<div>
<asp:GridView ID="grvVideos" CellSpacing="10" CellPadding="5" runat="server" GridLines="None" ShowHeader="false" AutoGenerateColumns="false">
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <a href='<%#Eval("UrlVideo")%>'>
                    <asp:Image ID="imgThumbnail" ImageUrl='<%#Eval("Thumbnail")%>' runat="server" Width="120" Height="90" BorderWidth="0" />
                </a>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:Label ID="lblTitle" runat="server" Text='<%#Eval("Title")%>' />
                <br />
                <a href='<%#Eval("UrlVideo")%>'>Ver Video</a>
               
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>
</div>
<div>
    <asp:Label ID="lblMessage" runat="server" />
</div>

En esencia cada Eval() hace llamado a las propiedades definidas en la clase de entidad TGVideo.

Ahora bien, el codebehind del control ASCX deberá tener lo siguiente:

private TGServices services = null;

        private string listName;
        public string ListName
        {
            get { return listName; }
            set { listName = value; }
        }

        private string viewName;
        public string ViewName
        {
            get { return viewName; }
            set { viewName = value; }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            this.lblMessage.Text = "";
            if (!Page.IsPostBack)
            {
                services = new TGServices();
                IList<TGVideo> videos = null;
                try
                {
                    videos = services.GetVideos(listName, viewName);
                    grvVideos.DataSource = videos;
                    grvVideos.DataBind();
                }
                catch (Exception ex)
                {
                    this.lblMessage.Text = "Se ha presentado un error cargando la información: " + ex.Message;
                }
                if (grvVideos.Rows.Count == 0 || videos == null)
                    this.lblMessage.Text = "Actualmente no hay información para desplegar en este elemento.";
            }
        }

Si se fijan es muy poco código, porque toda la complejidad se delegó en la capa de negocio. Lo primero es una instancia de la clase que contendrá el método que se explicó de la lógica de negocio. En este ejemplos e asume que dicha clase se llama TGServices. Luego se definen 2 propiedades que servirán como puente de comunicación entre la clase WebPart y el con trol ASCX. Para este caso la WebPart proporcionará como propiedades configurables el nombre de la lista y de la vista. Y Finalmente se invoca el método que retorna los videos en forma de lista genérica que se pasa como fuente de datos al GridView.

Finalmente así lucirá el código de la clase WebPart:

[Personalizable(), WebBrowsable, Category("Configuración"), WebDisplayName("Nombre de la Lista")]
        public String ListName { get; set; }

        [Personalizable(), WebBrowsable, Category("Configuración"), WebDisplayName("Nombre de la Vista")]
        public String ViewName { get; set; }

        // Visual Studio might automatically update this path when you change the Visual Web Part project item.
        private const string _ascxPath = @"~/_CONTROLTEMPLATES/Familia.WebParts/VideosPart/VideosPartUserControl.ascx";

        protected override void CreateChildControls()
        {
            Control control = Page.LoadControl(_ascxPath);
            ((VideosUserControl)control).ListName = ListName;
            ((VideosUserControl)control).ViewName = ViewName;
            Controls.Add(control);
        }

Se aprecia que las propiedades definidas en el code behind del control de usuario son usadas para pasar dichos valores desde la clase de tipo WebPart.

Hasta este punto tenemos ya lista la WebPart visual que consume y despliega un listado de Videos. Pero como ya se explicó, la URL configurada al hacer clic sobre el thumbnail o la opción Ver Video de la WebPart, lleva a una página llamada Videos.aspx. Como se pudo observar es una página que se ha creado en una biblioteca llamada Páginas de SharePoint. La página Videos.aspx se ha creado como una Página con WebParts. En esa página se deberá agregar un DIV de esta forma, por ejemplo en el Place Holder Main:

<div id="divMediaWebpart">
</div>

El único objetivo del DIV anterior es ser el contenedor de lo que será la WebPart de Media de SharePoint 2010, agregada dinámicamente.

Luego si se quiere agregando una WebPart de Editor de contenido, o directamente en el HTML de la página a través de SharePoint Designer 2010, agregar el siguiente JavaScript:

<script type="text/javascript">
   
        $(document).ready(function() {
            var videoHolder = document.getElementById('divMediaWebpart');
            mediaPlayer.createMediaPlayer(
            videoHolder, videoHolder.id, '640px', '390px',
            {
                 displayMode: 'Inline',
                 mediaTitle: '',
                 mediaSource: '/Videos/' + getQuerystring('video',''),
                 previewImageSource:'/_layouts/images/VideoPreview.png',
                 autoPlay: true,
                 loop: false,
                 mediaFileExtensions:'wmv;wma;avi;mpg;mp3;',
                 silverlightMediaExtensions:'wmv;wma;mp3;'
             }
         );

           
        });
 
    </script>

Muy importante a tener en cuenta los siguiente:

  1. El JavaScript anterior solo funciona agregando la siguiente referencia, bien sea directamente en la página o en la página maestra del sitio: <script type="text/javascript" src="/_layouts/mediaplayer.js"></script>
  2. El JavaScript usa una función que permite leer valores pasados por QueryString cuya implementación es similar a esto:

    function getQuerystring(key, default_)
    {
       if (default_==null) default_="";
          key = key.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
       var regex = new RegExp("[\\?&]"+key+"=([^&#]*)");
       var qs = regex.exec(window.location.href);
       if(qs == null)
         return default_;
       else
           return qs[1];
    }

    La función anterior en el JavScript de la página Videos.aspx estaría esperando un nombre de parámetro llamado video, que en esencia trae el nombre del video junto a su extensión. Dichos Videos deberán alojarse en una biblioteca de Media llamada Videos.

  3. Debido a que se hace uso de algunas opciones de JQuery, es también obligatorio adicionar una referencia a la librería respectiva, por ejemplo en la página maestra lo siguiente:

    <script type="text/javascript" src="/SiteAssets/JS/jquery-1.4.3.min.js"></script>

Con lo anterior se puede disfrutar de una página que despliega Video dinámicamente así:

WebPart desplegando el listado de Videos:

VideoPart

Luego al hacer clic en Ver Video o en el thumbnail se hace un redirect a la página ya explicada Videos.aspx:

Video

Feliz video player!!

No comments: