Mostrando entradas con la etiqueta Workflows SharePoint. Mostrar todas las entradas
Mostrando entradas con la etiqueta Workflows SharePoint. Mostrar todas las entradas

domingo, 4 de noviembre de 2012

SharePoint 2010 - New instances of this workflow template are currently disallowed

¿Les ha pasado que alguna vez quieren iniciar una instancia de un flujo de trabajo y aparece este mensaje de error?

Bueno púes a mí me había pasado muchas veces, solo que nunca al utilizar código para iniciar automáticamente un flujo de trabajo, es decir, siempre que había obtenido este mensaje de error había sido utilizando la interfaz de usuario, para lo cual existen muchas referencias en la red para solucionarlo.

SharePoint 2007 - New instances of this workflow template are currently disallowed
Error: New instances of this workflow template are currently disallowed.

¿Pero y cómo podemos resolverlo cuando hablamos de código?

Al igual que cuando lidiamos con este error al intentar inicializar in flujo de trabajo utilizando la interfaz de usuario, este error se debe a que existe más de una versión del mismo flujo relacionada con la lista o cualquier otro elemento al que se pueda asociar un workflow como librerías de documentos, sitios, etc., en mi caso específico con un tipo de contenido.

Para ejemplificar este escenario, muestro a continuación mi código de powershell que tenía este error, y el cual simplemente intentaba iniciar programáticamente todas las instancias de una lista de SharePoint con el estatus de “Cancelled”:

cls
#Add-PSSnapin microsoft.sharepoint.powershell
$siteUrl="http://sp2010/mysite"
$site = Get-SPSite -Identity $siteUrl

foreach($web in $site.AllWebs)
{
    $list=$web.Lists["List"]
    $manager=$site.WorkFlowManager
   
    foreach($ct in $list.ContentTypes)
    {
        if($ct.Name -eq "MyContentType")
        {
            $swapWorkflowAssociation = $Ct.WorkflowAssociations[0]
        }
    }
    
    #Si la referencia al objeto WorkflowAssociation es null, no intentamos iniciar el WF
    if($swapWorkflowAssociation -eq $null) { continue } 
   
    foreach($listItem in $list.Items)
    {
        foreach($workflowInstance in $listItem.Workflows)
        {
        
            if($workflowInstance.InternalState -eq [Microsoft.SharePoint.Workflow.SPWorkflowState]::Cancelled)
            {
               $data=$workflowAssociation.AssociationData
               try
               {
                    $result = $manager.StartWorkflow($listItem, $workflowAssociation, $data, $True)
               }
               catch
               {
                    #Write-Host $_.Exception.ToString()
               }
               
               $cadena =   "Se inicia el WF|" + $web.Url + "|" + $listItem.ID.ToString() + "|" + $listItem.Title + "|" + $workflowInstance.InternalState
               
               Write-Host $cadena 
            }  
        }
    } 
}


Como comente antes, en la línea marcada obtenía este mensaje de error. El error estaba en que debido al proceso de prueba y error desarrollo iterativo, el tipo de contenido tenía más de una versión del flujo de trabajo asociado, por lo que al intentar recuperar una referencia al objeto WorkflowAssociation, no se recuperaba precisamente la más “reciente” o actual. El truco está en que las versiones viejas del flujo de trabajo, además de ser establecidas con el atributo “No New Instances”, siempre son renombradas de la forma: “NombreDeMiWorkflow (fecha)”, excepto para la Asociación más reciente (la correcta).

Por lo que, para evitar este error, se debe validar que la propiedad Name del objeto WorkflowAssociation, sea exactamente como la establecimos en un principio, sin fechas (o algún otro texto adicional).
$swapWorkflowAssociation = $Ct.WorkflowAssociations[0]

Para concluir, el código corregido

cls
Start-SPAssignment –Global
#Add-PSSnapin microsoft.sharepoint.powershell
$siteUrl="http://sp2010/mysite"

$site = Get-SPSite -Identity $siteUrl

foreach($web in $site.AllWebs)
{
    $list=$web.Lists["List"]
    $manager=$site.WorkFlowManager
   
    foreach($ct in $list.ContentTypes)
    {
        if($ct.Name -eq "MyContentType")
        {
            foreach($swapWorkflowAssociation in $Ct.WorkflowAssociations)
            {
                if($swapWorkflowAssociation.Name -eq "MyWorkflowName") 
                {
                    $workflowAssociation = $swapWorkflowAssociation
                }
                
            }
        }
    }
   
   #Si la referencia al objeto WorkflowAssociation es null, no intentamos iniciar el WF
   if($swapWorkflowAssociation -eq $null) { continue } 
   
    foreach($listItem in $list.Items)
    {
        foreach($workflowInstance in $listItem.Workflows)
        {
        
            if($workflowInstance.InternalState -eq [Microsoft.SharePoint.Workflow.SPWorkflowState]::Cancelled)
            {
               $data=$workflowAssociation.AssociationData
               try
               {
                    $result = $manager.StartWorkflow($listItem, $workflowAssociation, $data, $True)
               }
               catch
               {
                    #Write-Host $_.Exception.ToString()
               }
               
               $cadena =   "Se inicia el WF|" + $web.Url + "|" + $listItem.ID.ToString() + "|" + $listItem.Title + "|" + $workflowInstance.InternalState
               
               Write-Host $cadena 
            }  
        }
    } 
}

Happy Coding!

domingo, 12 de agosto de 2012

Creando custom workflow activities para SharePoint 2010

Una de las mejores prácticas a la hora de crear flujos de trabajo (no solo para SharePoint) es encapsular funcionalidad común o reutilizable en actividades personalizadas, esto nos permitirá hacer flujos de trabajo mucho más mantenibles, disminuyendo así la cantidad de código que escribimos dentro del “code behind”, y es que al menos en mi muy humilde opinión, el código que escribimos debería ser mínimo y para casos muy específicos.

Además de todas las ventajas que implica el uso de actividades personalizadas, es bastante sencillo crearlas, a continuación explicaré el paso a paso de cómo crear una actividad que nos permita la creación de sub-sitios.

Pensemos en el siguiente escenario: El administrador de un sitio recibe muchas peticiones para generar sub sitios dentro del sitio que él administra, esta aburrido de hacer está tarea tan repetitiva, él lo que desea es salir temprano de la oficina y darle duro al PS3 Xbox 360, por lo que se le ocurre automatizar el proceso de solicitud de creación de sitios mediante una lista en la cual los solicitantes puedan ingresar la información fundamental para la creación del sitio y una justificación de porque lo necesitan, de esta manera el administrador solo recibe un mail de que tiene una tarea pendiente, entra a revisar la tarea y la aprueba (si es el caso) y automágicamente el sitio se provisiona, cool no!?. Pues entonces comencemos a  darle forma a los sueños de este administrador ficticio.

Primero creamos una solución de tipo de Sequential Workflow que nos servirá para probar la actividad personalizada que crearemos, después creamos un proyecto de tipo Workflow Activity Library que será en donde crearemos las actividades de workflow personalizadas.


Acto seguido agregamos una clase con el nombre que tendrá la actividad personalizada, en este caso CreateNewSubSite, y heredamos de la clase base Activity.


Creamos las propiedades que tendrán la información necesaria para crear el sub sitio: SiteName, SiteUrl, SiteLCID, TemplateName, SiteDescription, UseUniquePermissions y una propiedad para almacenar el contexto de ejecución del workflow.  Lo primero que debemos notar es que las propiedades que creamos son dependency properties (propiedades de dependencia), esto para permitir a cualquiera que use nuestra actividad poder enlazar (Binding) estas propiedades para que en tiempo de ejecución los valores se calculen. Si aún no sabes que es o para que se utilizan las property dependencies, lee esto.

public static readonly DependencyProperty SiteNameProperty = DependencyProperty.Register("SiteName", typeof(string), typeof(CreateNewSubSite));
public static readonly DependencyProperty SiteUrlProperty = DependencyProperty.Register("SiteUrl", typeof(string), typeof(CreateNewSubSite));
public static readonly DependencyProperty SiteLCIDProperty = DependencyProperty.Register("SiteLCID", typeof(uint), typeof(CreateNewSubSite), new PropertyMetadata((uint)1033));
public static readonly DependencyProperty TemplateNameProperty = DependencyProperty.Register("TemplateName", typeof(string), typeof(CreateNewSubSite), new PropertyMetadata("STS#1"));
public static readonly DependencyProperty SiteDescriptionProperty = DependencyProperty.Register("SiteDescription", typeof(string), typeof(CreateNewSubSite));
public static readonly DependencyProperty UseUniquePermissionsProperty = DependencyProperty.Register("UseUniquePermissions", typeof(bool), typeof(CreateNewSubSite));

public static DependencyProperty __ContextProperty = System.Workflow.ComponentModel.DependencyProperty.Register("__Context", typeof(Microsoft.SharePoint.WorkflowActions.WorkflowContext), typeof(CreateNewSubSite));

[Description("The name of the new subsite")]
public string SiteName
{
 get { return (string)GetValue(SiteNameProperty); }
 set { SetValue(SiteNameProperty, value); }
}

[Description("Relative Url of the new subsite. You should consider that any space will be codified, for example MySite=MySite, Financial Area=Financial%20Area")]
public string SiteUrl
{
 get { return (string)GetValue(SiteUrlProperty); }
 set { SetValue(SiteUrlProperty, value); }
}

[Description("Specify the LCID of the language that will be used to create the subsite. Example: English=1033, Spanish=3082. For more reference you can see http://technet.microsoft.com/en-us/library/ff463597.aspx")]
public uint SiteLCID
{
 get { return (uint)GetValue(SiteLCIDProperty); }
 set { SetValue(SiteLCIDProperty, value); }
}

[Description("Specify the template name that will be used to create the subsite. Example: GLOBAL#0 = Global template, STS#0 = Team Site, STS#1 = Blank Site.")]
public string TemplateName
{
 get { return (string)GetValue(TemplateNameProperty); }
 set { SetValue(TemplateNameProperty, value); }
}

[Description("Specify the description of the new subsite")]
public string SiteDescription
{
 get { return (string)GetValue(SiteDescriptionProperty); }
 set { SetValue(SiteDescriptionProperty, value); }
}

[Description("Specify if the site will have unique permissions")]
public bool UseUniquePermissions
{
 get { return (bool)GetValue(UseUniquePermissionsProperty); }
 set { SetValue(UseUniquePermissionsProperty, value); }
}

public WorkflowContext __Context
{
 get { return (WorkflowContext)GetValue(__ContextProperty); }
 set { SetValue(__ContextProperty, value); }
}

La parte medular para la creación de nuestra actividad personalizada es sobreescribir el método Execute, el cual contiene el código principal a ejecutar por la actividad personalizada. En este ejemplo tomamos los valores de las propiedades para crear el sub sitio, no sin antes ejecutar algunas validaciones.

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
 SPSecurity.RunWithElevatedPrivileges(() =>
  {
   using (SPSite site= new SPSite (__Context.Site.ID))
   {
    using (SPWeb web = site.AllWebs[__Context.Web.ID])
    {
     //before to add the new subsite, we need to validate some settings, remember! never trust in another dev
     //first at all, validate the LCID
     SPLanguage language = SPRegionalSettings.GlobalInstalledLanguages.Cast().FirstOrDefault(x => x.LCID == SiteLCID);
     if (language == null)
     {
      throw new ArgumentException(string.Format("The value '{0}' for SiteLCID is not supported, are you sure corresponding language pack is already installed?", this.SiteLCID));
     }
     
     //then validate the name of site template
     SPWebTemplate webTemplate = site.GetWebTemplates(this.SiteLCID).Cast().FirstOrDefault(x => x.Name == this.TemplateName);
     if (webTemplate == null)
     {
      throw new ArgumentException(string.Format("The specified TemplateName='{0}' is not supported for specified language='{1}'", this.TemplateName, language.DisplayName));
     }

     //then validate the SiteUrl is unique
     if (web.Webs.Names.Contains(SiteUrl))
     {
      throw new ArgumentException("The SiteUrl is not unique, there is another subsite with the same SiteUrl");
     }

     web.Webs.Add(SiteUrl, SiteName, SiteDescription, SiteLCID, webTemplate, UseUniquePermissions, false);
    }
   }
  }
 );

 return ActivityExecutionStatus.Closed;
}

Así de simple ya tenemos una actividad personalizada lista para ser utilizada. Para este ejemplo agregamos el proyecto del workflow (WorkfllowDemo) y el proyecto de las actividades personalizadas (CustomActivities) en la misma solución, por lo que al compilar la solución automáticamente la actividad que creamos aparece en el toolbox.

Aunque también se puede solo agregar la referencia a la dll que contiene las actividades utilizando la ventana de diálogos para agregar elementos al Toolbox, para poder utilizar las actividades sin necesidad del código fuente.
El paso siguiente es arrastrar la actividad CreateNewSubSite al diseñador de workflows y configurar sus propiedades con la información para crear el subsitio.

Como se aprecia en la imagen anterior, agregamos solo una actividad más para escribir en la lista de Workflow History que el sitio fue creado correctamente. Ahora hablemos un poco a cerca de la lista que utilizaremos para las solicitudes de nuevos sitios, la cual solo contiene dos columnas: SiteName y SiteUrl. Es a esta lista a la cual asociaremos el flujo de trabajo.

Configuramos el flujo para que se ejecute al crear un nuevo elemento en la lista y podremos ver como se crea el sitio automáticamente, además del mensaje en el historial del flujo de trabajo





Les comparto el código y si tienen algún comentario o pregunta no duden en hacerla

Happy coding!

Etiquetas

SharePoint 2010 (38) Microsoft (32) Desarrollo SharePoint (31) Gerardo Reyes Ortiz (27) SharePoint (20) SharePoint 2013 (18) Errores SharePoint (12) México (10) PowerShell (9) Silverlight (8) Visio Services (7) Features (6) MVP (6) Silverlight 3 (6) WebCast (6) Workflows (6) Configuracion SharePoint 2010 (5) D.F. (5) API REST (4) Configuracion SharePoint 2010; (4) Troubleshooting (4) Visual Studio 2010 (4) Visual studio (4) WSS (4) Web parts (4) Apps (3) Comunidad SharePoint (3) Configuración SharePoint 2013 (3) ODATA (3) SharePoint Server (3) SharePoint; Instalación SharePoint; Troubleshooting; Search Service (3) Silverlight 3.0 (3) Silverlight Toolkit (3) WebParts (3) javascript (3) jquery (3) Eventos SharePoint (2) Office 2010 (2) PeoplePicker (2) REST (2) SQL Server (2) Scripting (2) Search Service Application (2) SharePoint Designer (2) UPA (2) UPS (2) Workflows SharePoint (2) host header (2) Apps Development (1) Big Bang (1) CAS (1) CSOM (1) Codeplex (1) CompartiMOSS (1) Configuracion SharePoint 2010; Errores SharePoint (1) Configuracion SharePoint 2010; SharePoint 2010 (1) Custom Actions (1) Custom Editor Parts (1) Delegate Controls (1) Deployment (1) DisableLoopbackCheck (1) Document Library (1) Entrevista (1) Examenes de Certificación (1) Extract WSP (1) FBA (1) FS4SP (1) Fakes (1) Fast Search Server 2010 For SharePoint (1) Fiddler (1) HTTP.SYS (1) HTTPS (1) JSON (1) Language Pack's (1) Latam (1) MAXDOP (1) MCSM (1) MSExpertos (1) MVC (1) Microsoft México (1) Microsoft; Codeplex; Screencast; (1) My Sites (1) SQL Server 2012 (1) SQL Server Reporting Services (1) Screencast (1) Screencast; (1) Service Applications (1) Service Pack (1) SharePoint 2007 (1) SharePoint 2010 SP 1 (1) SharePoint API (1) SharePoint Conference (1) SharePoint Emulators (1) SharePoint Farm (1) SharePoint Health Analyzer (1) SharePoint Magazine (1) SharePoint Online (1) SharePoint Search (1) SharePoint Test (1) SharePoint; Desarrollo SharePoint (1) Shims (1) Simposio (1) Simposio Latinoamericano (1) SkyDrive Pro (1) Soporte Microsoft (1) Templates (1) Tip (1) VSeWSS (1) Virtual Machine (1) Visual Studio 2012 (1) WCF (1) WSS; IIS 7 (1) Web API (1) Web Content Management (1) Web Services (1) Windows 8 (1) Windows Live ID (1) Xml (1) appcmd (1) iOS (1) jqGrid (1) onload function (1)