Archive for February, 2017

Fake a Required Field on a SharePoint List Form

As with so many things I write about recently I had a need to do something different at my client site.  This time I needed to fake a required field on a SharePoint list form.  The reason behind it was we had customized the form and had some fields that were required in some cases, but not all.  These fields could not be marked as required in the content type because then we would need to fill in default or bogus data into the fields and that wouldn’t look good so I decided to simply make the fields look exactly like a SharePoint required field without actually setting them up that way.

Fake a Required Field on a SharePoint List Form

This may already be documented somewhere, but I didn’t bother to look.  I thought the solution I came up with is pretty quick and clean so I wanted to share it.  I am basically just inserting the same <span> field that SharePoint does onto a label when the field is set as a required field.  Using JQuery I get the <nobr> control and then insert the <span> after.  Here’s the code to accomplish that:

/********************************************************************************************
*Function Name: displayAsRequired                 											*
*Parameters: $fieldTitle - Title of the field                								*
*Purpose: Makes a control appear as if it were required, without making it required in SP   *
/********************************************************************************************/
function displayAsRequired(fieldTitle) {
    var reqField = $(".ms-formlabel nobr").filter(function (){
        return $(this).contents().eq(0).text() === fieldTitle;
    }).after("<span title='This is a required field.' class='ms-accentText'> *</span>");
}

So you just need to pass in the field name (display name, not the internal name) to the function.  The code will find the corresponding <nobr> control on the form and update it to look like a required field.  Kudos to a comment Marc Anderson made in a blog somewhere about accessing the <nobr> field.  I would have done it a bit differently, but I liked the flow of the code in the comment he made.

Thanks for reading!!

Field or Property ‘TimeZoneId’ does not exist when using SharePoint Search

Our test environment was giving me issues today.  It’s an error that was reported and supposedly fixed nearly 18 months ago.  When typing a query in the text box, the initial search works, but when using a refiner or changing the query the result would return an error Field or Property ‘TimeZoneId’ does not exist.  This was first reported after the July 2015 CU as a regression and supposedly fixed in the August 2015 CU.  The problem was in the Microsoft.Office.Server.Search.ServerProxy.dll.  Now I admit our servers are out of date when it comes to CUs (something I have been trying to get time from my client to do) but we are well beyond August 2015 CU.

Our environment is setup with Search being provided by our App servers in a Shared Services configuration.  When checking the WFE I found the dll version to be at 15.0.4815.1000.  We have two app servers that are running the search services.  App#1 had the same version of the dll, but App#2 was actually sitting at an older version: 15.0.44020.1017.  I believed this to be the culprit.

Resolving: “Field or Property ‘TimeZoneId’ does not exist

  1. Shut down IIS on the server you need to update.
  2. From a server with the correct version copy Microsoft.Office.Server.Search.ServerProxy.dll to the _app_bin folder of the SharePoint virtual directory (example: C:\inetpub\wwwroot\wss\VirtualDirectories\AppServer\_app_bin)
    • If you don’t have a “good” location, the correct file should also be located in “C:Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\CONFIG\BIN”
  3. Restart IIS and test.  Should be fine now.

The cause is likely this single file not being copied over properly when patched.  Hope this helps someone still having this issue.

 

Thanks for reading!

Start SharePoint 2013 Workflows with PowerShell

Recently was working on a small project that ended up requiring me to start SharePoint 2013 workflows with PowerShell scripts.  This turned out to be a necessity because of a few things.  I was finishing off some development recently on a smaller project that required a significant amount of JavaScript injection code.  To be terribly honest, had I known at the time it would have taken as much as it did, I would have rewritten it as a full Add-In, but that’s a story for another day.  To make a long story short because of the way the form had been written by another developer we were saving attachments to the list manually from the form (not allowing SharePoint to do it for us).  Because we had workflows running on item added, we were having save conflicts because the items could not be added until after the form data was initially inserted into the list.  Basically getting a kind of race condition between the workflows and the list item update.

My first thought was to simply disable the workflows from starting automatically and instead initiating them from the form using JSOM.  This however wasn’t going to work because while the code I wrote worked perfectly fine (I’ll blog on that later), I forgot the solution also had a mobile app feeding into it.  The mobile app wasn’t able to use the same model as I could and SharePoint 2013 workflows is one of the few places REST needs to be vastly improved.  In theory it can be done simply by using the ExecuteProcess endpoint and enter the necessary information in the body of the transmission.  However, I had never done this and didn’t have a lot of time left to figure it out (I’ll post something here if I get around to working it out).  So that left a PowerShell Script and scheduled tasks.

Start SharePoint 2013 Workflows with PowerShell

So now let’s get to why you’re here.  How to initiate SharePoint 2013 workflows from PowerShell.  The gist of my solution was this:

  1. Build a search query for every item created in the past 10 minutes (scheduled task will run every 5 min so there will be plenty of overlap).
  2. Loop through each item returned and check to see if the workflows have run based on the workflow status field created.
  3. If no workflow for that item, initiate the workflow.

Search Query

The query is pretty straight forward:

#############################################################################################
#Function Name: BuildItemsCreatedMinutesInPastQuery			     							#
#Parameters: $minutesInPast - Number of minutes to subtract from current time				#
#                                                                                           #
#Return Value: SPQuery object																#
#Purpose: Creates an SPQuery object that will check against the Created Date subracting the #
#         number of minutes passed in to the function          								#
#############################################################################################

function BuildItemsCreatedMinutesInPastQuery([Int]$minutesInPast)
{
    $setMinInPast = $minutesInPast * -1;
    $currentTime = (Get-Date).AddMinutes($setMinInPast);
    $formatTime = $currentTime.ToString('s');
    
    $spQuery = New-Object Microsoft.SharePoint.SPQuery;
    $spQuery.Query =
       "<Where>
            <Geq>
                <FieldRef Name='Created' />
                <Value IncludeTimeValue='TRUE' Type='DateTime'>$formatTime</Value>                
            </Geq>
        </Where>"

    return $spQuery;
}

The script only takes in the number of minutes you want to look into the past.  You get the current date and time, back it up the number of minutes you want, convert it to ISO 8601 format and build it into your query to find any items later than that time.  If you aren’t up on CAML query, the code basically translates to “Get all items, where the field ‘Created’ (of type DateTime) is greater than or equal (<geq>) to the date I want in ISO 8601 format.

Initiate the Workflow

When initiating the workflow there are a couple of steps you need to do first:

  1. Connect to the SharePoint Workflow Services and create a Workflow Services Manager object
  2. Connect to the subscription service
  3. Get the workflow subscription you are looking for (searching an enumerated list using the workflow name)
    • You are checking all the workflow associations (definitions) within the site and grabbing the one you are looking for
  4. Build an empty dictionary.  This is used to hold the parameters of the workflow if there was an initiate form gathering information.
  5. Creating an object of the workflow instance service and initiating the workflow against the item in question.
#build the WF Manager and Subscription Services objects to manipulate the WFs in the site
$spWFMgr = New-Object Microsoft.SharePoint.WorkflowServices.WorkflowServicesManager($spWeb);
$spWFSubSvc = $spWFMgr.GetWorkflowSubscriptionService();

#try to find the WF to initiate
$spWorkflow = $spWFSubSvc.EnumerateSubscriptions() | ?{$_.Name -eq $wfName}

if($spWorkflow)
{
    #connect to the Instance service, build empty parameter dictionary and initiate the WF.
    $spWFInstanceSvc = $spWFMgr.GetWorkflowInstanceService();
    $wfParams  = New-Object 'system.collections.generic.dictionary[string,object]'

    $wfGUID = $spWFInstanceSvc.StartWorkflowOnListItem($spWorkflow, $spListItem.ID, $wfParams);
}
else
{
    $returnMessage = ("Workflow: {0} not found" -f $wfName);
}

That’s it.  These few lines, find the workflow you need, create an instance of it against the item you wish and then start the workflow.

You next step is to build it all together and then toss it into a scheduled task to run every 5 minutes or however often you need it to run.

Thanks for reading!