jueves, 26 de septiembre de 2013

SP2010: Error en WF

Hoy estaba comprobando como un workflow de SP 2010 que creé en SharePoint Designer 2013 para una granja de SharePoint 2013 estaba dando continuamente error.

En la lista de historial del flujo de trabajo tan solo veía un mensaje así:

"Flujo de trabajo cancelado   Cuenta del sistema   Cuenta del sistema ha cancelado el flujo de trabajo (nombre del flujo)"
"Error   Cuenta del sistema   No se ha podido iniciar (nombre del flujo)"

Me extrañó muchísimo porque el flujo siempre había estado funcionando perfectamente hasta entonces, así que miré en el log de SharePoint, donde encontré un mensaje que empezaba tal que así:

"RunWorkflow: System.ArgumentNullException: Array cannot be null.  Parameter name: bytes     at System.Text.Encoding.GetString(Byte[] bytes)..."

Estuve volviéndome loco un rato hasta que, como suele pasar, google me dio la solución.

Efectivamente, había estado haciendo un retoque en el flujo con el Designer y modifiqué manualmente el archivo ".xoml" del mismo... y lo dejé "check out". Vale, fue un descuido, pero el error que da el flujo tampoco es muy explícito...

Se soluciona haciendo un check in del .xoml y todo vuelve a funcionar sin problemas.

Si esta entrada sirve para que alguien más no se vuelva loco, me quedaré muy contento.

¡Salud!

miércoles, 11 de septiembre de 2013

SP2013: Ocultar elementos según rol (permisos) de usuario

Otra cosa que suelen demandar los clientes es que determinadas zonas de la pantalla no se vean para determinados usuarios, según el rol de permisos que estos tengan.

Por ejemplo, que la rueda dentada de configuración (antes "Acciones del Sitio"), solo la puedan visualizar los Administradores del Site, pero no los usuarios colaboradores del mismo.

¿Cómo se consigue esto? Con la instrucción SPSecurityTrimmedControl podremos hacerlo fácilmente. Tan solo hay que editar la página que contenga la zona a ocultar (con SharePoint Designer, por ejemplo), e incluir dicha zona dentro de la siguientes líneas de código:

 <Sharepoint:SPSecurityTrimmedControl runat="server" PermissionsString="ManageWeb">
 (...Código HTML...)
 </SharePoint:SPSecurityTrimmedControl>

Con esto conseguiremos que tan solo los usuarios con el nivel de permisos de "Manage Web", vean lo que hay en medio del código.

El parámetro PermissionsString es el que marca el nivel de permisos que ha de tener el usuario para visualizar el código que estamos acotando. Si queréis saber cuales son los posibles valores para este parámetro, consultad este listado. (recomiendo que lo consultéis, porque no es tan evidente, si por ejemplo ponéis "FullControl" no funciona, ha de ser "FullMask")

Si, por ejemplo lo introducimos en la masterpage del site, justo entre el control de "siteactiontd":

<Sharepoint:SPSecurityTrimmedControl runat="server" PermissionsString="FullMask">
<span class="ms-siteactions-root" id="siteactiontd" >
...
</SharePoint:SiteActions></span>
</SharePoint:SPSecurityTrimmedControl>

Conseguiremos que los Administradores (Full Control) vean esto:



Pero el resto de roles, solo vean esto:

¡¡Muy útil!! ¿No? :-D

SP 2013: Ocultar botones de la cinta de acciones (Ribbon) mediante CSS.

Quien ha tenido un cliente en un proyecto de SharePoint, sin duda habrá tenido algún requerimiento del tipo: "Ese botón no lo debería ver el usuario. ¿Se puede ocultar?". La respuesta, siempre es la misma: "Todo se puede, pero todo tiene un coste...". Afortunadamente para todos, en SharePoint 2013 el coste de este tipo de personalizaciones es mínimo, ya que puede ocultarse cualquier botón o sección de la cinta de acciones (Ribbon) de forma muy rápida y sencilla.

De hecho, existen diversos métodos para ocultar dichos botones: programáticamente, mediante jquery en CEWP... pero el que expondré hoy aquí es el más sencillo de todos y rápido de ejecutar:

Vamos a ocultar, por ejemplo, el botón de "Ver todas las páginas" que hay en la sección "Biblioteca de páginas" de la pestaña "Página".

Primero vamos a necesitar obtener el ID del botón o sección de la Ribbon que queremos ocultar. Esto lo conseguiremos con las developer tools de IE o Firefox. Para el ejemplo me baso en IE 10. En IE hay que pulsar F12 para que estas aparezcan. Una vez las tengamos activadas, tenemos que seleccionar el botón en questión (hay que pulsar previamente el botón del icono del puntero de ratón en la pestaña HTML). Al seleccionarlo veremos que en el editor se nos abre el código HTML del botón. Tendremos que subir hasta el atributo <a y apuntar bien el ID que nos ofrece. En este caso, como se aprecia en la siguiente imagen, el ID del botón es "Ribbon.WikiPageTab.LibrarySettings.ViewAllPages-Large"


Ahora solo tendremos que abrir el fichero CSS que deberíamos tener personalizado para nuestro site (con SharePoint Designer, por ejemplo). Habitualmente el fichero estará en "\Style Library\CSS"

En caso de no tener un CSS personalizado (vamos hombre, todos los clientes quieren personalizar algo del Look&Feel!!) tendríamos que crearlo y vincularlo desde nuestra masterpage con una línea dentro del "head" del tipo:
<SharePoint:CssRegistration name="/Style Library/CSS/OurCustomCSS.css" runat="server" after="SharepointCssFile" />
Una vez estemos editando nuestro CSS personalizado, tan solo tendremos que añadir una entrada para el ID del botón que hemos apuntado, teniendo en cuenta que hay que insertar una contrabarra antes de cada punto del ID. El código completo sería así:

#Ribbon\.WikiPageTab\.LibrarySettings\.ViewAllPages-Large {
 display:none;
}
Al guardar correctamente el CSS (recordar hacer "check in" y guardarlo como "versión principal"). El botón ya no debería aparecer más allá donde tengamos aplicado nuestro CSS.


¡¡Adiós botones "molestos" que nadie quiere!! :-D

miércoles, 14 de agosto de 2013

SP 2013: Content Editor Web Part Multi Language

Recientemente tuve un cliente que, tras valorarle un proyecto y aceptar el trabajo, en la primera reunión de análisis funcional nos comentó (como quien no quiere la cosa) que "el portal debe ser multiidioma". Mi primera reacción fue de sudor frío... aunque como era un proyecto en SharePoint 2013 dejé la posibilidad de que "alguna de las novedades" de la nueva versión pudiera salvarme el pellejo. Mi alivio fue realmente importante cuando comprobé, que, efectivamente, 2013 trae alguna novedad muy "jugosa" al respecto: A parte de que los títulos de todos los webparts cambian con la MUI ¡¡El Content Editor Web Part es multi-idioma!!

¿Qué significa que el Content Editor Web Part es multi-idioma? Pues sencillamente, que si tu usas ese webpart (y solo ocurre en ese concreto) en una página en concreto y le introduces un contenido en el idioma principal, cambias a idioma secundario y lo modificas, tendrás 2 contenidos distintos, uno para cada idioma. Lo bueno es que hablamos de cualquier tipo de contenido que le introduzcas: Texto, imágenes, tablas, código JavaScript...

Para que os hagáis una idea de lo "potente" que es esto, os pongo un par de ejemplos:

El primero es un CEWP situado debajo de un formulario de nuevo ítem en una lista personalizada (formulario de solicitud):


El segundo es parte del contenido de una homepage, que contiene unos enlaces iconográficos a diversos contenidos (solicitudes). En este caso el texto forma parte de la propia imagen (.jpg):


Impresionante, ¿no? A partir de ahora, cuando un cliente requiera que ciertas pantallas sean multi-idioma ya no tendremos como única opción las tediosas "variaciones de sitio" o complejas soluciones programáticas. Si el contenido encaja bien en un CEWP ¡¡Pensad en ello!!

Y os preguntaréis (conociendo SharePoint)... ¿Dónde está el pero? Básicamente le veo 2 contras:

1.- Ha desaparecido el control de la MUI por interface de usuario. Es decir, cambiar el idioma de la página ya no es tan sencillo como ir al control del usuario, desplegar las opciones y señalar que se quiere un idioma distinto. Ahora el idioma de la MUI lo marca única y directamente el propio navegador. Esto significa que para cambiar el idioma el (pobre) usuario, tendrá que ir a las opciones del navegador, buscar la característica del idioma. Añadir el idioma deseado, situarlo como idioma preferente, salir de las opciones y refrescar la página. ¡¡ Muy tedioso, sí!! Respecto quien tuvo la fabulosa idea de eliminar el control del idioma desde el propio control de usuario... no tengo la respuesta, aunque está claro que los chicos de Microsoft no acaban de entender lo que los no-americanos entendemos por multi-idioma. Además, por lo que he buscado en la web, no hay manera de resolver esto de forma programática (no se puede acceder al control de la MUI desde código).

2.- Esto es un arma de doble filo. Imaginaos que un portal construido con CEWP es administrado por personal no demasiado experto en SharePoint 2013 (as usual) y empieza a modificar el contenido de los mismos sin tener en cuenta que lo que cambia en un idioma no cambia en el resto porque son contenidos totalmente diferenciados. El resultado puede ser un desastre de desfase de versiones entre diversos idiomas, o una complejidad administrativa extra no-deseada. Es por eso motivo que en SharePoint 2013 hay que utilizar el CEWP con mucha cabeza y explicándole muy bien al cliente cómo funciona este.

Por mi parte, la función multiidiomática del Content Editor Web Part ha sido un descubrimiento fascinante que ha permitido implantar un proyecto de forma rentable cuando tenía toda la pinta de irse directo a números rojos.

Pues eso es todo... a jugar con el CEWP 2013... ¡¡Con cabeza!!

SP 2013: Controlar los botones de "Cancelar" de un formulario.

Cuando trabajas en SharePoint 2013 y generas una lista personalizada para un formulario de solicitud, puedes hacer que el acceso a dicho formulario se realice mediante un enlace desde la página principal (por ejemplo) y pasarle un parámetro en la URL (Source) para que el usuario, al finalizar, sea reenviado a una página donde mostremos un mensaje del tipo "su solicitud se ha enviado correctamente, pulse aquí para continuar.". El ejemplo de una URL de este tipo sería:

/NewForm.aspx?Source=/SitePages/requestOK.aspx

Sin embargo aquí vamos a tener un problema: ¿Qué pasará si un usuario, en lugar de enviar el formulario lo cancela? Pues que, debido al parámetro introducido en la URL, también será redirigido a la página requestOK.aspx, con lo que, a pesar de su cancelación, se le mostrará el mensaje "su solicitud se ha enviado correctamente, pulse aquí para continuar.".

¿Cómo se puede evitar esto y dar una funcionalidad diferente al botón de "Cancelar" que al de "Guardar"? La ayuda más fácil en este caso, nos la da jquery y, por suerte, SharePoint 2013 tiene integración nativa con el mismo.

Antes de introducir el script en jquery, es importante comentar que:
  • Los 2 botones "Cancelar" (el de la Ribbon y el del formulario) están en un tag de tipo <input>
  • Dicho input tiene un valor de atributo que cambia según el idioma (en inglés es "Cancel", en Español "Cancelar", en catalán "Cancel·lar"...)
Entonces, para conseguir el control de los botones de "Cancelar", necesitaremos seguir los siguientes pasos:

  1. Acceder al formulario de "New Ítem" o "Edit Ítem", mediante la opción de la Ribbon de la pestaña "Lista" en la sección "Personalizar Lista"
  2. Añadir un nuevo Web Part a la página de tipo "Editor de secuencias de comandos"
  3. Una vez el webpart esté insertado en nuestra página, añadirle el siguiente fragmento de código (por ejemplo):
 <script type="text/javascript" src="/Style%20Library/JS/jquery-1.10.2.min.js"> </script>
  <script type="text/javascript">
     $(function() {
      $('input[value=Cancelar]').click(function() {history.go(-1);});
      $('input[value=Cancel]').click(function() {history.go(-1);});
      $('input[value=Cancel·la]').click(function() {history.go(-1);});
    });
</script>
A continuación explico un poco cada paso del script:

La primera línea invoca a jquery, que en SharePoint 2013 viene incluido por defecto en la carpeta /Style Library/JS, pero que podemos descargar nosotros y ponerla en cualquier otra ubicación que nos convenga más y deseemos utilizar.

Usamos "$(function() " porque así esperamos a que la página esté completamente cargada antes de hacer nada.

Usamos "$('input[value=Cancelar]').click(function() {" para capturar la pulsación que el usuario haga sobre cualquiera de los 2 botones de Cancelar de la página.

Usamos "history.go(-1);" para indicar, que, sencillamente, tras la pulsación del botón de Cancelar, la aplicación retorne a la página anterior donde estábamos antes de acceder al formulario (hace un back en la navegación del explorador). También se podría usar aquí alguna función para navegar a una URL específica o cualquier otro tipo de funcionalidad.

He añadido 3 líneas idénticas con identificadores en 3 idiomas distintos, porque en mi caso, trabajé con una aplicación multiidioma, donde según el lenguaje de la MUI, el botón tenía el valor del atributo distinto. Esto me dio algún quebradero de cabeza, pero se soluciona sin problemas contemplando todas las opciones idiomáticas de la forma indicada.

¡¡Y eso es todo!! Lo cierto es que, bajo mi punto de vista, que SharePoint 2013 venga con compatibilidad para JavaScript y jquery por defecto, abre un gran abanico de posibilidades al control del formato y eventos de cada página de nuestro portal, y, lo que es mejor ¡De una forma relativamente sencilla!

(thanks to Paul Galvin: http://www.sharepointbriefing.com/spcode/article.php/3865791/Take-Control-of-Your-OK-and-Cancel-Buttons.htm)