SharePoint 2013 Birthday Reminder Workflow

I haven't seen anyone blog this in 2013 style, so here we go. This workflow will send an email to people every year on the day someone has a birthday.  Here's the step by step of how to create this birthday reminder workflow!

STEP 1:  Create a Birthday Calendar!

STEP 2:  Fire up SharePoint Designer 2013, connect to your site

STEP 3:  Create the workflow

  1. In the navigation, click "Workflows"
  2. In the ribbon, click "List Workflow", select your calender list

  3. For the name, type "Reminder Workflow"
  4. For the type, ensure that "SharePoint 2013 Workflow" is selected:

  5. Click "OK"

STEP 4:  Configure the "Set ExecuteDate" stage

  1. Rename "Stage 1" to "Set ExecuteDate"
  2. Click in the stage, start typing "Set a workflow variable", press ENTER
  3. Click the "workflow variable" link, then select "Create a new variable"
  4. For the name, type "ExecuteDate"
  5. For the value, select "DateTime"
  6. Click "OK"
  7. Click the "value" link, then click the "Fx" button
  8. For the data source, select "Current Item"
  9. For the Field, select "Start Time"
  10. Click "OK"
  11. Click below the set workflow variable action
  12. Start typing "If value equals", press ENTER
  13. Click the first "value" link, then click the "Fx" button
  14. Select "Workflow Variables and Parameters"
  15. Select "Variable: ExecuteDate"
  16. Click "OK"
  17. Click the "equals" link, change the value to "is less than"
  18. Click the second "value" link
  19. Click the "…" button, select "Current Date"
  20. Click "OK"
  21. In the if condition, start typing "Add Time to Date", press ENTER
  22. Click the "0" for the months, set it to "12", press ENTER
  23. Click the "date" link
  24. Click the "value" link
  25. For the data source, select "Current Item"
  26. For the Field, select "Start Time"
  27. Click "OK"
  28. Click the "Variable: date" link, select "Variable: ExecuteDate"
  29. Click below the "Set ExecuteDate" stage, in the ribbon, click "Stage", a new stage should be added
  30. Change the name of the new stage to "Pause For ExecuteDate"
  31. In the "Set ExecuteDate" stage's transition to stage area, start typing "go to stage", press ENTER
  32. Select the "Pause For ExecuteDate" stage
  33. The stage should look like this:

STEP 5:  Configure the "Pause For ExecuteDate" stage

  1. Start typing "If value equals", press ENTER
  2. Click the first "value" link, then click the "Fx" button
  3. Select "Workflow Variables and Parameters"
  4. Select "Variable: ExecuteDate"
  5. Click "OK"
  6. Click the "equals" link, change the value to "is greater than"
  7. Click the second "value" link
  8. Click the "…" button, select "Current Date"
  9. Click "OK"
  10. In the if condition, start typing "Pause until…", press ENTER
  11. Click the "this time" link, select the "Variable: ExecuteDate"
  12. Click below the "Pause For ExecuteDate" stage
  13. In the ribbon, click "Stage"
  14. Rename the new stage to "Send Email"
  15. In the "Pause For ExecuteDate" stage's transition to stage area, start typing "go to stage", press ENTER
  16. Select the "Send Email" stage
  17. The stage should look like this:

STEP 5:  Configure the "Send Email" stage

  1. Start typing "If value equals", press ENTER
  2. Click the first "value" link, then click the "Fx" button
  3. Select "Workflow Variables and Parameters"
  4. Select "Variable: ExecuteDate"
  5. Click "OK"
  6. Click the "equals" link, change the value to "equals (ignoring time)"
  7. Click the second "value" link
  8. Click the "…" button, select "Current Date"
  9. Click "OK"
  10. In the if condition, start typing "Email", press ENTER
  11. Click "these users"
  12. Select a sharepoint or ad group that you want to email
  13. For the subject, click the "Fx" button, select the "Current Item: Title", click "OK"
  14. In the body, type "Happy Birthday!"

  15. Click "OK"
  16. Below the Email action, start typing "Add Time", press ENTER
  17. For the month, type "12"
  18. For the two variables, set them to "Variable: ExecuteDate"
  19. Below the if condition, start typing "If value", press ENTER
  20. Click the first "value" link, then click the "Fx" button
  21. Select "Workflow Variables and Parameters"
  22. Select "Variable: ExecuteDate"
  23. Click "OK"
  24. Click the "equals" link, change the value to "is less than"
  25. Click the second "value" link
  26. Click the "…" button, select "Current Date"
  27. Click "OK"
  28. Below the if condition, start typing "Add Time", press ENTER
  29. For the month, type "12"
  30. For the two variables, set them to "Variable: ExecuteDate"
  31. In the "Send Emails" stage's transition to stage area, start typing "go to stage", press ENTER
  32. Select the "Pause For ExecuteDate" stage
  33. The stage should like this:

STEP 6:  Set the Workflow firing actions

  1. In the navigation breadcrumb, click the "Reminder Workflow" node
  2. On the workflow properties page, in the "Start Options" section, check the "Start workflow when an item is created" checkbox:

  3. Press "Ctrl-S" to save the workflow
  4. In the ribbon, click "Publish"

Create an item, fire the workflow!

Enjoy!
Chris

Approval Workflow Access Denied – How to fix it!

Updated 3/18/2015:

Turns out when you migrate from one domain to another, this error pops up yet again (even after running the migrate user commands)!  Another option of fixing this is to open *each* site collection and delete the workflows.  Then deactive and active the feature.  Then you can create sites again!

Yet again, another weird bug in SharePoint 2010 that I had to track down and fix this week!  What is this?  Here's what happened:

 My customer has a few workflows created to approve important documents.  All of a sudden, these workflows stopped working with an access denied error message.  In looking at the log files, you would see this  (it's rather long):

06/27/2012 06:40:02.96     w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           ftd0    Verbose     Access Denied. Exception: System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))     at Microsoft.SharePoint.Library.SPRequestInternalClass.OpenWeb(String bstrUrl, String& pbstrServerRelativeUrl, String& pbstrTitle, String& pbstrDescription, String& pbstrTitleResourceId, String& pbstrDescriptionResourceId, Guid& pguidID, String& pbstrRequestAccessEmail, UInt32& pwebVersion, Guid& pguidScopeId, UInt32& pnAuthorID, UInt32& pnLanguage, UInt32& pnLocale, UInt16& pnTimeZone, Boolean& bTime24, Int16& pnCollation, UInt32& pnCollationLCID, Int16& pnCalendarType, Int16& pnAdjustHijriDays, Int16& pnAltCalendarType, Boolean& pbShowWeeks, Int16& pnFirstWeekOfYear, UInt32& pnFirstDayOfWeek, Int16& pnWorkDays, Int16& pnWorkDayStartHour, Int16& pnWorkD…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           ftd0    Verbose     …ayEndHour, Int16& pnMeetingCount, Int32& plFlags, Boolean& bConnectedToPortal, String& pbstrPortalUrl, String& pbstrPortalName, Int32& plWebTemplateId, Int16& pnProvisionConfig, String& pbstrDefaultTheme, String& pbstrDefaultThemeCSSUrl, String& pbstrThemedCssFolderUrl, String& pbstrAlternateCSSUrl, String& pbstrCustomizedCssFileList, String& pbstrCustomJSUrl, String& pbstrAlternateHeaderUrl, String& pbstrMasterUrl, String& pbstrCustomMasterUrl, String& pbstrSiteLogoUrl, String& pbstrSiteLogoDescription, Object& pvarUser, Boolean& pvarIsAuditor, UInt64& ppermMask, Boolean& bUserIsSiteAdmin, Boolean& bHasUniquePerm, Guid& pguidUserInfoListID, Guid& pguidUniqueNavParent, Int32& plSiteFlags, DateTime& pdtLastContentChange, DateTime& pdtLastSecurityChange, String& pbstrWelcomePage, Boolean& pb…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           ftd0    Verbose     …OverwriteMUICultures, Boolean& pbMUIEnabled, String& pbstrAlternateMUICultures, Int32& puiVersion, Int16& pnClientTag)     at Microsoft.SharePoint.Library.SPRequest.OpenWeb(String bstrUrl, String& pbstrServerRelativeUrl, String& pbstrTitle, String& pbstrDescription, String& pbstrTitleResourceId, String& pbstrDescriptionResourceId, Guid& pguidID, String& pbstrRequestAccessEmail, UInt32& pwebVersion, Guid& pguidScopeId, UInt32& pnAuthorID, UInt32& pnLanguage, UInt32& pnLocale, UInt16& pnTimeZone, Boolean& bTime24, Int16& pnCollation, UInt32& pnCollationLCID, Int16& pnCalendarType, Int16& pnAdjustHijriDays, Int16& pnAltCalendarType, Boolean& pbShowWeeks, Int16& pnFirstWeekOfYear, UInt32& pnFirstDayOfWeek, Int16& pnWorkDays, Int16& pnWorkDayStartHour, Int16& pnWorkDayEndHour, Int16& pnMeetingC…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           ftd0    Verbose     …ount, Int32& plFlags, Boolean& bConnectedToPortal, String& pbstrPortalUrl, String& pbstrPortalName, Int32& plWebTemplateId, Int16& pnProvisionConfig, String& pbstrDefaultTheme, String& pbstrDefaultThemeCSSUrl, String& pbstrThemedCssFolderUrl, String& pbstrAlternateCSSUrl, String& pbstrCustomizedCssFileList, String& pbstrCustomJSUrl, String& pbstrAlternateHeaderUrl, String& pbstrMasterUrl, String& pbstrCustomMasterUrl, String& pbstrSiteLogoUrl, String& pbstrSiteLogoDescription, Object& pvarUser, Boolean& pvarIsAuditor, UInt64& ppermMask, Boolean& bUserIsSiteAdmin, Boolean& bHasUniquePerm, Guid& pguidUserInfoListID, Guid& pguidUniqueNavParent, Int32& plSiteFlags, DateTime& pdtLastContentChange, DateTime& pdtLastSecurityChange, String& pbstrWelcomePage, Boolean& pbOverwriteMUICultures, Boolea…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           ftd0    Verbose     …n& pbMUIEnabled, String& pbstrAlternateMUICultures, Int32& puiVersion, Int16& pnClientTag).    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96     w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           8gs1    Verbose     Access Denied for /contoso/_layouts/IniWrkflIP.aspx?List={b713a4ad-747e-4980-ad1c-c420d3f588b7}&ID=1&TemplateID={3cf9d212-c83d-4486-9fce-ee8d00efacbc}&Source=blah StackTrace:    at Microsoft.SharePoint.Utilities.SPUtility.HandleAccessDenied(HttpContext context)     at Microsoft.SharePoint.Utilities.SPUtility.HandleAccessDenied(Exception ex)     at Microsoft.SharePoint.Library.SPRequest.OpenWeb(String bstrUrl, String& pbstrServerRelativeUrl, String& pbstrTitle, String& pbstrDescription, String& pbstrTitleResourceId, String& pbstrDescriptionResourceId, Guid& pguidID, String& pbstrRequestAccessEmail, UInt32& pwebVersion, Guid& pguidScopeId, UInt32& pnA…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           8gs1    Verbose     …uthorID, UInt32& pnLanguage, UInt32& pnLocale, UInt16& pnTimeZone, Boolean& bTime24, Int16& pnCollation, UInt32& pnCollationLCID, Int16& pnCalendarType, Int16& pnAdjustHijriDays, Int16& pnAltCalendarType, Boolean& pbShowWeeks, Int16& pnFirstWeekOfYear, UInt32& pnFirstDayOfWeek, Int16& pnWorkDays, Int16& pnWorkDayStartHour, Int16& pnWorkDayEndHour, Int16& pnMeetingCount, Int32& plFlags, Boolean& bConnectedToPortal, String& pbstrPortalUrl, String& pbstrPortalName, Int32& plWebTemplateId, Int16& pnProvisionConfig, String& pbstrDefaultTheme, String& pbstrDefaultThemeCSSUrl, String& pbstrThemedCssFolderUrl, String& pbstrAlternateCSSUrl, String& pbstrCustomizedCssFileList, String& pbstrCustomJSUrl, String& pbstrAlternateHeaderUrl, String& pbstrMasterUrl, String& pbstrCustomMasterUrl, String& pbs…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           8gs1    Verbose     …trSiteLogoUrl, String& pbstrSiteLogoDescription, Object& pvarUser, Boolean& pvarIsAuditor, UInt64& ppermMask, Boolean& bUserIsSiteAdmin, Boolean& bHasUniquePerm, Guid& pguidUserInfoListID, Guid& pguidUniqueNavParent, Int32& plSiteFlags, DateTime& pdtLastContentChange, DateTime& pdtLastSecurityChange, String& pbstrWelcomePage, Boolean& pbOverwriteMUICultures, Boolean& pbMUIEnabled, String& pbstrAlternateMUICultures, Int32& puiVersion, Int16& pnClientTag)     at Microsoft.SharePoint.SPWeb.InitWeb()     at Microsoft.SharePoint.SPWeb.get_Title()     at Microsoft.SharePoint.SPSite.OpenWeb(Guid gWebId, Int32 mondoHint)     at Microsoft.SharePoint.Workflow.SPWinOEWSSService.GetWebForWorkflow(SPWorkflow wf, SPWorkflowUserContext runAsUser)     at Microsoft.SharePoint.Workflow.SPWinOEWSSService.get…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           8gs1    Verbose     …_Web()     at Microsoft.SharePoint.Workflow.SPWinOEWSSService.GetWebForListItemService()     at Microsoft.SharePoint.Workflow.SPWinOEWSSService.UpdateModerationStatus(Guid id, Guid listId, SPItemKey itemKey, SPModerationStatusType newModerationStatus, String comments)     at Microsoft.Office.Workflow.Actions.SetTaskProcessItemModerationStatus.DoUpdate(ActivityExecutionContext context)     at Microsoft.SharePoint.WorkflowActions.WaitForDocumentUnlockActivity.Execute(ActivityExecutionContext executionContext)     at Microsoft.Office.Workflow.Actions.SetTaskProcessItemModerationStatus.Execute(ActivityExecutionContext context)     at System.Workflow.ComponentModel.ActivityExecutor`1.Execute(T activity, ActivityExecutionContext executionContext)     at System.Workflow.ComponentModel.ActivityExe…    aed86901-9e96-4e1d-acac-32f5d8bba3d8

Looking at the stack trace, you can see that the code is making a call to the GetWebForWorkflow method of the SPWinOEWSSService.  In reflecting this method, you will see that it can take one of three paths:

  • Execute as the system account
  • Execute as the workflow template author
  • Execute as the workflow template owner

The system account always has access, so it was obvious that this wasn't the path it was taking.  The owner seems to always be set to "-1" and no where in the massive amount of code that I reviewed did it ever get set to anything else.  That left the author as the most probably entry point.  When reviewing where the author comes from, you find a tangled set of code that really doesn't make much sense at all (hmm…SharePoint in all its glory).  After decoding the entanglement, I found that the author comes from some set of properties that end up in a object array for the SPWebWorkflow.  This array is built from a call to SPRequest.GetListItemWorkflowAsSafeArray.  This of course means that you can't go any farther into the code unless you get really fancy building a SPRequest wrapper (which can be done, but its really hard).  Because the properties are built in a somewhat OnDemand fashion, you don't really have any way of knowing when that code will be executed…and because I'm in production, I can't sit here and do IISRESET's all day long watching for some workflow related Stored Procedure via SQL Profiler.

So how did I figure out what was up with this?  Well, the hint was in the SPRequest method call name.  GetListItemWorkflowAsSafeArray.  ListItem?  Which implies the metadata for the workflow is in the list.  Which means the author info is also coming from the listlistitem.  So where do workflows get stored in a siteweb?  They get saved to the \_catlogswfpub library at the site collection root web.

In looking at this library through SharePoint Designer, you will see that when you first activate the workflow features, the files are created by THE USER who actually made the feature activation call (whether through the UI or stsadm or PowerShell).  Hmm, very poor design.

Why is it poor design?  Well, the author comes from the created field for the list item that has the workflow template defined.  In our case, this account was recently DISABLED.  When an account becomes disabled, you can't do JACK in SharePoint.  This includes executing the workflows that were built off the template in the first place. So what are your options?

  • Delete all the files, reactivate the workflows – YOU MUST DELETE THE FILES, if you do not, the files will stay with the created field still set to the old userId (If you have ever taken any of my course, you know rule number one is "SharePoint never deleted anything").  This means you'll still have the ACCESS DENIED error even after you deactivate and re-activate the workflows
  • Try to modify the CREATED and MODIFIED columns in the list to change the SPUser (I didn't try this, but it seems like it should work)

So, have you seen this?  Probably not.  Will every SharePoint farm on the planet eventually have this issue – YES!  You won't keep accounts around forever (unless you have a very poor security policy).  So it is worthwhile to review all your site collections and check to see who the workflow files are owned by.  Hopefully you have an SPAdmin or SPFarm account that you can use to enable the workflows with.  SO…NET NET:

Never enable workflow features as a regular user, always use some SharePoint account that will never be disabled

Yuk, yuk…yuk|
Chris

 

SharePoint Designer 2010 Workflow Bug (Check out causes Exception)

I was doing a demo today around SharePoint Designer 2010 workflows and found a bug in the design.  When you create a workflow and want to edit the xoml file for the workflow, you must check it out.  Why would you do that you ask?  Well, the Designer Interface is far from perfect and you may need to move a step around in case you didn't realize you started to sub-nest your steps. The only way to do this is to edit the .xoml file.  In order to edit the .xoml file, you must "Check Out" the file first.  After you check it out, you can then edit the file in Designer via the XML Editor.

Saving the file but not "Checking It Back in" will cause the file to lose resolution to the SharePoint workflow code.  This will cause the following error in the log files:

System.ArgumentNullException: Array cannot be null.  Parameter name: bytes     at System.Text.Encoding.GetString(Byte[] bytes)     at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.CompileBytes(Byte[] xomlBytes, Byte[] rulesBytes, Boolean doTestCompilation, String assemblyName, SPWeb web, Boolean forceNewAppDomain)

Checking the file back in will allow the workflow to execute again.  

RESOLUTION:  A check out should cause the workflow to be unpublished. You would then need to save and publish it again after check in

 

SharePoint Designer 2010 Workflow UI

Yuk.  It has come a long way, but still has a long way to go.  If users start a workflow and realize they have put a step in the wrong place, you can't drag and drop it anywhere.  You can't cut and paste the condition or actions and you bascially have to start all over again (as an end user).  If you aren't an end user, you can actually open up the XOML file and move the items around, but its not a step you should have to do!

Chris

Customizing SharePoint Approval Workflow – Step by Step

I like challenges and I was posed this one today in class.  Change the built in approval workflow to add steps!  Since I have written a really Advanced Workflow course and know a few things that most people don't about workflows in general, I decided to take on the challenge on the plane ride back to San Diego from Houston.

Attached is the results of my exploits.  Using the WorkflowChanges class, I was able to inject a new activity into the workflow.  I was also able to change the InfoPath form that drive the approval workflow.  The only thing I didn't do was to actually take a new element value out of the modified form.  I have left that for someone else to figure out!

 If you are using FireFox, right click the file and select download…

Enjoy!
Chris