SharePoint 2010. Enlace de Inicio Rápido con JavaScript

Ahora que SharePoint 2010 utiliza ventanas modales, se nos plantea la posibilidad de poder crear enlaces en el Inicio Rápido (Quick Launch) utilizando JavaScript, y no un enlace http, que nos permite abrir un formulario pero en una ventana modal. Así que vamos a ver qué posibilidades tenemos y como lo podemos implementar sin programación.

El Inicio Rápido o Quick Launch es el menú lateral que nos permite tener enlaces a listas, biblioteca de documentas y demás.

sharepoint2010_quicklaunch_1

Para personalizar este menú y añadir nuestros enlaces personalizados tenemos una opción en la configuración del sitio que nos permite crear encabezados y enlaces.

sharepoint2010_quicklaunch_2

Una vez en la página de configuración, nos creamos un Nuevo vínculo de navegación con la función JavaScript que nos permite abrir una url en un diálogo de SharePoint.

sharepoint2010_quicklaunch_3

javascript:SP.UI.ModalDialog.ShowPopupDialog(‘http://sitio/_layouts/listform.aspx?PageType=8&ListId=IdDeLaLista}&RootFolder=’);

Y obtenemos un resultado parecido al siguiente.

sharepoint2010_quicklaunch_4

¿Qué pasa con los sitios de Publicación?

Cuando activamos la característica que nos permite tener un sitio de publicación nos encontramos con que desaparece el menú de configuración de Inicio Rápido y nos encontramos con un menú de configuración de la Navegación de la colección de sitios.

sharepoint2010_quicklaunch_5

Este menú de Navegación no nos permite incluir enlaces JavaScript en el Inicio Rápido de nuestro sitio, realiza una validación asegurándose que escribimos una url válida.

sharepoint2010_quicklaunch_6

Sin embargo, podemos navegar hasta la página de configuración del Inicio Rápido aunque no tengamos la opción en la configuración del sitio. Nos basta con ir a la página que nos permite configurar este menú, _layouts/quiklnch.aspx, y desde esta crear nuestro enlace con la funcionalidad JavaScript que necesitamos.

 

Saludos a todos…

SharePoint 2010. Informes con listas

Una de las ventajas que siempre le he encontrado a SharePoint es la posibilidad que le ofrece a los usuarios para poder crearse sus cosas ellos solos. Que se puedan crear sus sitios de colaboración, sus listas, bibliotecas de documentos, etc., aunque siempre nos ha faltado la posibilidad de que se puedan crear informes sobre los datos de las listas con sus gráficos y demás.

Damos un paso adelante con Report Builder 3.0 y nos encontramos con un nuevo origen de datos que nos permite conectarnos a listas de SharePoint.

sharepoint_2010_reportbuilder_1

Una vez creado el origen de datos con la url de nuestro sitio (no la url de la lista), nos permite seleccionar la lista que queremos consultar y los filtros de la consulta.

sharepoint_2010_reportbuilder_2

A partir de aquí seguimos con el diseño del report y seleccionamos los campos a mostrar y la agrupación por filas o por columnas, el modo de agrupación, los colores y demás.

Una vez que tenemos nuestro report diseñado, sólo nos queda publicarlo en el servidor de informes de nuestro SharePoint (si lo tenemos integrado) y ya lo tenemos accesible para que el resto de usuarios pueda ejecutar el informes.

Siempre he creído que los usuarios deben de ser autosuficientes, que tienen que tener la formación necesaria para poder crear sus elementos de colaboración sin necesidad de un experto. SharePoint nos da esa capacidad y la podemos ampliar con Report Builder 3.0 que es una herramienta que permite que los informes los realicen los usuarios.

 

Saludos a todos…

SharePoint. Variables globales de JavaScript

SharePoint trabaja con una serie de variables globales en JavaScript que nos pueden facilitar la vida en nuestros desarrollos L_Menu_BaseUrl, L_Menu_LCID, L_Menu_SiteTheme, etc. Hoy hablaremos del L_Menu_BaseUrl.

Esta variable almacena la url del sitio actual. Es muy útil cuando necesitamos crear enlaces en JavaScript o cuando necesitamos añadir acciones o url al onet.xml (fichero de configuración de una plantilla de sitio).

Por ejemplo, queremos crear un enlace que utilice el nuevo diálogo modal de SharePoint 2010 para mostrar un formulario. La función JavaScipt SP.UI.ModalDialog.showModalDialog necesita la url a mostrar. Con esta variable es muy sencillo construir una url del tipo URL_SITIO + ‘Lists/Registro/NewForm.aspx’ sin necesidad de instanciar el modelo de objetos de JavaScript y realizar una llamada asíncrona para obtener ese valor.

Tan fácil como añadir un link a la barra de navegación:

<NavBarLink Name="Crear un recurso" Url= "javascript:showModalDialog(L_Menu_BaseUrl + '/Lists/Resources/NewForm.aspx', 'Nuevo Recurso')"></NavBarLink>

Y si nos planteamos hacerla con el modelo de objetos de JavaScript:

var ctx = new SP.ClientContext.get_current();

this.web = ctx.get_web();

ctx.load(this.web, 'ServerRelativeUrl');

ctx.ExecuteQueryAsync(Function.createDelegate(this, this.onSuccess), Function.createDelegate(this, this.onFail));

 

function onSuccess(sender, args) {

  relativeUrl = this.web.get_serverRealiveUrl();

}

 

Con lo que tendríamos que añadir este código a nuestro web template y luego obtener el nombre en el onet.xml. Mucho más sencilla la primera opción.

 

Saludos a todos…

Office 365 viene con SharePoint 2010 Online

Tenemos nueva beta pero esta vez en la nube. Microsoft ha presentado la beta de Office 365, el nuevo BPOS (Business Productivity Online Services). Con este anuncio, Microsoft espera que podamos llegar a todas las empresas, incluyendo las PYMES que necesitan de servicios de productividad y no se pueden permitir la inversión de una infraestructura mínima para ello.

Office 365 incluye los servicios en la nube de:

  • SharePoint 2010
  • Exchange 2010
  • Lync Server
  • y, esperemos, de CRM 2011

office365web

Aunque de momento, la suscripción a la beta no funciona del todo bien, se produce una excepción no controlada cuanto accedes a la página de suscripción (http://office365.microsoft.com/office365-beta.aspx), esperemos que en breve podamos enseñaros los servicios y las limitaciones que nos podamos encontrar en ellos.

Estoy ansioso de subir una solución sandbox al SharePoint 2010 online para comprobar las posibilidades.

No dejéis de pasar por la página del equipo de producto para recibir la última información.

 

Saludos a todos…

SharePoint 2010. Job de importación de perfiles de usuario

[ACTUALIZADO] He subido el código del artículo.

En el artículo anterior nos encontramos con un problema en la sincronización de perfiles. No podemos sincronizar el campo Administrador de un perfil utilizando un origen externos (BCS) como lo hacíamos en SharePoint 2007. Para saltarnos este problema, en lo que el equipo de producto de SharePoint saca un Hotfix, vamos a ver como desarrollar un Timer Job que sea el encargado de obtener los datos a importar, utilizando la entidad del BCS, y guarde la información en el perfile correspondiente.

Un Timer Job en SharePoint es una programación de una tarea que se encarga de ejecutar un cierto código con la frecuencia programada. En nuestro servidor, debemos de tener un gran número de Jobs programados que son los encargados de analizar y mantener la plataforma, por ejemplo, hay un job que se encarga de enviar las alertas de los usuarios. En la sección de Supervisión, tenemos una opción que nos permite visualizar la definiciones de estos jobs.

sharepoint2010_timerjobs_1

Para crear un Job sólo necesitamos una clase que herede de la clase del sistema Microsoft.SharePoint.Administration.SPJobDefinition y una característica que lo registre en la definición de trabajos en nuestro servidor.

Nuestra clase debe de sobrescribir el método Execute(), que es el método que se ejecuta cuando se activa la tarea, y crearnos unos constructores de clases que se encargan de inicializar el job. Para esto nos vamos a crear un proyecto vacío de SharePoint 2010 y añadimos la siguiente clase:

class SyncJob : SPJobDefinition

{

    string linkField = "UserID";

 

    public SyncJob()

        : base()

    {

    }

 

    public SyncJob(string jobName, SPService service, SPServer server, SPJobLockType targetType)

        : base(jobName, service, server, targetType)

    {

    }

 

    public SyncJob(string jobName, SPWebApplication webApplication)

        : base(jobName, webApplication, null, SPJobLockType.Job)

    {

        this.Title = "Anada Sync Job";

    }

 

    public override void Execute(Guid targetInstanceId)

    {

        //Obtenemos la aplicación web en la que se está ejecutando el job

        SPWebApplication webApplication = this.Parent as SPWebApplication;

        SPContentDatabase contentDb = webApplication.ContentDatabases[targetInstanceId];

 

        ImportPeople(webApplication.Sites[0]);

    }

}

Una vez que tenemos nuestra estructura de Job, nos creamos una nueva característica a nivel de Site y añadimos un Event Receiver que se encargará de registrar nuestro Job en el servidor, cuando esta sea activada.

sharepoint2010_timerjobs_2

public class FeatureAnadaSyncJobEventReceiver : SPFeatureReceiver

{

    const string List_JOB_NAME = "AnadaSyncJob";

 

    public override void FeatureActivated(SPFeatureReceiverProperties properties)

    {

        SPSite site = properties.Feature.Parent as SPSite;

        

        //Eliminamos el Job si ya existe

        foreach (var job in site.WebApplication.JobDefinitions)

        {

            if (job.Name == List_JOB_NAME)

                job.Delete();

        }

 

        //Registramos el job con una programación por defecto.

        Job.SyncJob syncJob = new Job.SyncJob(List_JOB_NAME, site.WebApplication);

        SPDailySchedule schedule = new SPDailySchedule();

        schedule.BeginHour = 0;

        schedule.EndHour = 1;

        syncJob.Schedule = schedule;

        syncJob.Update();

    }

 

    public override void FeatureDeactivating(SPFeatureReceiverProperties properties)

    {

        //Cuando se desactiva la característica, eliminamos el Job del sistema

        SPSite site = properties.Feature.Parent as SPSite;

 

        foreach (var job in site.WebApplication.JobDefinitions)

        {

            if (job.Name == List_JOB_NAME)

                job.Delete();

        }

    }

}

Sincronización de los perfiles

En el método Execute del Job, vamos a añadir la llamada al método ImportPeople, que es el encargado de instanciar todas las entidades del BCS y actualizar los perfiles de los usuarios con esos datos.

Lo primero es ejecutar el método del BCS ReadList que es el que obtiene todas las entidades People del origen externo.

//Instanciamos el servicio de BDC

BdcService service = SPFarm.Local.Services.GetValue<BdcService>();

IMetadataCatalog catalog = service.GetDatabaseBackedMetadataCatalog(context);

 

//Obtenemos la definición de la entidad y del sistema externo

IEntity entity = catalog.GetEntity("GSC.Anada.SharePoint.PeopleModel", "Person");

ILobSystemInstance lobSystemInstance = entity.GetLobSystem().GetLobSystemInstances()[0].Value;

 

//Realizamos la consulta con el método ReadList

IMethodInstance methodInstance = entity.GetMethodInstance("ReadList", MethodInstanceType.Finder);

IEntityInstanceEnumerator ientityInstanceEnumerator = entity.FindFiltered(methodInstance.GetFilters(), lobSystemInstance);

 

while (ientityInstanceEnumerator.MoveNext())

{

    //Actualizamos los datos del perfile correspondiente

}

Ahora recorremos cada una de las entidades externas y actualizamos el perfil correspondiente en el manager de los perfiles de SharePoint, incluyendo la foto que subimos al sitio de los perfiles para que esté accesible para todos los usuarios del servidor.

//Obtenemos el id del usuarios de la entidad externa actual

string userId = ientityInstanceEnumerator.Current["UserID"].ToString();

 

//Obtenemos el perfile del usuario, buscando con el id del usuario externo

UserProfile user = upm.GetUserProfile(userId);

if (user != null)

{

    //Actualizamos los valores del perfile con los de la entidad externa

    user["WorkPhone"].Value = GetNullValue(ientityInstanceEnumerator.Current["Phone"]);

    user["CellPhone"].Value = GetNullValue(ientityInstanceEnumerator.Current["Mobile"]);

    user["Title"].Value = GetNullValue(ientityInstanceEnumerator.Current["JobTitle"]);

    user["Office"].Value = GetNullValue(ientityInstanceEnumerator.Current["WorkName"]);

    user["SPS-Location"].Value = GetNullValue(ientityInstanceEnumerator.Current["WorkAddress"]);

    user["Department"].Value = GetNullValue(ientityInstanceEnumerator.Current["Department"]);

    user["SPS-HireDate"].Value = ientityInstanceEnumerator.Current["HireDate"];

    user["Manager"].Value = GetNullValue(ientityInstanceEnumerator.Current["Manager"]);

    user["PhoneShort"].Value = GetNullValue(ientityInstanceEnumerator.Current["PhoneShort"]);

    user["MobileShort"].Value = GetNullValue(ientityInstanceEnumerator.Current["MobileShort"]);

    user["CustomerPhone"].Value = GetNullValue(ientityInstanceEnumerator.Current["CustomerPhone"]);

    user["NIF"].Value = GetNullValue(ientityInstanceEnumerator.Current["NIF"]);

    user["WorkPhoneCentro"].Value = GetNullValue(ientityInstanceEnumerator.Current["WorkPhone"]);

    user["WebSite"].Value = GetNullValue(ientityInstanceEnumerator.Current["WebUrl"]);

 

    //Actualizamos la foto utilizando el binario de la foto de la entidad externa

    if (ientityInstanceEnumerator.Current["Photo"] != null)

    {

        byte[] photo = ientityInstanceEnumerator.Current["Photo"] as byte[];

 

        SPFolder folder = UploadUserPhoto(userId, photo, site);

        if (folder != null)

        {

            SPSite userSite = folder.ParentWeb.Site;

 

            string fileNameWithoutExtension = GetFileNameFromAccountName(userId);

 

            string pictureUrl = String.Format("{0}/{1}/{2}_MThumb.jpg", userSite.Url, folder.Url, fileNameWithoutExtension);

 

            user["PictureUrl"].Value = pictureUrl;

        }

    }

 

    //Guardamos los cambios del perfile actual

    user.Commit();

}

Y con esto conseguimos actualizar los perfiles de nuestros usuarios desde una entidad externa, salvando el problema de la actualización del Administrador (manager) que no funciona en SharePoint 2010. Ya avisaré cuando me llegue el Hotfix y lo pruebe, para que quitemos este Job y utilicemos el servicio de sincronización de SharePoint.

No he puesto todo el código auxiliar, si alguien lo necesita que me lo pida que lo publico para que pueda utilizarlo.

Os dejo el código del job y del BCS, se puede descargar desde mi skydrive

 

Saludos a todos…

SharePoint 2010. Sincronización de Perfiles de Usuarios con datos empresariales

Como comentamos en el post anterior, SharePoint nos permite sincronizar  los perfiles de usuarios con diversos orígenes, entre los que destaca la posibilidad de utilizar una entidad de BCS (Business Conectivity Services) para importar o exportar la información de los perfiles.

Con esta capacidad podemos preparar un BCS que enlace con nuestra aplicación de Recursos Humanos y obtener información de los empleados para que sea visible desde los perfiles.

sharepoint2010_userprofiles_bcs_2

Una vez tenemos el BCS en nuestro servidor, podemos crear una nueva conexión de sincronización, que, junto a la sincronización con el Directorio Activo, importe los datos en los perfiles de los usuarios utilizando, por ejemplo, el campo WorkEmail para hacer el enlace y la consulta al BCS.

sharepoint2010_userprofiles_bcs_1

Ahora nos toca configurar los campos a importar en la administración de propiedades de usuario, y es aquí dónde nos encontramos con un pequeño problema. Podemos configurar sin problemas los campos cadena, URL, sin embargo, nos encontramos que el campo Administrados (Manager) que es del tipo Persona no lo podemos mapear.

En SharePoint 2007, podíamos configurar para que realizara la búsqueda de esa Persona, utilizando un campo cadena tipo WorkEmail o un Nombre de cuenta de Directorio Activo.

sharepoint2010_userprofiles_bcs_3

Después de algunas semanas buscando el problema, incluso cambiando el modelo del BCS para que uno de los campos fuera un enlace a si mismo (simulando que ese campo es de tipo Persona), enviamos el caso a Microsoft y nos confirman, pasado un tiempo, que estamos ante un bug del producto y hay que esperar a un HotFix o Service Pack.

Me van a matar. Tengo parada una migración a SharePoint 2010 por culpa de este problema. Es importante la importación de los Administradores porque esa información no se encuentra en el Directorio Activo y la necesitamos para los flujos de trabajo de aprobación de gastos, vacaciones, etc.

¿Y ahora qué?

Dándole vueltas al asunto, empezamos intentando modificar el agente de FIM (Forefront Identity Manager), que es el encargado de la sincronización de los perfiles en SharePoint 2010, para que transforme el campo cadena Responsable en una Persona que se pueda asignar al campo Administrador.

Nos hemos dado muchos cabezazos con FIM, intentando que lo haga y resulta que los identificadores de consulta son internos y no hemos encontrado la manera de que utilice el WorkEmail para hacer el enlace.

Necesitamos un workaround que nos permita esta sincronización, toca desarrollar un Timer Job de SharePoint que consulte los datos de la aplicación de RRHH y los guarde en el perfil del usuario.

En el siguiente artículo, os contaré como se desarrolla el job y importar la información en los perfiles, y como utilizamos la entidad del BCS para realizar las consultas a través de este sistema para evitar la conexión directa a un sistema externo.

 

Saludos a todos…