sábado, 17 de diciembre de 2016

Comprobaciones antes de guardar (PreSaveItem)

Muchas veces me encuentro con el escenario en el que en una lista se precargan unos valores y luego a través del formulario de edición queremos que otra persona complete otros datos.
En esta situación puede ser que nos interese que algún campo sea obligatorio. Sí en una lista de SharePoint marcamos un campo como obligatorio, nos obligará a cargarle un valor cuando lo creamos, en lugar de edición. Para ello, podemos utilizar la función PreSaveItem:

function PreSaveItem()
{
var save = false;
var area = $( "[title*='Area'] option:selected" ).text();
var centro = $( "[title*='Centro'] option:selected" ).text();
if (area != '(Ninguno)' && centro != '(None)'){
            save = true;
}else if(area == '(Ninguno)'){
alert('Seleccione un área');
}else if(centro == '(None)'){
alert('Seleccione un centro');
}
return save;
}

Al incluir el código superior, lo que hacemos es llamar a una función en la que en este caso en concreto, lo que hacemos es recoger el valor seleccionado en dos campos de tipo elección a los cuales le hemos aplicado SPServices para que se comporten como campos en cascada.

Y comparamos que no tomen el valor (Ninguno) para el campo area, ni (None) para el campo centro, si tienen valor en esos campos, devolveremos el valor true, que permitirá que se guarde, sino devolverá false, lo que hará que no se guarden los valores y gracias a los alert mostraremos un mensaje al usuario indicándole que campo se le ha olvidado completar.

sábado, 12 de noviembre de 2016

Mostrando el nombre de un archivo y enlazarlo en una vista de datos

Anteriormente ya hice una entrada de como mostrar los iconos de los documentos en una vista de datos.

Pues bien, otra cosa que me suelen pedir mucho y que va justo detrás del icono, es mostrar el nombre del documento y el enlace al mismo. Lo que viene siendo el comportamiento propio de SharePoint.

Para una vista de datos, basta con poner:

<a href="{@FileRef}">
<xsl:value-of select="@FileLeafRef.Name" />
</a>

Y nos pondrá la URL relativa, lo cual es muy útil, si tenemos diferente URL desde dentro que desde fuera y nos mostrará el nombre del archivo sin acabar en la extensión.

domingo, 18 de septiembre de 2016

Detener la carga de una página durante X segundos para esperar por un flujo de trabajo

 Esta semana he tenido que hacer una relación entre dos listas y para enlazar la primera con la segunda he tenido que crear el enlace en una columna oculta con un flujo de SharePoint.

El problema era que se cargaba más rápido la nueva página donde se mostraba el enlace, que el flujo nos completará el campo.

La solución que se me ha ocurrido (posiblemente no sea la mejor), es meter en el head un retardo de 2 segundos y recargar la página. Con el fin de evitar un bucle infinito, una vez espero, le añado a la URL de la página el carácter #.

Lo que he hecho, ha sido buscar con el Designer en el código de la página el bloque:

<asp:Content ContentPlaceHolderId="PlaceHolderAdditionalPageHead" runat="server">

Y si no, bastaría con crearlo abriendo y cerrando la etiqueta. Para posteriormente incluir en él:

<SharePoint:ScriptBlock runat="server">
    
function sleep(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) {
    if ((new Date().getTime() - start) > milliseconds){
      break;
    }
  }
}
var url = window.location.href;
 if(url.indexOf("#")!= -1){
 
 }else{
  sleep(2000);
  var nurl = window.location.href;
  nurl = nurl+"#";
  window.location.href = nurl;
  location.reload();
 }


</SharePoint:ScriptBlock>

domingo, 21 de agosto de 2016

Recoger parámetro de la URL con Jquery y completar un campo de tipo búsqueda de SharePoint

 Siguiendo con la entrada anterior quedaría recoger el número de ID correspondiente al libro y dejarlo seleccionado en un campo de tipo búsqueda de la lista reserva de libros. Dicho campo, será un campo de búsqueda sobre la lista de libros, cogiendo como clave el nombre del libro.

Para ello tendremos una dirección del tipo:

https://URLdelSitio/Lists/ReservaLibro/Nuevo.aspx?Libro=1&Source=/URLdelSitioalquevolvertrasaceptar/

Este sería el libro con el ID=1 en la lista de libros.

Para ello, en formulario de nuevo elemento de la lista reserva de libros, debería añadir un editor de código fuente con el siguiente código:

<script>

$(document).ready(function(){

var direccion = window.location.href;

var numero = direccion.split('Libro=')[1];

                 numero = numero.split('&Source')[0];

$("[title='Libro'] option[value="+numero+"]").prop('selected', true);

$('#Book option:not(:selected)').attr('disabled',true);

});

</script>

Habiendo añadido el ID=Book al tr del campo de busqueda, en mi caso:

<td width="400px" valign="top" class="ms-formbody" id="Book">

<SharePoint:FormField runat="server" id="ff3{$Pos}" ControlMode="New" FieldName="Libro" __designer:bind="{ddwrt:DataBind('i',concat('ff3',$Pos),'Value','ValueChanged','ID',ddwrt:EscapeDelims(string(@ID)),'@Libro')}"/>

<SharePoint:FieldDescription runat="server" id="ff3description{$Pos}" FieldName="Libro" ControlMode="New"/>

</td>

El código JavaScript de arriba lo que hace es:

  1. Recoger la URL en la variable dirección
  2. Quedarnos con la parte tras la palabra 'Libro=' en la variable numero
  3. En esa misma variable, eliminamos la parte del redireccionamiento
  4. Buscamos el campo de tipo búsqueda, que en mi caso se llama Libro y seleccionamos el valor que hemos recogido de la URL en la variable numero
  5. Por último, deshabilitamos el resto de opciones diferentes a la que hemos pasado como parámetro del Libro.
Está última parte, fue para la cual tuve que buscar en Google, ya que si hacía que el campo estuviera en disable no me guardaba el valor correcto y al ser un campo de elección no tenía sentido ponerlo en readonly.
Encontré esta entrada, que me dio la solución:



domingo, 31 de julio de 2016

Completar campo de tipo hipervínculo con un flujo

Recientemente me han pedido realizar una lista de libros y otra lista para la reserva de libros.

Para la primera parte, simplemente he creado una listas con los campos correspondientes: Nombre, Autor, ISBN, Resumen y un campo oculto de tipo hipervínculo que se asoma en las vistas llamado inscripción.

Este campo llevará un enlace a la lista de reserva de libros, en el que se pasará el ID del libro en cuestión que se quiere reservar.

Para ello, crearemos un flujo que se lance cuando se crea o se modifica un elemento. En el que crearemos una variable de tipo Cadena, para guardar la URL del enlace:


Estableceremos en dicha variable, la URL del enlace al elemento de Nuevo de la lista reserva de libros, donde le pasaremos como parámetro el ID del elemento en la lista de libros y separaremos la URL del nombre a mostrar por una coma y un espacio en blanco (, ) como por ejemplo:
 

Finalmente bastaría con guardar la variable del flujo, en nuestro campo de tipo hipervínculo:


En la siguiente entrada continuo con la parte de recoger el parámetro pasado desde la lista reserva de libros, con Jquery.





sábado, 4 de junio de 2016

Ordenar una consulta al API REST por dos columnas


Recientemente teníamos una consulta al API REST de SharePoint 2013, la cual ordenábamos por un campo, pero recientemente nos han pedido ordenarlo por dos campos.

Tras buscarlo, hemos encontrado que la manera de hacerlo debiera ser:
var url = "https://misitio/_api/web/lists/getbytitle('Nombre%20de%20Lista')/items?$orderby=Tema,Orden&20asc";

Las fuentes que he utilizado para encontrarlo son:
https://docs.microsoft.com/en-us/sharepoint/dev/sp-add-ins/use-odata-query-operations-in-sharepoint-rest-requests

https://www.odata.org/documentation/odata-version-2-0/uri-conventions/#OrderBySystemQueryOption

sábado, 21 de mayo de 2016

Ocultar el Contenido de Sitio del menú lateral en SharePoint 2013

Recientemente nos han pedido que ocultemos de todo SharePoint 2013 de la navegación lateral la opción de "Contenidos del Sitio". Para ello, basta con incluir en la página maestra o una referencia a una hoja de estilos o a un archivo Javascript que contenga:

CSS:
.ms-core-listMenu-verticalBox a.ms-core-listMenu-item.ms-core-listMenu-heading[href*='viewlsts.aspx'] {
   display:none;
}

Javascript:
$(".ms-core-listMenu-verticalBox a.ms-core-listMenu-item.ms-core-listMenu-heading[href*='viewlsts.aspx']").attr("style","display:none");


sábado, 9 de abril de 2016

Ocultar el campo Título en una lista personalizada de SharePoint

Muchas veces cuando creamos listas en SharePoint son listas concretas, vemos que no nos sirven las plantillas de SharePoint, sino que es más fácil utilizar la plantilla de Lista personalizada e ir añadiendo las columnas necesarias.

En ese caso, hay veces que nuestra lista no necesita una columna de tipo una línea de texto. Pero para sorpresa nuestra, el Tipo de Contenido que tiene por defecto es Elemento, el cual tiene una columna Título de tipo una línea de texto.

Sí hacemos clic en la columna a través de la configuración de la lista, vemos que no nos permite eliminarla, sólo indicar si es obligatoria o no:



Para poder ocultarla podremos hacerlo desde el Tipo de Contenido pero para ello deberemos habilitar la administración de tipos de contenidos. Para ello, lo primero que haremos será acceder a la configuración de la lista y seleccionar la opción de Configuración avanzada:


En la ventana que nos lleva, deberemos seleccionar en la pregunta de permitir la administración de tipos de contenido:


Tras aceptar dicho cambio, podremos ver como nos aparece una nueva zona en la configuración de la lista, destinada a los tipos de contenidos, mostrándonos el tipo del contenido Elemento, sobre el cual podremos hacer clic:


Una vez dentro del tipo de contenido, podremos acceder a la columna Título, que es la columna que queremos ocultar:


Tras ello, ya podremos seleccionar, si la queremos requerida, opcional u oculta, como es nuestro caso:


Esto hará que la columna ya no nos aparezca en los formularios por defecto de la lista (NewForm, EditForm y DispForm).

Mi experiencia con esta columna, me recomienda completarla por medio de un flujo, con el ID o el valor que sea y mostrarla en las vistas de la lista. Ya que esta columna es la que contiene todo el menú de edición, que nos permite acceder al DispForm, EditForm, Flujos, Eliminar... Mientras que la columna Editar, sólo me permite acceder al EditForm:




sábado, 19 de marzo de 2016

Flujo de trabajo (tipo 2010) reutilizable en SharePoint 2013

En un cliente hemos montado la nueva versión 2013 y vistos nuestros antecedentes con ellos, nos han dado carta blanca para personalizarlo como nosotros veamos mejor. Así que una de las decisiones que hemos tomado, es trabajar con tipo de contenidos. Esto nos reporta una serie de mejoras que no tenemos si trabajos directamente creando las columnas biblioteca a biblioteca o lista a lista.

Una de esas mejoras, es poder utilizar flujos de trabajo reutilizables:


Si procedemos a crear un flujo reutilizable, una de las cosas que nos lo marcará sera el tipo de contenido al que lo asociamos. En nuestro ejemplo utilizaremos Dossier de Prensa:



Tras crear nuestro flujo con sus acciones y condiciones, lo guardamos y podremos ver que podemos decidir si queremos que puedan o no, lanzarlo a mano, automático cuando se crea o se modifica elementos.
También podemos observar que se engloba dentro de Flujo de trabajo reutilizable:

De hecho, SharePoint Designer nos lo mostrará como un flujo reutilizable globalmente, como son los propios flujos de SharePoint de Aprobación, Recopilación de firmas, etc...


Ahora sólo deberemos acceder con el navegador a las bibliotecas donde utilicemos ese tipo de contenido y desde el menú contextual superior de la biblioteca, elegiremos la opción de Agregar un flujo de trabajo:


Esto nos llevará a una página donde podremos seleccionar el tipo de contenido y tras ello, nos saldrá nuestro flujo, y por último sólo quedará definir que evento es el que nos lanzará nuestro flujo:


Resumen: Crear flujos reutilizables asociados a un tipo de contenido, nos sirve para poder gestionar cualquier cambio desde un único sitio y una única vez. El cómo encima de estas líneas.

sábado, 20 de febrero de 2016

Recordatorio de vencimiento de tareas (III)

En dos entradas anteriores, ya aborde este tema:
http://sharepointyamigos.blogspot.com.es/2012/07/recordatorio-de-vencimiento-de-tareas.html
http://sharepointyamigos.blogspot.com.es/2012/11/recordatorio-de-vencimiento-de-tareas-ii.html

Para que la directiva de información se lance todos los días, es cierto que hay que modificar dos tareas programadas de la administración central. Que por defecto, viene configuradas a que se lancen semanalmente y que debido a este tema, nos interesa modificar a que se lancen diariamente.

Estas tareas son "Information management policy" y "Expiration policy".

Para ello, accederemos a la administración central de nuestro SharePoint y en el menú lateral elegiremos la opción Monitoring:


En la página que se nos abre, deberemos eligir la opción Review job definitions, que se encuentra dentro del menú Timer Jobs:


Dentro de la cual, deberemos buscar las tareas programas  "Information management policy" y "Expiration policy". Para cambiar la periodicidad deberemos hacer clic en el nombre de la tarea deseada, de la aplicación web deseada:



Y una vez dentro, cambiaremos la periodicidad a diaria y pondremos la hora de inicio y la hora de fin de la tarea programada:


Haremos lo mismo para la tarea "Expiration policy" teniendo en cuenta que debe ejecutarse en el tiempo, tras la tarea "Information management policy":


Y solucionado, todo lo comentando en las entradas anteriores, serán validas y se ejecutarán todos los días.

IMPORTANTE:

La tarea Expiration policy debe ejecutarse tras la de Information management policy y ambas en el mismo día y por supuesto, que se ejecuten antes de la hora interesada de recibir los correos. 

sábado, 23 de enero de 2016

Eliminar una palabra de un texto en SharePoint

Esta entrada es una continuación de la anterior. En la anterior entrada vimos como era posible completar un campo de tipo hipervínculo. Todo ello, vino de una lista llamada Miembros que servía para guardar todos nuestros contactos de diferentes grupos y una lista llamada Grupos, que nos servia para nombrar los diferentes grupos y para tener un "botón" que al hacer clic en el, nos permitiera mandar un correo a todos los miembros de ese Grupo.

Obviamente la siguiente parte que tuvimos que implementar era el mecanismo de dar de baja miembros de dicho grupo. El eliminar elementos de la lista de Miembros, es básico, es eliminar un elemento de la lista. Pero eso no nos eliminaría el correo asociado de la lista Grupos donde esta el "botón".

Así que lo que hicimos fue crear una columna de tipo elección en la lista Miembros, que llamamos Eliminar miembro, cuyo valor por defecto es No. Pero que cuando el usuario quiera eliminar un contacto, simplemente tenga que cambiar a . También cree una columna oculta de tipo texto llamada Todos, donde guardaría los valores de todos los email que había bajo el "botón" Enviar Email. Adicionalmente cree 4 campos calculados sobre la lista  Miembros: SinCorreoI, SinCorreoD, SinCorreoPre y SinCorreo.

Estos campos calculados nos servirán para almacenar los correos que hay a la izquierda del correo que queremos eliminar y sin el "mailto:", la parte derecha que hay tras el correo a eliminar, concatenar ambas partes y finalmente comprobar si es vacío o si quedan correos tras eliminar el correo a borrar.

Para ellos utilizamos también un flujo de trabajo sobre la lista Miembros que se lanza cuando se modifica un elemento y que tiene la siguiente forma:


Ejecutaremos sólo el borrado, si seleccionan Eliminar miembro a .

Lo siguiente que hacemos es guardar en la variable del flujo EmailTodos de tipo cadena, los emails que hay en el "botón".


Lo siguiente que hacemos es rellenar la columna oculta Todos de tipo una línea de texto, de nuestra lista Miembros con el valor de la variable EmailTodos, que contendrá todos los emails:


Tras ello, entra en funcionamiento las columnas calculadas que hemos citado antes:

  • SinCorreoI: =RIGHT(LEFT(Todos;(FIND(Email;Todos)-1));LEN(LEFT(Todos;(FIND(Email;Todos)-1)))-7)
  • SinCorreoD: =RIGHT(Todos;(LEN(Todos)-((FIND(Email;Todos)+LEN(Email)))))
  • SinCorreoPre: =SincorreoI&SinCorreoD
  • SinCorreo: =IF(SinCorreoPre="";"@";SinCorreoPre)
Lo siguiente que hacemos es esperar a que se ejecute y se rellenen los campos calculados. Personalmente tuve que poner una espera hasta que en el campo hubiera una arroba, ya que la acción esperar 1 minuto, no terminaba nunca por un problema de configuración de nuestro SharePoint que debemos revisar. Sino hubiera sido más óptimo poner una espera de 1 minutos y la última columna calculada hubiera sido:

  • SinCorreo: =IF(SinCorreoPre="";"";SinCorreoPre)
No obstante el resto del flujo lo separe en un paso más para asegurarme de que estaban todos los valores asignados.

En el paso 2, lo único que hacemos es establecer la variable del flujo EmailTodosSin de tipo cadena con el valor de la columna SinCorreo.

Lo último que hacemos es volver a rellenar el "botón" tal y como vimos en la anterior entrada, con el valor de la variable, que puede contener correo o no. Y en ambos casos, lo último que hacemos el borrar el elemento de la lista Miembros que queríamos borrar.

RESUMEN:

En las acciones de flujos de SharePoint no existe una función que nos haga eliminar una palabra de un cadena de texto. Sólo existe en 2013 la opción de quedarnos con una parte a partir de una posición determinada. Entonces para ello, utilizaremos columnas calculadas.