¿Está una página en modo edición?

En muchas ocasiones me he preguntado si existe alguna forma de saber si una página esta en modo de edición o no, para personalizar el contenido que se muestra en cada uno de los modos. Navegando por internet, he encontrado como se hace, y la verdad es que es bastante simple, cuando se conoce.

Se puede utilizar desde una página o control de usuario en aspx, de modo que todo lo que esté dentro de la etiqueta sólo se mostrará si esta en el modo que se especifica en la propiedad.

<PublishingWebControls:EditModePanel runat=server id=»idEditPanel» PageDisplayMode=»Display»>

<!- – Controles definidos en esta zona se muestran en modo visualización – ->

</PublishingWebControls:EditModePanel>

 

<PublishingWebControls:EditModePanel runat=server id=»idEditPanel1″ PageDisplayMode=»Edit»>

<!- – Controles definidos en esta zona se muestran en modo edición- ->

</PublishingWebControls:EditModePanel>

Si se necesita realizar la comprobación mediante código fuente:

if( SPContext.Current.FormContext.FormMode == SPControlMode.Edit)

 Cabe destacar que en el caso de los webparts, será necesarios utilizar la propiedad de WebPartManager.DisplayMode

Dejo unos enlaces para ampliar la información.

WebPartManager.DisplayMode Property

SPControlMode Enumeration

Acceso a las Bases de Datos de MOSS/WSS

Comparto con vosotros esta información acerca de las bases de datos que se crean y llenan de datos con nuestros fantásticos sitios de SharePoint, pero que nunca se por donde cogerlas… 

Me apunto estos enlaces en el blog, para encontrarlos luego fácilmente…

Os dejo unos enlaces que pueden ser de utilidad:

Tablas de la Base de Datos de Contenido

Tablas de la Base de Datos de Configuración

Tablas de la Base de Datos

Procedimientos almacenados

¿Eliminar vs cerrar Web Parts?

Durante estos días he probado cuantos web parts se pueden añadir a una página de elementos web en SharePoint 2007, para ver si conseguía aclarar cuál es la diferencia entre cerrar (“X” en la esquina superior derecha del webpart) y eliminar un webpart de la página, dado que a efectos visuales para el usuario es el mismo…

Las pruebas las he realizado con web parts del tipo “Content Editor Web Part” añadiendo contenido a los mismos, en algunos casos, más contenido que en otros, pero todos ellos con contenido. Las recomendaciones del TechNet, indican que el número máximo recomendado de web parts por página no debe exceder 50 para no tener bajadas de rendimiento. Este número es una recomendación, ¿pero es real? Y, ¿qué indica? Tras realizar las pruebas pertinentes, comprobé que no se pueden añadir más de 50 webparts en una página mediante la interfaz gráfica, desde código podrían añadirse más pero provoca un error a la hora de visualización de la página.

De modo si no se pueden tener insertar más de 50, ¿por qué recomiendan no tener más de 50? La respuesta es sencilla, como todo en SharePoint tiene truco. No se pueden tener abiertos más de 50 webparts, pero no se cuentan los que están cerrados en la página que no se renderizan. Por tanto el principal problema que ocasionan estos Webparts cerrados es que se el contenido se sigue descargando de la base de datos, de modo que se realizan más peticiones de las necesarias, y el tiempo de carga aumenta notablemente, independientemente del número de Webparts que estén abiertos en la página. Si eliminamos los Webparts que ya no se utilizan en una página en lugar de dar al aspa “X” evitaremos que los Webparts que no se utilizan se carguen innecesariamente en nuestra página, mejorando el rendimiento de la misma.

Para comprobar los elementos web que tenemos añadidos a una página sólo tenemos que añadir “?contents=1″ en el querystring después del nombre de nuestra página (http://servidor/default.aspx?contents=1), de esta forma se puede acceder de forma rápida a la página de mantenimiento de elementos web, mediante la cual podremos eliminar los Webparts cerrados, entre otras opciones.

Impersonación sharepoint

Debido al modelo de seguridad de MOSS/WSS y a las personalizaciones que se realizan mediante el desarrollo de nuevos webparts, interfaces,.. podemos necesitar realizar operaciones que el usuario final no tiene permisos para realizar, por ejemplo, acceder al perfil de un usuario, agregar elementos a listas con un usuario lector, que cualquier usuario pueda activar features desde un webpart… (aunque en la mayoría de los casos, suele tratarse de operaciones más complejas).

Esta tarea, era más compleja en SharePoint 2003, pero ahora gracias al nuevo modelo de objetos y los métodos que ofrecen, podemos realizar una impersonación mediante la cual podremos ejecutar código que realice acciones para las cuales el usuario actual no tiene permisos. El modelo de objetos proporciona dos formas de realizar esta tarea:

·    Utilizando la cuenta del sistema: ejecutaremos el código con los permisos que tiene la cuenta del sistema en el sitio (FullMask), de modo que podremos realizar todas las acciones.

·    Utilizando los datos de otro usuario: ejecutaremos el código con los permisos que tiene la cuenta del usuario utilizado en el sitio.

A continuación veremos que se realiza esta tarea y algunos aspectos a tener en cuenta.

En el primer caso, impersonar utilizando la cuenta del sistema, vamos a crear un nuevo elemento de una lista con un usuario lector. Para realizar la impersonación utilizamos el método RunWithElevatedPrivileges de la clase estática SPSecurity.


SPSecurity.RunWithElevatedPrivileges(delegate()
{
 using (SPSite miSitio = new SPSite(&amp;quot;http://urlServidor&amp;quot;))
 {
  miSitio.AllowUnsafeUpdates = true;

  using (SPWeb miWeb = miSitio.OpenWeb())
  {
   miWeb.AllowUnsafeUpdates = true;
   SPList miLista = miWeb.Lists[&amp;quot;Pruebas&amp;quot;];
   SPListItem elemento = miLista.Items.Add();
   elemento[&amp;quot;Title&amp;quot;] = &amp;quot;PRUEBA CON USUARIO LECTOR&amp;quot;;
   elemento.Update();
   miweb.AllowUnsafeUpdates = false;
  }
  miSitio.AllowUnsafeUpdates = false;
 }
});

En el caso de querer utilizar otro usuario, se puede realizar la impersonación de forma similar:

SPUser miUsuario = SPContext.Current.Web.AllUsers[&amp;quot;miDominio\ccanov&amp;quot;];
SPSite sitioImpersonado = new SPSite(&amp;quot;http://urlServidor&amp;quot;, miUsuario.UserToken);
SPWeb webImpersonada = sitioImpersonado.OpenWeb();

En ambos casos, si se obtiene el usuario llamando al método CurrentUser del objeto SPWeb impersonado, se obtendrá el usuario con el que se ha impersonado, siendo en el primer “SHAREPOINT\SYSTEM” y en el segundo “miDominio\ccanov”, mientras que si se realiza la llamada al objeto del contexto SPContext.Current.Web.CurrentUser, el usuario será el que está accediendo realmente a la página.

Hay que realizar siempre el Dispose de los objetos que se han utilizado para la impersonación, ya sea mediante el uso de using (primer ejemplo) o mediante el uso del método Dispose (segundo ejemplo).

En el caso de tener que realizar modificaciones, ya sea de listas, elementos de listas, páginas,… hay que poner la propiedad AllowUnsafeUpdates a true, antes de realizar la acción, y ponerla a false después, para esto, es recomendable hacer uso de finally e introducir todo el código dentro de un try-catch, para evitar que esta propiedad se quede con el valor a true.

Estructura de sitios utilizando PortalProvissioningProvider

Cuando se trabaja con definiciones de sitio en WSS/MOSS se tiende a pensar que únicamente nos proporcionan la base para la creación de sitios con una determinada estructura de listas/bibliotecas y elementos básicos de configuración.

En una próxima entrada comentare como crear una definición de sitio, y muchas de las features no documentadas por Microsoft, que permiten realizar la mayoría de las opciones que se muestran en la página de “Configuración de Sitio”. En esta entrada, veremos que utilizar las definiciones que proporciona microsoft, para crear una colección de sitios con subsitios en varios niveles, empleando únicamente los ficheros xml de configuración.

La estructura que crearemos será la siguiente:

Estructura

Una vez tenemos clara la estructura que vamos a crear, únicamente necesitamos crear 2 ficheros xml:

webtempMySiteProvider.xml: (ubicado en 12/TEMPLATE/3082/XML) en este fichero, definiremos la definición de sitio que se va a emplear. No hay que crear una definición de sitio como tal, lo que pretendemos es que aparezca en la página de creación de sitios. A continuación se muestra el contenido del fichero:

 <Templates xmlns:ows=»Microsoft SharePoint»>
<Template Name=»MySiteProvider» ID=»10900″>
    <Configuration ID=»0″ Title=»Estrucutura desde xml» Hidden=»FALSE» ImageUrl=»/_layouts/images/stsprev.png» Description=»Definicion que creaa toda la estructura de la coleccion de sitios» ProvisionAssembly=»Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c» ProvisionClass=»Microsoft.SharePoint.Publishing.PortalProvisioningProvider» ProvisionData=»xml\estructura.xml» RootWebOnly=»TRUE» DisplayCategory=»MiTopologia» />
   </Template>
</Templates>

Como se puede ver, aparentemente se trata de una definición de sitio básica, salvo por las propiedades:

  • ProvissionAssembly: especifica que librería se utilizará para el aprovisionamiento del sitio.
  • ProvissionClass: especifica que clase se utilizará para el aprovisionamiento del sitio.
  • ProvisionData: especifica el fichero que proporciona los datos para el aprovisionamiento. En nuestro caso estructura.xml

estructura.xml: (ubicado en 12/TEMPLATE/3082/XML) en este fichero se especifica la estructura que hay que crear, utilizando el esquema PortalSchema.xsd. Mediante el uso de y especificaremos los sitios y subsitios que se van a crear. El fichero estructura.xml, con la estructura especificada en la imagen, quedaría de la siguiente forma: 

<?xml version=»1.0″ encoding=»utf-8″ ?>
<portal xmlns=»PortalTemplate.xsd»>
  <web name=»Inicio» siteDefinition=»CMSPUBLISHING#0″ displayName=»Inicio» description=»Inicio» >
    <webs>
      <web name=»sitio1″ siteDefinition=»STS#0″ displayName=»Sitio 1″ description=»Sitio 1″/>
      <web name=»sitio2″ siteDefinition=»MPS#0″ displayName=»Sitio 2″ description=»Sitio 2″/>
      <web name=»sitio3″ siteDefinition=»STS#1″ displayName=»Sitio 3″ description=»Sitio 3″/>
       <webs>
        <web name=»blog» siteDefinition=»BLOG#0″ displayName=»Blog» description=»Blog»/>
        <web name=»wiki» siteDefinition=»WIKI#0″ displayName=»Wiki» description=»wiki»/>
       </webs>
      </web>
      <web name=»sitio4″ siteDefinition=»SRCHCEN#0″ displayName=»Sitio 4″ description=»Sitio 4″/>
      <web name=»sitio5″ siteDefinition=»SPSREPORTCENTER#0″ displayName=»Sitio 5″ description=»Sitio 5″/>
    </webs>
  </web>
</portal>

 En el caso del ejemplo, se utilizan las definiciones de sitio que proporciona Microsoft, pero del mismo modo se puede utilizar con las definiciones que creemos, y de ésta forma, podremos crear la estructura que necesitemos empleando nuestras definiciones de sitio.

Problemas con eventos en MOSS/WSS

En algunas ocasiones, necesitamos generar un fichero (por ejemplo, zip con adjuntos de elementos de las listas, un pdf…)  cuando se pulsa un botón. En mi caso, se trataba de obtener un fichero comprimido con los adjuntos de los elementos que se muestran como resultado de la búsqueda, para una única página. Realice múltiples pruebas y siempre funcionaba todo correctamente hasta que añadí el webpart a SharePoint. En ese momento, comenzó a suceder una cosas muy rara: únicamente podía pulsar una vez el botón. Cuando me devolvía el fichero, el resto de la página no respondía a los eventos. Realice pruebas de nuevo con el mismo webpart fuera de SharePoint y todo funcionaba correctamente.

Este problema se debe a que cuando se envía el formulario, se llama a la función WebForm_OnSubmit y desde ahí a la función _spFormOnSubmitWrapper (en el init.js).  Precisamente en ésta función se verifica si la variable _spFormOnSubmit se ha puesto a true, y en ese caso, se cancelan las peticiones de envió. Imagino que este mecanismo evita que se produzcan múltiples envíos del formulario ante un doble click del usuario.

La solución consta de dos pasos:

·    Asignar en el evento del botón (en el lado del cliente) a: 

exportRequested=true;

·    Añadir las siguientes líneas en el evento de carga (page_load) del control de usuario/pagina/webpart

string beforeSubmitJS = &quot;var exportRequested = false; &quot;;
beforeSubmitJS += &quot;var beforeFormSubmitFunction = theForm.onsubmit;&quot;;
beforeSubmitJS += &quot;theForm.onsubmit = function(){ &quot;;
beforeSubmitJS += &quot;var returnVal = beforeFormSubmitFunction();&quot;;
beforeSubmitJS += &quot;if(exportRequested == returnVal){_spFormOnSubmitCalled=false; exportRequested=false;}&quot;;
//beforeSubmitJS += &quot;alert(returnVal + ‘\n’ +_spFormOnSubmitCalled);&quot;;
beforeSubmitJS += &quot;return returnVal;&quot;;
beforeSubmitJS += &quot;};&quot;;
//beforeSubmitJS += &quot;alert(theForm.onsubmit);&quot;;
this.Page.ClientScript.RegisterStartupScript(this.GetType(),&quot;alterFormSubmitEvent&quot;, beforeSubmitJS, true);

Espero que os ayude.

SPUtility, la gran desconocida

Muchos somos los que desarrollamos soluciones para MOSS o WSS y en ocasiones, damos muchas vueltas para obtener información o realizar acciones que ya existen por defecto pero no lo sabemos. En esta entrada, vamos a ver algunas de las utilidades que proporciona la clase SPUtility, hay muchas más y en función de las necesidades de cada uno, tendrán o no sentido, pero en estas son algunas de las que he utilizado hasta la fecha.

Para hacer uso de esta clase, es necesario incluir la siguiente línea:

using Microsoft.SharePoint.Utilities;

Antes de comentar algunos de los métodos que proporciona, destacar que una de las ventajas de esta clase, es que al tratarse de una clase estática, no necesitamos instanciarla, lo cual facilita el acceso desde cualquier parte de nuestro código. A continuación describo la utilidad y como utilizar algunos de los métodos de ésta clase:

·    EnsureSiteAdminAccess: éste método permite validar si el usuario actual es administrador del sitio al que accede. Existen otros métodos para validar esto, pero lo interesante es que en este caso, si no se trata del un usuario administrador, aparecerá la ventana de login tres veces, dando la opción al usuario a acceder con otra cuenta. En caso de que el usuario no tenga acceso, se le enviará a la página de acceso denegado. En el código del ejemplo, se utiliza un control de usuario que estará cargado en un webpart.

protected void Page_Load(object sender, EventArgs e)
{
    SPUtility.EnsureSiteAdminAccess(SPContext.Current.Web);
    //Resto del código
}

·    GetGenericSetupPath: ¿quién no ha necesitado alguna vez la famosa ruta hasta el directorio 12? Este método nos devuelve la ubicación del directorio de instalación, en la mayoría de los casos es c:Program Files…….12 pero no queda demasiado bien dejar el código con estas ruta y si cambia necesitaremos realizar modificaciones. En el caso del ejemplo, obtenemos la ruta hasta la carpeta features.


string directorioFeature= SPUtility.GetGenericSetupPath(&amp;quot;template\features&amp;quot;);


SendEmail: permite enviar correos electrónicos haciendo uso de las configuraciones de la granja.


string subject = &amp;quot;Correo electronico de notificación de &amp;quot; + SPContext.Current.Web.Title;
string body = &amp;quot;El cuerpo del correo electronico a enviar&amp;quot;;

SPUtility.SendEmail(SPContext.Current.Web, false, false, &amp;quot;miemail@miemail.com&amp;quot;, subject, body);

·    TransferToErrorPage y TransferToSuccessPage:permiten redirigir a las páginas por defecto de error o bien de operacón realizada con éxito.

Evidentemente no son todos los métodos ya que la idea principal es ver como se pueden utilizar éstos métodos.

Finalmente, recomiendo que visitéis en el SDK los métodos y propiedades que proporciona ésta clase, aunque aviso que no todos están documentados.

SDK Clase SPUtility

 

Controles Web SharePoint

Durante estos días he tenido que modificar diseños de páginas y crear formularios personalizados para rellenar todos los campos de una biblioteca de páginas. Antiguamente, creaba un formulario personalizado donde añadía etiquetas y cuadros de texto, DropDownList y demás elementos para permitir rellenar el formulario.

Esta forma de programación está muy extendida y es completamente funcional, pero no es la más óptima, dado que cualquier cambio en el tipo de columna de una lista, puede generar numerosos cambios en el formulario (por ejemplo, en un campo de búsqueda, deberíamos obtener los elementos, rellenar el combo, añadir eventos….). Gracias a los controles web de SharePoint, sería suficiente con cambiar el control, y automáticamente realizaría los cambios necesarios.

Para utilizar estos controles, es necesario registrar la librería en la página, webpart o control de usuario dónde creemos el formulario.

<%@ Register Tagprefix=»SharePointWebControls» Namespace=»Microsoft.SharePoint.WebControls» Assembly=»Microsoft.SharePoint, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c» %>

 

Una vez tenemos registrada la librería, se puede hacer uso de estos controles del mismo modo que cualquier otro control asp, html… Por ejemplo, para añadir un control que permita escoger el valor de una columna de búsqueda de nuestra lista, denominado “Departamento”

<SharePointWebControls:LookupField runat=»server» id=»idDepartmentoField» FieldName=»Departamento»/>

Este es sólo un ejemplo de uno de los controles existentes en esta librería, cabe destacar los controles que permiten subir ficheros, o botón guardar, editar,… o bien visualizar información de los elementos de la lista.

A continuación muestro una tabla donde aparecen los controles más utilizados en función del tipo de columna.

webcontrols

Para información detallada de éstos controles, pueden consultar:

Microsoft.SharePoint.WebControls Namespace

¿Qué utiliza un page layout?

Para eliminar un page layout es necesario eliminar previamente todas las páginas que lo utilizan. Puede que debido a las dimensiones del portal, o bien por no recordar dónde utilizamos ese layout para hacer una prueba, no sepamos que páginas lo utilizan y al eliminarlo se produzca el siguiente error: «This item cannot be deleted because it is still referenced by other pages».

SharePoint ofrece la funcionalidad de listar todos los elementos que utilizan un page layout, y después de dar mil vueltas por el portal, la he encontrado. Como se puede ver puede ver en las imágenes, esta funcionalidad muestra que elementos usan un page layout como estructura de la página, pero además muestra que elementos componen dicho page layout.

Para acceder a esta funcionalidad, hay que seguir los siguientes pasos:

1.- Menú Acciones de Sitio -> Administrar Contenido y Estructura

2.- Pulsar sobre la carpeta «Galería de Páginas Maestras»

3.- Seleccionar el layout del cual queremos obtener la información y pulsar sobre «Mostrar recursos relacionados».

A continuación se cargará la siguiente página:

En la parte inferior, se puede observar que elemento utiliza el layout seleccionado, y que páginas utilizan este layout.

SPDisposeChecker desde Visual Studio

Recientemente publiqué una entrada que explicaba como se puede utilizar la herramienta SPDisposeCheck desde la consola, pero resulta que he encontrado una forma para poder ejecutarlo desde el Visual Studio (ya no hay excusas para no pasar esta herramienta por nuestro código).  Para hacer uso desde visual studio, y teniendo instalada la herramienta, tenemos que ir al menu Tools (o herramientas) y pulsar sobre External Tools (herramientas externas). Una vez hay, tenemos que crear una nueva y configurarlo de la forma que se indica en la imagen.

externaltools

Una vez este configurado, pulsamos en Apply y luego en Ok.

A partir de ahora, en el menú Tools (Herramientas) encontramos una opción SPDispose Check, la cual lanzara el SPDisposeCheck sobre la dll que se genera en nuestro proyecto y la salida de la herramienta, la tenemos en la ventana de salida de nuestro visual studio.

A continuación se muestran dos ejemplos con el código de la entrada anterior donde se pueden ver los resultados.

Salida VS con errores

Salida VS sin errores

En fin, ya no hay excusas.