This Blog

Syndication

Search

Tags

Community

Email Notifications

Archives

Enlaces Recomendados

SharePoint 2010: ¿Se pueden activar features con el modelo de objetos en cliente? (II)

Esta pregunta me hacía hace unos meses, sobre todo tratando de ver como automatizar el despliegue y activación de soluciones Sandbox tanto para SharePoint 2010 On-Premise como para SharePoint Online a través del modelo de objetos en cliente. El caso es que esta semana, a partir de un requerimiento de un proyecto, le he tenido que meter “mano” en serio a este tema y he llegado a una solución más o menos satisfactoria en cuanto a que funciona, pero que no me parece nada óptima…pero salvo que me equivoque, de momento no hay mucho que rascar a falta de clases que permitan activar soluciones Sandbox en el modelo de objetos en cliente (CSOM). Bueno, vamos a meternos en harina:

  • Lo primero que he hecho es crear un proyecto de tipo Windows Forms (por ejemplo, lo que implica que vamos a trabajar con el CSOM .NET), con un formulario inicial en el que el usuario especifique la url de la colección de sitios con la que se va a trabajar. En este ejemplo particular, se va a tratar además de una colección de sitios de SharePoint Online por lo que el formulario, que consta de un simple botón y una caja de texto, tiene el siguiente código:
    • Cómo veis, desde el formulario inicial llamamos a otro, pero en otro hilo creado a tal efecto.
    • La razón de crear un nuevo hilo es debida al mecanismo de autenticación contra Office 365 que implica iniciar una sesión en Office 365 a través de un control WebBrowser…y el caso es que este control WebBrowser no se puede abrir en el mismo hilo si ya se ha abierto otro formulario.
   1: private void btnGo_Click(object sender, EventArgs e)
   2: {
   3:     sUrl = txtOffice365Site.Text;
   4:     this.Close();
   5:     this.Dispose();
   6:     //Application.Run(new frmActivarSBSPO(sUrl));
   7:     Thread thrThread = 
   8:         new Thread(new ThreadStart(OpenActivateSolutionForm));
   9:     thrThread.Name = "MainForm";
  10:     thrThread.SetApartmentState(ApartmentState.STA);
  11:     thrThread.Start();
  12: }
  13:  
  14: void OpenActivateSolutionForm()
  15: {
  16:     frmActivarSBSPO frmActivarSolution =
  17:         new frmActivarSBSPO(sUrl);
  18:     Application.Run(frmActivarSolution);            
  19: }
  • En un segundo formulario, lo qué haremos una vez iniciada sesión en Office 365 es activar una solución Sandbox en concreto. Añadimos en primer lugar una referencia al CSOM de .NET (Microsoft.SharePoint.Client y Microsoft.SharePoint.Client.Runtime). Añadimos las directivas necesarias en el segundo formulario:
   1: //Espacios de nombres necesarios
   2: using MO_NET = Microsoft.SharePoint.Client;
   3: using System.Net;
   4: using MSDN.Samples.ClaimsAuth;
   5: using System.IO;
  • Añadimos los miembros de la clase formulario y codificamos el constructor. Como veis, es aquí dónde iniciamos la autenticación usando para ello el ejemplo de Robert Bogue disponible aquí y que por supuesto tendremos que referenciar en nuestro proyecto. Como veis, generamos el contexto a partir de un ClaimClientContext,
   1: MO_NET.ClientContext ctx;
   2: DataTable dtDatos;
   3: WebBrowser wBrowser;
   4:  
   5: /// <summary>
   6: /// Form constructor
   7: /// </summary>
   8: /// <param name="sTargetSite"></param>
   9: public frmActivarSBSPO(string sTargetSite)
  10: {
  11:     InitializeComponent();           
  12:     
  13:     //Accesing to a Claim Client Context
  14:     using (ctx = ClaimClientContext.GetAuthenticatedContext(sTargetSite))
  15:     {
  16:         if (ctx != null)
  17:         {
  18:             MO_NET.Web wSite = ctx.Web;
  19:             ctx.Load(wSite);
  20:             ctx.ExecuteQuery();                    
  21:         }
  22:     }
  23: }
  • A continuación, en el código del botón que usaremos para activar la solución es dónde comienza “la chicha” de la activación de la solución. La única forma que he visto para activar una solución Sanbox pasa por simular una navegación a la solución en concreto y simular un clic de ratón de forma que la solución se active. Para simular la navegación, usamos un objeto de tipo WebBrowser y el método Navigate() en el que especificamos la Url de activación de la solución. A continuación, para dar tiempo a cargar esa página tenemos que indicar el manejador para DocumentCompleted que luego codificaremos. Como veis, además nos aseguramos de suprimir cualquier posible error de JavaScript que pudiese darse para evitar que se le muestre al usuario.
   1: private void btnInstalar_Click(object sender, EventArgs e)
   2: {
   3:     try
   4:     { 
   5:         string targetSolutionUrl = ctx.Url +
   6:             "/_catalogs/solutions/Forms/Activate.aspx?Op=ACT&ID=5&IsDlg=0";
   7:         //WebBrowser object allows to simulate a navigation
   8:         wBrowser = new WebBrowser();
   9:         wBrowser.Navigate(new Uri(targetSolutionUrl));
  10:         wBrowser.DocumentCompleted +=
  11:             new WebBrowserDocumentCompletedEventHandler(wBrowser_DocumentCompleted);
  12:         //Supressing any JavaScript error
  13:         wBrowser.ScriptErrorsSuppressed = true;
  14:         MessageBox.Show("¡Solución SandBox Activada!");       
  15:     }
  16:     catch (Exception ex)
  17:     {
  18:         MessageBox.Show(ex.Message);
  19:     }
  20: }
  • A continuación, codificamos el manejador para DocumentCompleted dónde nos aseguraremos por una parte de localizar el botón “Activate” relativo a una solución Sandbox y luego forzaremos el correspondiente clic. Cómo veis, todo pasa por definir un objeto Document a partir del objeto WebBrowser y luego un objeto ElementCollection que contendrá la colección de hipervínculos de la página. Localizamos el botón de activación y simulamos el clic en el mismo…lamentablemente, con hacer una simulación y debido al comportamiento del objeto WebBrowser no es suficiente por lo que tenemos que asegurarnos que la solución se ha activado y esto es lo que hacemos con la llamada a ActivateSolution. Una vez que la solución se ha activado, simplemente mostramos en un control de tipo DataGridView las soluciones de la galería de soluciones a través del método ReedSolutions().
   1: private void wBrowser_DocumentCompleted(object sender, 
   2:         WebBrowserDocumentCompletedEventArgs e)
   3: {
   4:     dtDatos = new DataTable();
   5:     dtDatos.Columns.Add("A Tag");
   6:     string sButton;
   7:     HtmlDocument hDoc = ((WebBrowser)sender).Document;
   8:     //Collection of hyperlinks elements in the page
   9:     HtmlElementCollection aTag = hDoc.Links;
  10:     foreach (HtmlElement he in aTag)
  11:     {
  12:         DataRow drow = dtDatos.NewRow();
  13:         drow["A Tag"] = he.Id;
  14:         dtDatos.Rows.Add(drow);
  15:     } 
  16:  
  17:     //This block allows to simulate a click in the Activate button for a Sandbox solution
  18:     if (dtDatos != null)
  19:     {
  20:         bool bActivarSolucion; ;
  21:         foreach (DataRow dtr in dtDatos.Rows)
  22:         {
  23:             bActivarSolucion = false;
  24:             sButton = dtr["A Tag"].ToString();
  25:             //Looking for the activate button
  26:             if (sButton.Contains("Activate"))
  27:             {
  28:                 //Simulating the click in the button
  29:                 wBrowser.Document.GetElementById(sButton).InvokeMember("click");
  30:                 iCounter = iCounter + 1;
  31:                 bActivarSolucion = ActivateSolution(ctx);
  32:             }
  33:             if (bActivarSolucion)
  34:             {
  35:                 dataGridView1.DataSource = 
  36:                     ReedSolutions(ctx);
  37:                 break;
  38:             }
  39:         }
  40:     }            
  41: }
  • El método ActivateSolution() simplemente comprueba que la solución en cuestión ha sido activada en cuyo caso devuelve True o no de manera que el clic de activación sea forzado de nuevo:
   1: public static bool ActivateSolution(MO_NET.ClientContext ctx)
   2:    {
   3:        bool bActivarSolucion = false;
   4:        try
   5:        {
   6:            using (MO_NET.ClientContext ctxAux = ctx)
   7:            {
   8:                if (ctx != null)
   9:                {
  10:                    //Accessing to the solutions gallery
  11:                    MO_NET.List solutionList = ctx.Site.GetCatalog(121);
  12:  
  13:                    //Accessing to the solutions collection
  14:                    MO_NET.ListItemCollection licCollection =
  15:                        solutionList.GetItems(MO_NET.CamlQuery.CreateAllItemsQuery());
  16:  
  17:                    //Defining the operation
  18:                    ctx.Load(licCollection);
  19:  
  20:                    //Performing the operation
  21:                    ctx.ExecuteQuery();
  22:  
  23:                    //Processing results
  24:                    foreach (MO_NET.ListItem li in licCollection)
  25:                    {
  26:                        MO_NET.FieldLookupValue fl = (MO_NET.FieldLookupValue)li.FieldValues["Status"];
  27:                        string sSolutionPath = li["FileRef"].ToString();
  28:                        //Checking if the solution is active
  29:                        if (fl != null && sSolutionPath.Contains("SPOWP"))
  30:                        {
  31:                            bActivarSolucion = true;
  32:                            break;                                   
  33:                            
  34:                        }
  35:                        else
  36:                        {
  37:                            if (sSolutionPath.Contains("SPOWP"))
  38:                            {                                    
  39:                                break;
  40:                            }
  41:                            
  42:                        }
  43:                    }
  44:                }
  45:            }
  46:        }
  47:        catch (Exception ex)
  48:        {
  49:            MessageBox.Show("Error: {0}", ex.Message);
  50:        }
  51:  
  52:        return bActivarSolucion;
  53:    }
  • Finalmente, el método ReedSolucions() simplemente me devuelve información relativa al listado de soluciones disponible en la galería de soluciones:
   1: public static DataTable ReedSolutions(MO_NET.ClientContext ctx)
   2: {
   3:     DataTable dtSoluciones = new DataTable();
   4:     dtSoluciones.Columns.Add("ID");
   5:     dtSoluciones.Columns.Add("Solución");
   6:     dtSoluciones.Columns.Add("Estado Activación");
   7:     DataRow dtrSoluciones;
   8:     
   9:     try
  10:     {
  11:         using (MO_NET.ClientContext ctxAux = ctx)
  12:         {
  13:             if (ctx != null)
  14:             {
  15:                 //Accessing to the solutions gallery
  16:                 MO_NET.List solutionList = ctx.Site.GetCatalog(121);
  17:  
  18:                 //Accessing to the solutions collection
  19:                 MO_NET.ListItemCollection licCollection =
  20:                     solutionList.GetItems(MO_NET.CamlQuery.CreateAllItemsQuery());
  21:  
  22:                 //Defining the operation
  23:                 ctx.Load(licCollection);
  24:  
  25:                 //Performing the operation
  26:                 ctx.ExecuteQuery();
  27:  
  28:                 //Processing the results
  29:                 foreach (MO_NET.ListItem li in licCollection)
  30:                 {
  31:                     MO_NET.FieldLookupValue fl = (MO_NET.FieldLookupValue)li.FieldValues["Status"];
  32:                     dtrSoluciones = dtSoluciones.NewRow();
  33:                     dtrSoluciones["ID"] = li["ID"];
  34:                     dtrSoluciones["Solución"] = li["FileRef"];
  35:                     //Solución activada o no
  36:                     if (fl != null)
  37:                     {
  38:                         dtrSoluciones["Estado Activación"] = "Sí";
  39:                     }
  40:                     else
  41:                     {
  42:                         dtrSoluciones["Estado Activación"] = "No";
  43:                     }
  44:                     dtSoluciones.Rows.Add(dtrSoluciones);
  45:                 }
  46:             }
  47:         }
  48:     }
  49:     catch (Exception ex)
  50:     {
  51:         MessageBox.Show(ex.Message);
  52:     }
  53:     return dtSoluciones;
  54: }

Y a partir de aquí a comprobar que todo funciona como se espera:

  • En primer lugar, verificamos que la solución sanbox en cuestión no está activa en la galería de soluciones.
  • Ejecutamos el proyecto de forma que en la primera ventana del mismo especificamos la Url de la colección de sitios de Office 365.
  • A continuación y desde el formulario anterior se muestra la ventana de petición de credenciales de Office 365.
image image image
  • Una vez especificadas las credenciales, se muestra el segundo formulario. Pulsamos el botón de activación de solución.
  • Una vez que el proceso de activación concluye, veremos como en el grid aparece la solución Sandbox con estado activado.
  • Lo mismo se puede comprobar al inspeccionar de nuevo la galería de soluciones.
image image image

Published 7/3/2012 23:37 por Juan Carlos González Martín

Comparte este post:

Comentarios

# ¿Se pueden activar features con el modelo de objetos en cliente?@ Wednesday, March 7, 2012 11:48 PM

Esta pregunta me hacía hace unos meses , sobre todo tratando de ver como automatizar el despliegue

Blog técnico de Office 365

# SharePoint Online: Cómo leer información relativa a las soluciones Sandbox cargadas!@ Thursday, March 15, 2012 10:38 PM

En este artículo os comentaba como activar soluciones Sandbox, y por ende características, utilizando

Blog del CIIN

# ¡Cómo leer información relativa a las soluciones Sandbox cargadas en SharPoint Online!@ Thursday, March 15, 2012 10:46 PM

En este artículo os comentaba como activar soluciones Sandbox, y por ende características

Blog técnico de Office 365

# SharePoint 2010: Resumen de posts (XXIX)!@ Sunday, April 1, 2012 4:13 PM

Como siempre , después del recopilatorio de enlaces interesantes sobre SharePoint 2010 os dejo el resumen

Blog del CIIN

# Office 365: Resumen de posts (IV)!@ Monday, April 2, 2012 10:03 AM

Ya tocaba hacer un primer resumen de posts sobre Office 365 publicados en el blog del CIIN desde el resumen

Blog del CIIN

# ¡Office 365: Resumen de posts (IV)! @ Monday, April 2, 2012 10:31 AM

Os dejo un primer resumen en 2012 de posts sobre Office 365 publicados en el blog del CIIN desde el resumen

Blog de Microsoft Office 365