SSD: el síndrome de la Sharepoint dependencia

Sharepoint me proporciona seguridad y me hace sentir más fuerte. Las 10 cosas que más me gustan de Sharepoint.

10 puntos para entender a Project Server 2010

Microsoft Project es quizá la herramienta de gestión de proyectos más conocida y utilizada por los líderes de proyectos...

Diseño Gráfico en SharePoint

Serie de artìculos que nos ayudan a incorporar diseño gráfico en las implementaciones de SharePoint...

Revista CompartiMOSS

Artículos publicados en la revista especializada en SharePoint: CompartiMOSS.

Contacto

Enviame un correo :-)

domingo, 25 de octubre de 2015

¿Cómo listas los nombres internos de las columnas de una lista en SharePoint con PowerShell?

$lista = $web.Lists["Issues y riesgos"]
$lista.Fields | Select Title, InternalName | sort InternalName | out-file c:\nombres.txt
NotePad c:\nombres.txt


jueves, 8 de octubre de 2015

Stack Overflow (in Spanish)

jueves, 1 de octubre de 2015

Quinto año de MVP :-)

Muy contento :-)


miércoles, 23 de septiembre de 2015

¿Cómo eliminar manejadores de eventos huérfanos con C# en SharePoint?

Arquitectura de la solución
  • Un sitio raíz
  • Varios sitios hijos
  • Tipos de contenido en el sitio raíz
  • Listas en los sitios hijos que consumen los tipos de contenido del sitio raíz
  • Manejadores de eventos asociados a los tipos de contenido del sitio raíz en forma programática:



El problema

Se presenta un error al tratar de eliminar los eventos asociados el tipo de contenido, propagando el cambio hacia los tipos de contenido descendientes:




El error

Mensaje: Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. Operación no válida dado el estado actual del objeto. 

Stack trace: en Microsoft.SharePoint.SPContentType.PushDownChanges(CodeToPushDownChangesToDerivedCT derivedCTPushdownCode, CodeToPushDownChangesToListCTs listDerivedCTsPushdownCode, Boolean throwOnSealedOrReadOnly, IList`1 exceptions) en Microsoft.SharePoint.SPContentType.UpdateOnWeb(Boolean bPushdown, Boolean ignoreSealedOrReadOnly, Boolean bThrowOnSealedOrReadOnly, Boolean bSetNextChildByteToZero, IList`1 exceptions) en Microsoft.SharePoint.SPContentType.Update(Boolean updateChildren, Boolean ignoreSealedOrReadOnly, Boolean throwOnSealedOrReadOnly, IList`1 exceptions) en Microsoft.SharePoint.SPContentType.Update(Boolean updateChildren, Boolean throwOnSealedOrReadOnly) en EMP_Procesos.Layouts.EMP_Procesos.Upgrade.asociarEventosGovernance(SPSite sitio) en EMP_Procesos.Layouts.EMP_Procesos.Upgrade.b__2()

Source: Microsoft.SharePoint

Target site: Void PushDownChanges(CodeToPushDownChangesToDerivedCT, CodeToPushDownChangesToListCTs, Boolean, System.Collections.Generic.IList`1[System.Exception])



El análisis del problema

Investigando con SharePoint Manager, se observa que hay tipos de contenido en los sitios hijos que tienen un ensamblado erróneo y no tienen los manejadores de eventos. Se observa que la rama de EventReceivers no se puede abrir:



Debería verse así (sitios en que se encuentra bien)



La solución:

Se busca eliminar los manejadores de eventos dentro del XML utilizando este código proporcionado en una respuesta de Stackoverflow:

http://sharepoint.stackexchange.com/a/47408/39542 (se recomienda ver el hilo completo ya que existen varias soluciones que dependen de cada escenario)

var ctG = listaG.ContentTypes["Governance"];
if (ctG.XmlDocuments.Count > 0)
{
  ctG.XmlDocuments.Delete("http://schemas.microsoft.com/sharepoint/events");
  contenido.InnerHtml += "<span>" + oWebsite.Title + ": XML Documents encontrados en Governance</span><br>";
  ctG.Update();
}

El arreglo

Los tipos de contenido se visualizan así en SharePoint Manager luego de la "limpieza"




El resultado

Luego de borrar y volver a asociar los eventos, se observa el XML con el ensamblado correcto y los eventos correctos asociados:


lunes, 21 de septiembre de 2015

¿Cómo listas los manejadores de eventos de los tipos de contenido del sitio raíz en SharePoint con C#?

SPContentTypeCollection tcs = oSiteCollection.RootWeb.ContentTypes;
foreach (SPContentType tc in tcs)
{
  contenido.InnerHtml += "<span><b>" + tc.Name + "</b></span><br>";
  SPEventReceiverDefinitionCollection eventos = tc.EventReceivers;
  foreach (SPEventReceiverDefinition evento in eventos)
  {
    contenido.InnerHtml += "<span>" + evento.Synchronization + "</span><br>";
    contenido.InnerHtml += "<span>" + evento.Class + "</span><br>";
    contenido.InnerHtml += "<span>" + evento.Type + "</span><br>";
  }
}



lunes, 17 de agosto de 2015

¿Cómo leer un ítem con buena performance de una lista en SharePoint con C#?

La recomendación es usar GetItemByIdSelectedFields y especificar sólo los elementos que necesitamos. Recordar usar el internal name.

using (SPWeb web = SPContext.Current.Web)
{
  string urlRiesgos = (web.ServerRelativeUrl +  "/Lists/Riesgos").Replace("//","/");
  SPList listaRiesgos = web.GetList(urlRiesgos);
  SPListItem itemRiesgo = listaRiesgos.GetItemByIdSelectedFields(idRiesgo, "Estado_Cumplimiento_Riesgo", "Tipo_Riesgo");
  if (itemRiesgo["Estado"] != null)
    estadoActual = itemRiesgo["Estado"].ToString();
  tipoRiesgo = itemRiesgo["Tipo_Riesgo"].ToString();
}

domingo, 16 de agosto de 2015

¿Cómo especificar un valor nulo como default de una columna de sitio choice en SharePoint con C#?

SPFieldChoice estadoRiesgo = (SPFieldChoice)oSiteCollection.RootWeb.Fields.GetFieldByInternalName("Estado_Cumplimiento_Riesgo");
if (estadoRiesgo.DefaultValue != null)
{
  estadoRiesgo.DefaultValue = null;
  estadoRiesgo.Update(true);
}

¿Cómo inhabilitar la edición en hoja de datos de una lista en SharePoint con C#?

SPList listaRiesgos = oWebsite.Lists.TryGetList("Issues y riesgos");
if (listaRiesgos != null)
{
  if (!listaRiesgos.DisableGridEditing)
  {
    listaRiesgos.DisableGridEditing = true;
    contenido.InnerHtml += "OK";
  }
  else
    contenido.InnerHtml += "NO-OK";
}

viernes, 14 de agosto de 2015

¿Cómo hacer no visible un campo en las pantallas de edición de lista en SharePoint con C#?

        // Modificar visibilidad de columna de sitio
        public string alterarVisibilidadColumnaSitio(SPSite sitio, string columna, bool showNew, bool showEdit, bool showDisp)
        {
            SPField columnaSitio = sitio.RootWeb.Fields.GetFieldByInternalName(columna);
            if (columnaSitio.ShowInNewForm == showNew && columnaSitio.ShowInEditForm == showEdit && columnaSitio.ShowInDisplayForm == showDisp )
                return "<span style='color: blue'>Columna " + columna + " no requiere modificación de visibilidad.</span><br>";
            else
            {
                columnaSitio.ShowInNewForm = showNew;
                columnaSitio.ShowInEditForm = showEdit;
                columnaSitio.ShowInDisplayForm = showDisp;
                columnaSitio.PushChangesToLists = true;
                columnaSitio.Update(true);
                return "<span style='color: green'>Columna " + columna + ": visibilidad modificada.</span><br>";
            }
        }

En la siguientes imágenes pueden ver la diferencia entre la pantalla de alta y la de edición:






miércoles, 12 de agosto de 2015

¿Cómo crear un custom job para una colección de sitios en particular en SharePoint con C#?

En este artículo veremos como crear un job personalizado en SharePoint que sea específico de una colección de sitios. Esto lo haremos sin "hardcodear" la URL ni almacenarla en alguna opción de configuración.

Para ello seguimos los siguientes lineamientos:
  • Creamos una feature de scope SITE
  • Guardamos la URL de la colección del sitio en forma dinámica en el momento de crear el job
  • Leemos la URL guardada en el momento de ejecutar el job

Paso 1: feature

Creamos una feature de scope SITE.



Y agregamos eventos para activarla y desactivarla

using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Administration;

namespace EMP_Procesos.Features.EMP_Alarmas
{

    [Guid("blabla")]
    public class EMP_AlarmasEventReceiver : SPFeatureReceiver
    {
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                SPSite sitio = properties.Feature.Parent as SPSite;
                SPWebApplication webApp = sitio.WebApplication;
                EMPJobs.alarmasJobAdm.borrarJobAlarmas(webApp.JobDefinitions);
                EMPJobs.alarmasJobAdm.crearJobAlarmas(webApp, sitio.Url);
            });

        }
        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                SPSite sitio = properties.Feature.Parent as SPSite;
                SPWebApplication webApp = sitio.WebApplication;
                EMPJobs.alarmasJobAdm.borrarJobAlarmas(webApp.JobDefinitions);
            });
        }
    }
}


Paso 2: Creación del job

Desde la feature se llama a las funciones que crean y borran el job. Fíjense en particular que la función que crea el job almacena la propiedad con la URL del site collection:

    public class EMPJobs
    {

        // Lógica del JOB


        // Administración del JOB

        public static class alarmasJobAdm
        {

            public static void crearJobAlarmas(SPWebApplication webApp, string url)
            {
                EMPJobs.alarmasJob alarmasTimerJob = new EMPJobs.alarmasJob(webApp);

                // Guardo la URL del site collection como propiedad
                if (alarmasTimerJob.Properties.ContainsKey("sitioJobUrl"))
                    alarmasTimerJob.Properties.Remove("sitioJobUrl");
                alarmasTimerJob.Properties.Add("sitioJobUrl", url);

                // Schedule
                SPMinuteSchedule schedule = new SPMinuteSchedule();
                schedule.BeginSecond = 0;
                schedule.Interval = 30;
                alarmasTimerJob.Schedule = schedule;

                alarmasTimerJob.Update();
            }

            public static void borrarJobAlarmas(SPJobDefinitionCollection jobs)
            {
                foreach (SPJobDefinition job in jobs)
                    if (job.Name.Equals(EMPJobs.alarmasJob.nombreJob, StringComparison.OrdinalIgnoreCase))
                        job.Delete();
            }
        }

    }


Paso 3: Ejecución del job

    public class EMPJobs
    {

        // Lógica del JOB

        public class alarmasJob : SPJobDefinition
        {
            public const string nombreJob = "SURPOINT_Alarmas";
            
            public alarmasJob() : base() { }
            
            public alarmasJob(SPWebApplication webApplication) : base(nombreJob, webApplication, null, SPJobLockType.Job)
            {
                this.Title = "SURPOINT - Sistema de alarmas";
            }
            
            public override void Execute(Guid targetInstanceId)
            {
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    using (SPSite miSitio = new SPSite(this.Properties["sitioJobUrl"].ToString()))
                    {
                        using (SPWeb miWeb = miSitio.OpenWeb())
                        {
                            // El procesamiento de nuestro job 
                            // El procesamiento de nuestro job 
                            // El procesamiento de nuestro job 
                            // El procesamiento de nuestro job 
                        }
                    }
                });
            }
        }

        // Administración del JOB

    }

El resultado



Algunos puntos a tener en cuenta:
  • El usuario que activa la feature debe tener permisos para crear jobs
  • El usuario que desactiva la feature debe tener permisos para borrar jobs
  • Es posible que se requiera reiniciar el Time job luego de hacer un despliegue para que el job tome la siguiente versión del código.



Bibliográfica: