Thursday, May 16, 2013

SharePoint 2013 Workflow error : Custom outcome column returns default option as a task result


I was facing weird issue in SharePoint 2013 Designer workflow.
Scenario was,
I wanted to create one list workflow where custom list was created programmatically and having my custom workflow task list created through code as well custom content type and outcome columns also created programmatically.
Now in workflow stage having an Assign task action and in Outcome options using the above created content type and site columns, see the figure below:


Issue here I was facing, Task result was always showing "TaskOutcome:0" i.e the default Outcome in task inspite of I selected any another option.
example in above figure result was always coming "Action Plan Created".

Tried different approaches to make it work:

-While debugging tried various options to make it work, like installed update http://support.microsoft.com/kb/2767999
where it mentioned that
"Assume that you use non-ASCII characters to name a task outcome field in a task in a custom workflow. Then, you change the default value in the task outcome field. In this situation, the task outcome field still displays the default value on the workflow status page."
but no luck with this as well)

- Tried to make the schema of custom outcome column same with SharePoint default TaskOutcome having however still same issue

- Any many other options tried but no luck.

After a week's debugging finally found the trick to solve this product bug.

1. Create one global variable after the Assign task step.


2. Set the global variable value to task outcome column explicitly.


3. Mapped it with assigned task's TaskID variable.


4. we are good to go, now we get the task outcome as a result which we select in task form.

Happy coding!


Monday, May 13, 2013

Errors were found when compiling the workflow. The workflow files were saved but cannot run. - SharePoint 2013

Today while trying to publish the workflow, came across the weird error
"Errors were found when compiling the workflow. The workflow files were saved but cannot run."



After digging into it, came to the solution that need to  Re-register service into my site using following commands:


Register-SPWorkflowService -SPSite 'https://my-host/mysite' -WorkflowHostUri 'https://workflowhost' -AllowOAuthHttp -Force

I reopened SharePoint Designer again and problem disappeared.

Hope this will save your time.

Wednesday, April 3, 2013

Powershell To Attach workflow on Content Type and create Retention Policy on it - SharePoint 2013

Hi Friends,

I got a requirement to create retention policy on Content type and action is to start workflow. as well attach workflow to the content type.

I choose to go with powershell.

save the following code into file called SetupRetentionPolicy.ps1

call like:

 c:\>.\SetupRetentionPolicy.ps1 -url <weburl> -ContentTypeName <Contenttype>  



 param(  
 $url=$null,  
 $ContentTypeName=$null)  
 $site = get-spsite $url  
 $web = $site.openweb()  
 Enable-SPFeature –identity "LocationBasedPolicy" -URL $url -ErrorAction SilentlyContinue  
 $Property = "Modified"  
 $Period = 1;  
 $PeriodType = "years"  
 function AddWorkflowToContentType($site, $ctName, $WfName, $WfAssociationName, $TaxTaskList)  
   {  
   [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SharePoint')   
   [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.Office.Policy')  
   [Guid]$wfTemplateId = New-Object Guid  
     $web = $site.RootWeb  
     $ct = $web.ContentTypes[$ctName]  
     $culture = New-Object System.Globalization.CultureInfo("en-US")  
     $template = $site.RootWeb.WorkflowTemplates.GetTemplateByName($WfName, $culture)  
     if($template -ne $null)  
     {              
     $tasklist = $TaxTaskList  
     $historylist = "IP Workflow History"  
   # Workflow Association with Content Type  
       $association = [Microsoft.SharePoint.Workflow.SPWorkflowAssociation]::CreateSiteContentTypeAssociation($template, $WfAssociationName, $web.Lists[$tasklist], $web.Lists[$historylist])   
       $association.AllowManual = $false  
       $association.AutoStartCreate = $true  
       $ct.AddWorkflowAssociation($association)  
       $ct.UpdateWorkflowAssociationsOnChildren($true, $true, $true, $false)  
       $association.Enabled=$true  
       $ct.Update()  
       $web.Update()  
     }  
     else  
     {  
     Write-Error "Workflow Template not found"  
           Add-Content $logFileName "Workflow Template not found."  
     }  
   }  
 function StartWorkflow($site, $wfAssociationName, $listName, $contentType, $WfStartColumn)  
 {  
   $contentType = $web.ContentTypes[$contentType]  
   if($contentType)  
   {  
     [Microsoft.Office.RecordsManagement.InformationPolicy.Policy]::CreatePolicy($contentType, $null);  
     $newPolicy = [Microsoft.Office.RecordsManagement.InformationPolicy.Policy]::GetPolicy($contentType);  
      $wfAssociationId = $contentType.WorkflowAssociations.GetAssociationByName($wfAssociationName, 1033).ParentAssociationId.ToString()  
     $newPolicy.Items.Add("Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration",  
      "<Schedules nextStageId='3'>"+  
      "<Schedule type='Default'>"+  
      "<stages>"+  
      "<data stageId='1' stageDeleted='true'></data>"+  
      "<data stageId='2'>"+  
      "<formula id='Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration.Formula.BuiltIn'>"+  
      "<number>"+$Period+"</number>"+"<property>"+$Property+"</property>"+  
      "<period>"+$PeriodType+"</period>"+  
      "</formula>"+  
      "<action type='workflow' id='"+ $wfAssociationId +"' />"+  
      "</data>"+  
      "</stages>"+  
      "</Schedule>"+  
      "</Schedules>");  
     $newPolicy.Items.Add("Microsoft.Office.RecordsManagement.PolicyFeatures.PolicyAudit",  
      "<Audit>"+  
      "<Update />"+  
      "<View />"+  
      "<CheckInOut />"+  
      "<MoveCopy />"+  
      "<DeleteRestore />"+  
      "</Audit>");  
     $newPolicy.Update();  
   }  
   }  
 $web.Dispose()  
 $site.Dispose()  
 echo "Finished!!!"  
  AddWorkflowToContentType -site $site -ctName $ContentTypeName -wfName "DocumentApprovalWorkflow" -WfAssociationName "DocumentApproval" -TaxTaskList "Improvement Tasks";  
  StartWorkflow -site $site -contentType $ContentTypeName -wfAssociationName "DocumentApproval" -listName "Documents" -WfStartColumn "Modified";  

Workflow should be availble on web when you run this script.
workflow is of sharepoint 2010 reusable type.

Happy Coding!!

Monday, March 18, 2013

How to: Follow documents and sites by using the .NET JavaScript object model in SharePoint 2013

Scenario is here we will create one sample user control, which will have simple one star which we can place on our page to follow the site/documents using JavaScript object model.

1. Visual Studio 2012, SharePoint project.
2. Add new item, web user control and give a name : favoriteControl.ascx
3.

 <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="FavoriteControl.ascx.cs" Inherits="test.FavoriteControl" %>  
 <div class="favIcon" runat="server" id="favoriteLink"><a href="#" runat="server" id="favorite" onclick="javascript:Follow();"></a></div>  
4. Add Script file to project, named favoritehelper.js and paste the following code:



 $(document).ready(function () {  
   // Ensure that the SP.UserProfiles.js file is loaded before the custom code runs.  
   //SP.SOD.executeOrDelayUntilScriptLoaded(loadUserData, 'sp.userprofiles.js');  
   SP.SOD.executeOrDelayUntilScriptLoaded(function () {  
     SP.SOD.executeOrDelayUntilScriptLoaded(function () {  
       SP.SOD.executeOrDelayUntilScriptLoaded(isAlreadyFollowed, 'SP.UserProfiles.js');  
     }, 'SP.js');  
   }, 'SP.runtime.js');  
 });  
 var clientContext;  
 var socialActor;  
 function isAlreadyFollowed() {  
   clientContext = SP.ClientContext.get_current();  
   socialManager = new SP.Social.SocialFollowingManager(clientContext);  
   socialActor = new SP.Social.SocialActorInfo();  
   var siteContentUrl = window.location;  
   socialActor.set_contentUri(siteContentUrl);  
   var result = socialManager.isFollowed(socialActor);  
   if (result = true) {  
     document.getElementById('ctl00_PlaceHolderMain_favoriteControl1_favoriteLink').className = 'favIconSelected';  
     //alert('Alreadyfollowed');  
   }  
   else {  
     document.getElementById('ctl00_PlaceHolderMain_favoriteControl1_favoriteLink').className = 'favIcon';  
     // alert('alreadynotfollowed');  
   }  
 }  
 function Follow() {  
   if (!isAlreadyFollowed()) {  
     clientContext = SP.ClientContext.get_current();  
     socialManager = new SP.Social.SocialFollowingManager(clientContext);  
     var siteContentUrl = window.location;  
     var siteActorInfo = new SP.Social.SocialActorInfo();  
     siteActorInfo.set_contentUri(siteContentUrl);  
     //4 type is for set of site  
     siteActorInfo.set_actorType(4);  
     socialManager.follow(siteActorInfo);  
     document.getElementById('ctl00_PlaceHolderMain_favoriteControl1_favoriteLink').className = 'favIconSelected';  
     alert('Added into your follow list');  
   }  
 }  
 function StopFollowing() {  
   clientContext = SP.ClientContext.get_current();  
   socialManager = new SP.Social.SocialFollowingManager(clientContext);  
   var siteContentUrl = window.location;  
   var siteActorInfo = new SP.Social.SocialActorInfo();  
   siteActorInfo.set_contentUri(siteContentUrl);  
   siteActorInfo.set_actorType(4);  
   socialManager.stopFollowing(siteActorInfo);  
 }  
 function GetFollowers() {  
   clientContext = SP.ClientContext.get_current();  
   socialManager = new SP.Social.SocialFollowingManager(clientContext);  
   var followersArray = socialManager.getFollowers();  
   return followersArray;  
 }  
 function GetFollowed() {  
   clientContext = SP.ClientContext.get_current();  
   socialManager = new SP.Social.SocialFollowingManager(clientContext);  
   var followedArray = socialManager.GetFollowed(4);  
   return followedArray;  
 }  
 function GetFollowedCount() {  
   clientContext = SP.ClientContext.get_current();  
   socialManager = new SP.Social.SocialFollowingManager(clientContext);  
   var count = socialManager.getFollowedCount(types)  
 }  
5. Add New web form and  register the control and the necessary script references.

follow the code:
 <%@ Register Src="~/Widgets/FavoriteControl.ascx" TagName="FavoriteControl" TagPrefix="gims" %>  
 <asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">  
 <script src="/_layouts/15/sp.runtime.js" ></script>  
     <script src="/_layouts/15/sp.js" ></script>  
     <script src="/_layouts/15/sp.UserProfiles.js" ></script>  
    <script src="/Scripts/jquery-ui.js"></script>  
   <script src="/Scripts/jquery.min.js"></script>  
 <script src="/Scripts/FavoriteHelper.js" ></script>  
 </asp:Content>  
and in your content area add the control


 <div>  
    <gims:FavoriteControl  ID="favoriteControl1" runat="server" />  
 </div>  

That's it! we are good to go, on page will get star, if it is already followed will be get selected by default, if not than on click of star it will get followed.

Reference link:

http://msdn.microsoft.com/en-us/library/jj667824.aspx


Tuesday, February 12, 2013

SharePoint 2013 Workflow gets canceled automatically

Hi,

I was struck with the error of Workflow gets canceled automatically.
Scenario was :
A new SP 2013 installation, one server with SP, WF, etc and the other is simply SQL Server. Went through the installation steps for Workflow including the powershell bits. In designer, I am able to create a simple workflow and select 2013 as the type. When the workflow is run, is shows internal status is canceled and has the following error text below.

  • RequestorId: 55c6990b-c4a6-352c-4a5b-449fc1aecac4. Details: System.ApplicationException: HTTP 401 {"x-ms-diagnostics":["3001000;reason=\"There has been an error authenticating the request.\";category=\"invalid_client\""],"SPRequestGuid":["55c6990b-c4a6-352c-4a5b-449fc1aecac4"],"request-id":["55c6990b-c4a6-352c-4a5b-449fc1aecac4"],"X-FRAME-OPTIONS":["SAMEORIGIN"],"SPRequestDuration":["61"],"SPIisLatency":["1"],"Server":["Microsoft-IIS\/8.0"],"WWW-Authenticate":["Bearer realm=\"61e7a76b-024e-40d5-9185-2887a044d7cc\",client_id=\"00000003-0000-0ff1-ce00-000000000000\",trusted_issuers=\"00000005-0000-0000-c000-000000000000@*,00000003-0000-0ff1-ce00-000000000000@61e7a76b-024e-40d5-9185-2887a044d7cc\"","Negotiate","NTLM"],"X-Powered-By":["ASP.NET"],"MicrosoftSharePointTeamServices":["15.0.0.4420"],"X-Content-Type-Options":["nosniff"],"X-MS-InvokeApp":["1; RequireReadOnly"],"Date":["Tue, 05 Feb 2013 19:17:31 GMT"]} {"error_description":"The server was unable to process the request due to an.....


    Finally nailed it by following steps:

    1. Be sure User profile synchronization is started.

    2. The user by whom you are logged is available in User Profile list. ( In my case it was not, created new profile for the current logged in user and make sure, you are not running workflow with SharePoint system user. Please create a new user in Active directory, and using this user, create/provision workfow
          3. Final step full synchronization of User Profile Application.


and its started working!!!

Hope it saves your time

Wednesday, February 6, 2013

SharePoint 2013 Taxonomy import using .csv and Setting site navigation to use taxonomy using PowerShell

Now requirement to import taxonomy from .csv file using PowerShell as well set taxonomy as site navigation with url.

1. Create .csv file


2. Create new script and paste below code.



 Param(  
  [string] $siteUrl = $(throw "Error: Parameter siteUrl is required"),  
  [boolean]$emptyfirst  
 )  
 ##Variables that should not be edited  
 $termsetName="My Navigation"  
 function CreateTerm( $parent, $name, $url )  
 {  
  Write-Host "Adding term $($parent.Name) -> $name"  
   $term = $parent.CreateTerm("$name", 1033)  
  $term.IsAvailableForTagging = $false  
  $term.SetLocalCustomProperty("_Sys_Nav_ExcludedProviders", '"CurrentNavigationTaxonomyProvider"')  
  $term.SetLocalCustomProperty("_Sys_Nav_SimpleLinkUrl", $url)  
  #$term.SetLocalCustomProperty("tIME", $url)  
  return $term  
 }  
 function GetTerm($termName, $parent)  
 {  
   $termName = [Microsoft.SharePoint.Taxonomy.TaxonomyItem]::NormalizeName($termName)  
   $term = $null  
  if( $termName -ne "" -and $parent -ne $null ){  
  if( $parent.Terms -ne $null ) {  
   $term = $parent.Terms | Where-Object {$_.Name -eq "$termName"}  
  }  
  if($term -eq $null ){  
   $term = CreateTerm -parent $parent -name "$termName" -url $_.URL  
  }  
  }  
  return $term;  
 }  
 function ImportTermSet([Microsoft.SharePoint.Taxonomy.TermSet]$set, [PSCustomObject]$terms) {   
  $terms | foreach {  
  $level1TermName = $_."Level 1 Term"  
  $level2TermName = $_."Level 2 Term"  
  $level3TermName = $_."Level 3 Term"  
   $level1Term = GetTerm -termName $level1TermName -parent $set  
  $level2Term = GetTerm -termName $level2TermName -parent $level1Term  
  $level3Term = GetTerm -termName $level3TermName -parent $level2Term   
  }  
  $ErrorActionPreference = "Continue";  
 }  
 Write-Host "Loading IIS module"  
 Import-Module WebAdministration  
 Write-Host "Loading SharePoint Commandlets"  
 Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue  
 Write-Host -ForegroundColor Green " Commandlets Loaded ... Loading Variables"  
 Write-Host "Connecting to Term Store"  
 Write-host  
 $termsetName="My Navigation"  
 $site = Get-SPSite $siteUrl  
 $web = $site.RootWeb  
 $session = [Microsoft.SharePoint.Publishing.Navigation.TaxonomyNavigation]::CreateTaxonomySessionForEdit($web)  
 $store = $session.TermStores[0]    
 $group = $session.TermStores.Groups | Where-Object {$_.SiteCollectionAccessIds -eq $site.ID }  
 $navigationSet = $group.TermSets | where { $_.Name -eq $termsetName }  
 if( $navigationSet -ne $null -and $emptyfirst) {  
  Write-Host -ForegroundColor Yellow "Removing existing termset"  
  $navigationSet.Delete()  
  $navigationSet = $null  
 }  
 if( $navigationSet -eq $null) {  
  Write-Host -ForegroundColor Yellow "Creating termset"  
  $navigationSet = $group.CreateTermSet($termsetName)  
 }  
 $navigationSet.SetCustomProperty("_Sys_Nav_IsNavigationTermSet", "True")  
 $navigationSet.SetCustomProperty("_Sys_Nav_AttachedWeb_SiteId", $site.ID.ToString())  
 $navigationSet.SetCustomProperty("_Sys_Nav_AttachedWeb_WebId", $site.RootWeb.ID.ToString())   
 $navigationSet.SetCustomProperty("_Sys_Nav_AttachedWeb_OriginalUrl", $site.RootWeb.Url )  
 #2013-02-05T12:52:07.5250653Z  
 $date = Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ"   
 $navigationSet.SetCustomProperty("_Sys_Nav_AttachedWeb_Timestamp", $date )  
 Write-Host "Importing Term Set CSV File"  
 $scriptpath = $MyInvocation.MyCommand.Path  
 $dir = Split-Path $scriptpath  
 $fileEntries = [IO.Directory]::GetFiles($dir);   
 foreach($fileName in $fileEntries)   
 {   
  $ext=[System.IO.Path]::GetExtension($fileName)  
  if($ext -eq ".csv")  
  {  
  Write-Host -ForegroundColor Green "Processing $fileName"  
  $CSVFILEPATH=$fileName;  
  $terms = Import-Csv -Delimiter ';' $fileName  
  ImportTermSet $navigationSet $terms  
  "All term sets have been imported"  
  Write-Host  
   }  
 }  
 $store.CommitAll()   
 Write-Host "Setting site nav to use taxonomy"  
 $settings = new-object Microsoft.SharePoint.Publishing.Navigation.WebNavigationSettings($web);  
 $settings.GlobalNavigation.Source = [Microsoft.SharePoint.Publishing.Navigation.StandardNavigationSource]::TaxonomyProvider  
 $settings.GlobalNavigation.TermStoreId = $store.Id  
 $settings.GlobalNavigation.TermSetId = $navigationSet.Id  
 $web.Update()  
 $settings.Update($session)  


Note :
1. Script file and .csv file should be in same folder.
2. This script supports import for multiple .csv files too. However it has to be in same folder.
3. Script will look for .csv in the current folder and will do import to Term store.
4.As well this script will set site navigation to use the taxonomy, It will set the navigation link with provided url to the term.




Tuesday, February 5, 2013

SharePoint 2013 TermSet with PowerShell


Create new script with following code:

$session = Get-SPTaxonomySession -Site "Your site Url"
$termStore = $session.TermStores[“Managed Metadata Application”] - change your managed metadata service name
$group = $termstore.CreateGroup(“My New Group”)
$group.Description = "My Term Group"
$termStore.CommitAll()
$termSet = $group.CreateTermSet(“My TermSet”,1033)
$termSet.Description = My TermSet
$termSet.IsAvailableForTagging = $true
$termSet.IsOpenForTermCreation = $true
$termStore.CommitAll()

To enable the term set for navigation, i.e  "Use this Term Set for Site Navigation" 
use following code to make it enabled.

$navigationSet = $group.CreateTermSet($termsetName)
 $navigationSet.SetCustomProperty("_Sys_Nav_IsNavigationTermSet", "True")
 $navigationSet.SetCustomProperty("_Sys_Nav_AttachedWeb_SiteId", $site.ID.ToString())
 $navigationSet.SetCustomProperty("_Sys_Nav_AttachedWeb_WebId", $site.RootWeb.ID.ToString())    
 $navigationSet.SetCustomProperty("_Sys_Nav_AttachedWeb_OriginalUrl", $site.RootWeb.Url )
You can check your new termset inside "My New Group" : go to  site management -> Term Store Management.