How to Create a Timer Job with SharePoint Designer 2013

In this post I’m going to show you how to create a no server side code timer job using a SharePoint Designer 2013 site workflow.

Scenario

We have a SharePoint Online (Office 365) site with a task list in it and we need to send daily alerts to users that have overdue tasks assigned to them.

sp_task_list

Problem

The obvious solution would be to create a timer job to do that… Unfortunately we are in an Office 365 environment so we do not have the possibility to implement a standard timer job using server side code.

office_365_red

Solution

We can simulate the behaviour of a timer job using a Sharepoint Designer 2013 site workflow.

Steps

1. Create a New Site Workflow in Sharepoint Designer 2013

Open SharePoint Designer 2013 and create a new site workflow

site_wotkflow

2. Get All Overdue Tasks that are not Completed

In the first stage (let’s call it “Getting Overdue Tasks”) build a dictionary to store the accept header for our rest request and get results in json:

accept: application/json;odata=verbose

tj_build_dictionary

tj_accept_header

Put the current date in a DateTime variable (let’s call it “today”).

tj_today

Then call the SharePoint REST Service to get the overdue tasks.
To do so insert the “Call HTTP Web Service” action in the workflow (in an App Step to avoid permission related problems).

tj_call_service

The action url must be constructed dynamically  using the “today” variable like this:

tj_request_endpoint

tj_today_iso_formatted

Set the action verb to GET, the request headers to the headers dictionary previously built and the response content to a dictionary to store the request result.

tj_rest_call_properties

Get and store the returned tasks in a separate dictionary to count them and loop through them later.

tj_store_tasks

3. Send Alerts to Overdue Tasks Assignees

Create a second stage (let’s call it “Sending Alerts”) to send alerts and then link the first stage to it.

Insert a loop and inside it extract the task id from the rest response and send an email to each overdue task assignees.

Use a loop index variable to address the current task and remember to initialize it to zero and increment it properly at the end of the loop.

tj_loop

tj_mail

To get the email address of the recipient (the task assignee) use the task id previously extracted from the rest response like this:

tj_recipient

4. Wait One Day and Start Over Again

Insert a pause action and configure it to wait one day.

Link the “Sending Alerts” stage to the “Getting Overdue Tasks” stage to start over again.

tJ_pause

Conclusion

Here is how the complete workflow will look at the end of the process:

tj_complete_workflow

Publish the site workflow and start it (you can access the “Site Workflows” page from the “Site Contents” page).

tj_start_wf

It will send the alerts daily as expected.

tj_receive_mail

References

Fabian Williams

4 Steps to Get the Real Error Message from SharePoint 2013 Log Files

In this post I’m going to show you how to find the details of an error in the SharePoint log files.

Scenario

Sorry, something went wrong. An unexpected error has occurred.

Many of us have seen this non-informative message before, wondering what really happened.

FindError_Message

We need a way to get the real SharePoint exception to investigate the problem.

Solution

We can do that by using the SharePoint “Unified Logging System” (ULS).

SharePoint saves the details of errors and other diagnostic information in a log file on the server where the error happened.

Steps

1. Get Correlation ID

The first step is to note the Correlation ID.
You can directly copy it from the browser in the SharePoint error page.

FindError_CorrelationID

2. Get and Open ULS Viewer

You can open log files with Notepad or your text editor of choice.

However Microsoft has a tool called ULSViewer that can help you find your error more easily with the use of filters.

FindError_ULSViewer

3. Open Log File

Now you need to find and open the specific log file that contains your error.

To open a log file click on File -> Open From -> File.

FindError_OpenFile

By default SharePoint log files are stored in the SharePoint hive in the LOGS folder:

C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\LOGS

FindError_LogFolderHowever note that this path can be changed in SharePoint Central Administration.

SharePoint log files are named with the format “SERVERNAME-YYYYMMDD-HHMM.txt” where SERVERNAME is the server’s computer name, YYYYMMDD is the current date and HHMM is the time the log was started using a 24-hour clock.

In single server farms open the log from the day and time which the error happened.

In multi-server farms you’ll need to go to the specific server where the error happened to locate the log. If it’s not immediately obvious where the error happened you’ll need to check every server in the farm.

FindError_LoadedLogFile

4. Filter Log Entries

To find your error you will need to filter the log entries by Correlation ID and Level.

To do this click on Edit -> Modify Filter.

FindError_ModifyFilter

Then set the following values in the Filter By dialog.

Correlation ID filter:

Field: Correlation
Operation: Equals
Value: The Correlation ID you copied from the SharePoint Error message
And/Or: And

Level filter:

Field: Level
Operation: Equals
Value: Unexpected

FindError_Filters

Click OK and ULSViewer will show only the events that have the Correlation ID and Level specified.

FindError_FilteredLogEntries

Conclusion

Now we can finally see the real error!

If you double click on the log entry, a window will open to show all the error details so that you can begin your investigation.

FindError_ErrorDetails

References

Jason Warren

4 Steps to Dynamically Display Locations on a Map in SharePoint 2013

In this post I’m going to show you how to display on a map the locations contained in a SharePoint 2013 list enabling the new Geolocation Field and Map View.

Note: The proposed solution works both for SharePoint 2013 On Premises and Online.

Scenario

We want to dynamically display the locations contained in a list on a Bing Maps map in a SharePoint 2013 site.

locationsOnMap

Steps

1. Create the list

Create the list that will contain the locations to show on the map.

In our example the list has simply Title and Description fields.
Later in this post we’ll learn how to add the location field.

locationList

2. Get a Bing Maps key

You can obtain a valid Bing Maps key from here.

bingMapsKey

3. Create the location field

To create the location field on our list we need to use a custom form in a page containing a script.

So first create a page where you wish to place the custom form.

formPage

On that page add a Script Editor web part that will contain both the script and html for our form.

scriptEditor

First add this javascript:


<script type="text/javascript">
var clientContext;
var relativeAdress;
 
 function AddGeolocationField() {
 ClearNotifications();
 GetRelativeAdress();
 clientContext = new SP.ClientContext(relativeAdress); 
 var web = clientContext.get_web();
 
 var targetList = web.get_lists().getByTitle(document.getElementById('listname_input_id').value);
 var fields = targetList.get_fields();
 fields.addFieldAsXml(
 "<Field Type='Geolocation' DisplayName='" + document.getElementById('fieldname_input_id').value + "'/>",
 true,
 SP.AddFieldOptions.addToDefaultContentType);
 
 clientContext.load(fields);
 clientContext.executeQueryAsync(function (sender, args) {
 document.getElementById('success_id').innerHTML = "You have succesfully created new geolocation field.";
 },
 function (sender, args) {
 document.getElementById('error_id').innerHTML = "Error: "+args.get_message();
 });
 }
 
 
 function SetBingKey() {
 ClearNotifications();
 GetRelativeAdress();
 clientContext = new SP.ClientContext(relativeAdress);
 
 var web = clientContext.get_web();
 var webProperties = web.get_allProperties();
 
 webProperties.set_item("BING_MAPS_KEY", document.getElementById('bing_key_id').value);
 web.update();
 clientContext.load(web);
 
 clientContext.executeQueryAsync(function (sender, args) {
 document.getElementById('success_id').innerHTML = "You have succesfully entered BING map key on "+web.get_title()+" site";
 }, function (sender, args) {
 document.getElementById('error_id').innerHTML = "Error: "+args.get_message();
 });
 }
 
 function GetBingKey() {
 
 ClearNotifications();
 GetRelativeAdress();
 
 clientContext = new SP.ClientContext(relativeAdress);
 var web = clientContext.get_web();
 
 var webProperties = web.get_allProperties();
 
 clientContext.load(webProperties);
 
 clientContext.executeQueryAsync(function (sender, args) {
 document.getElementById('bing_key_id').value = (webProperties.get_fieldValues()["BING_MAPS_KEY"]);
 
 }, function (sender, args) {
 document.getElementById('bing_key_id').value = "";
 document.getElementById('error_id').innerHTML = "Property not found! Please check your web site relative URL.";
 });
 }
 
 function ClearNotifications(){
 document.getElementById('success_id').innerHTML = "";
 document.getElementById('error_id').innerHTML = "";
 }
 
 function GetRelativeAdress(){
 if (document.getElementById('webrelative_url_id').value === "")
 relativeAdress = "/";
 else
 relativeAdress = document.getElementById('webrelative_url_id').value;
 if(relativeAdress.charAt(0)!='/')
 document.getElementById('error_id').innerHTML = "Relative adress has to start with /";
 }
 
 </script>

Then add this html:

<table style="width: 480px;">
<tbody>
<tr>
<td style="width: 200px;">Web relative URL:</td>
<td style="width: 5px;">&nbsp</td>
<td valign="top"><input id="webrelative_url_id" name="relative" type="text" value="/">
<label style="font-size: 8pt;">
* Input web relative URL and select "Get BING key" to check if key is set</label></td>
<td style="text-align: right;" valign="top"><input onclick="GetBingKey()" type="button" value="Get BING key"></td>
</tr>
<tr>
<td style="width: 200px;" valign="top">Bing Maps Key:</td>
<td style="width: 5px;">&nbsp</td>
<td valign="top"><input id="bing_key_id" name="bingkey" type="text">
<label style="font-size: 8pt;">
* Input Bing map key and relative url to web site to wich you wish to add the key</label></td>
<td style="text-align: right;" valign="top"><input onclick="SetBingKey()" type="button" value="Set BING key"></td>
</tr>
<tr>
<td style="width: 200px;" valign="top">List name:</td>
<td style="width: 5px;">&nbsp</td>
<td valign="top"><input id="listname_input_id" name="listname" type="text">
<label style="font-size: 8pt;">
* Name of the list where you wish to add your new geolocation field</label></td>
<td></td>
</tr>
<tr>
<td valign="top">Field name:</td>
<td style="width: 5px;">&nbsp</td>
<td valign="top"><input id="fieldname_input_id" name="fieldname" type="text">
<label style="font-size: 8pt;">
* Name of the new geolocation field you wish to add</label></td>
<td style="text-align: right;" valign="top"><input onclick="AddGeolocationField()" type="button" value="Create field"></td>
</tr>
</tbody>
</table>
<label id="error_id" style="color: red;">
</label>
<label id="success_id" style="color: green;">
</label>

And now you have your form for adding geolocation fields anywhere on your site collection.

geolocationForm

Once you have entered the relative path to your web you can use the Set BING key button to add the BING maps key to the web.
You can optionally use the Get Bing key button to check if you have already a BING maps key placed in that web.

bingMapsKeyEntered.png

Once entered List name and Field name you can use the Create field button to add a geolocation field with the name you specified to the list on the web.

locationFieldEntered

geolocationField

Note: For SharePoint 2013 On Premises an MSI package named SQLSysClrTypes.msi must be installed on every SharePoint front-end web server to view the geolocation field value or data in a list.  This file is installed by default for SharePoint Online.

More information on this can be found here.

4. Create the Map view

You now have the possibility to create a Map view for your list to show locations.

mapView

mapViewFields

Conclusion

You can now add items to your list and see their locations appear on the Map view.

insertLocation

locationsOnMap

References

MSDN
Borislav Grgić

How to Export a SharePoint 2013 List with Lookup Columns

In this post I’m going to show you a workaround to an issue that causes data loss when exporting lists with lookup columns to another site.

Scenario

We have 2 lists in a SharePoint 2013 site, one with a lookup column to the other, and we need to export them to another site without loosing any data.

Export List with Lookup - List A

Export List with Lookup - List B

Problem

In the List Settings page of our lists we can use the “Save as Template” functionality to export them.

Export List with Lookup - Save as Template

The list templates are successfully created so we can load them to the destination site collection and recreate the lists from them in the new site.

Note: the destination site language must be the same as the source one

Export List with Lookup - List Templates

However looking closer to List B we note that all the data in the lookup column has been lost…

Export List with Lookup - List B with Lost Data

This happens because the lookup column points to List A GUID, not its path or name…

The GUID of List A in the destination site is different from that of the same list in the source site, so the lookup column is now pointing to a list that does not exist in the new environment.

Unfortunately we can’t change the lookup column definition in the destination site and get our data back because the “Get information from: ” section is locked…

Export List with Lookup - Lookup Column Locked

So, how to solve this problem?

Solution

We can change the list template manifest file and set the correct list GUID or path for the lookup column.

Let’s see how to do that in detail…

Steps

1. Change the list template file extension

Download the template file of our list with the lookup column (in our example the file is ListBTemplate.stp) and change its file extension from “stp” to “cab”.

Export List with Lookup - List B CAB Extension

2. Extract the files contained in the list template cab package

You can use WinRAR or WinZip to extract the files contained in the cab file

Export List with Lookup - List B Extracted Files

3. Change the list template manifest file

Open the manifest.xml file contained in the list template extracted files (in our example it is the only file) and replace the lookup column list GUID (in our example the GUID of list A in site A, f8088b28-1434-4b51-96d3-a36e945e5146) with the new list GUID or relative path (in our example the GUID or relative path of list A in site B, 265bffc8-dcc9-48e3-99e4-d3ad1d5e2260 or Lists\List A).

Export List with Lookup - Manifest

4. Recreate the list template package

Now that we have changed the content of our manifest file we need to recreate the cab package. To do that we ca use the makecab command in the command prompt using the following syntax:

makecab "<source file>" "<destination file>"

In our example the specific command is as follows:

makecab "C:\List Templates\ListBTemplate\manifest.xml" "C:\List Templates\ListBTemplate\ListBTemplate.cab"

Export List with Lookup - makecab

Export List with Lookup - makecab result

Once created the cab file we can change its file extension to “stp”, reload it to the destination site collection and recreate our List B.

Conclusion

This is how our new list with the lookup column should look like:

Export List with Lookup - List B without Lost Data

You can see that our lookup data is there again without any loss.

References

dotNETgeekster

How to Create a List Item Inside a Folder Using a SharePoint Designer 2013 Workflow

In this post I’m going to show you how to create a list item inside a folder in a SharePoint Designer 2013 workflow even though the “Create List Item” built-in action and even the new rest api do not allow us to do that.

Scenario

We have a folder inside a custom list and we need to automatically create items inside it using a SharePoint Designer 2013 workflow.

Item in Folder- Folder

Problem

In a SharePoint 2010 workflow we used to simply set the “Folder” property in the built-in action “Create List Item”… and it worked! Our item was correctly created inside the specified folder.  Unfortunately in a SharePoint 2013 workflow setting the “Folder” property has no effect. Even the new SharePoint 2013 rest api does not come to our rescue.

Solution

We can use the old listdata.svc service to achieve our goal making a rest call to it inside our SharePoint Designer 2013 workflow.

Steps

1. Create the headers dictionary

We must build a dictionary to store some headers, specifically the following ones:

Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose

Item in Folder- Headers.png

2. Create the data dictionary

Next, we need a dictionary that will contain our request data.

The dictionary must have an entry for each item mandatory fields and an entry to set the item path.

Title: Item 1
Path: <list url>/Folder 1

Item in Folder- Data

The item path must be constructed dynamically to point to the folder we want to place our item in.

Item in Folder- Path

3. Call the SharePoint REST service

Finally, we are ready to call the SharePoint REST service to create our item inside the list folder.

To do this we need to insert the “Call HTTP Web Service” action in our workflow.
The action url must be constructed dynamically like this:

https://site/_vti_bin/listdata.svc/SimpleList

Item in Folder- Endpoint.png

The http method must be set to POST.

Item in Folder- Method

The request headers must be set to the headers dictionary.
The request content must be set to the data dictionary.

create_list_folder10

Conclusion

This is how the complete workflow should look like:

Item in Folder - Workflow

Now we can publish and run it and see that a new item is correctly created inside our folder.

Item in Folder- Folder Workflow Completed

Item in Folder- Item Workflow Completed

References

MSDN

How to Approve a List Item Using a SharePoint Designer 2013 Workflow

In this post I’m going to show you how to approve a list item in a SharePoint Designer 2013 workflow even though the “Set Content Approval Status” built-in action is no longer available in SharePoint 2013 On-Premises.

Note: the issue this post is about has been solved in Sharepoint 2013 Online (Office 365).

Scenario

We have a custom list that requires content approval for submitted items and we need to automatically approve them when they are created using a SharePoint Designer 2013 workflow.

ApprovalList

Problem

It seems quite simple, like in a SharePoint 2010 workflow there should be a built-in action to do this… but unfortunately this is not the case. In a SharePoint Designer 2013 workflow there is no such action as the old and dear “Set Content Approval Status”.

So we need to find a different way to achive the same result and as always with SharePoint 2013, the REST API comes to our rescue.

Solution

We can use the SharePoint REST API to update a list item and set its approval status.

Now we only need to know which particular property of the item sets its approval status…
We can find that by inspecting the item properties through a rest call via a browser like this:

https://site/_api/web/lists/getbytitle('listname')/items(ID)

ApprovalStatusProperty
You can see that the only property that could do our job is the one called “OData__ModerationStatus” (note the double underscore before the “ModerationStatus” string).

The possibile values for this property are the following:

  • Approved: 0
  • Rejected: 1
  • Pending: 2

So now we need to update this field.

Let’s see how to do that in detail…

Steps

1. Get the list item type

To get the list item type we need to use the SharePoint REST API via a browser like this:

https://site/_api/web/lists/getbytitle('listtitle')

and get the value for the “d:ListItemEntityTypeFullName” element.

In the example below the list item type is “SP.Data.Test_x0020_ListListItem” (the “_x0020_” string replaces the space inside the list name).

ApprovalListItemType

2. Create the headers dictionary

We must build a dictionary to store some headers, specifically the following ones:

Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose
Content-Length: <length of post body>
X-HTTP-Method: MERGE
If-Match: *

create_list_folder5

3. Create the metadata dictionary

Next we need to create a dictionary to store our metadata.
This dictionary will contain only one entry for the list item type we’ve obtained previously:

type: SP.Data.Test_x0020_ListListItem

ApprovalMetadata.png

4. Create the data dictionary

Next, we need one more dictionary that will contain our request data.

The dictionary must have an entry with key “__metadata” (note the double underscore before the “metadata” string) with its value set to the previously created metadata dictionary.

We also need to add another entry in the data dictionary with its key set to “OData__ModerationStatus” and its value set to 0 (Approved).

__metadata: <metadata dictionary>
 OData__ModerationStatus: 0

ApprovalData

5. Call the SharePoint REST service

Finally, we are ready to call the SharePoint REST service to update our item approval status.

To do this we need to insert the “Call HTTP Web Service” action in our workflow.
Put it inside an App Step to make sure the REST call is executed with elevated privileges.

To see how to enable the use of App Steps in your workflow and give it elevated privileges see this article.
To see pending items in the same web of the workflow you need to follow the above procedure with Scope=”http://sharepoint/content/sitecollection/web”.
To see pending items in the same site collection of the workflow but in a different web you need to follow the above procedure with Scope=”http://sharepoint/content/sitecollection”.

The action url must be constructed dynamically to point to the current item like this:

https://site/_api/web/lists/getbytitle('listtitle')/items(n)

ApprovalEndpoint

The action verb must be set to POST.

create_list_folder9

Set the request headers to the headers dictionary.
Set the request content to the data dictionary.

create_list_folder10

6. Change workflow default settings

At this point it is very very important to uncheck the “Automatically update the workflow status to the current stage name” option, otherwise during the workflow execution the item will be updated and its approval status reset to “Pending”.

We also set our workflow to start automatically when an item is created.

ApprovalWorkflowSettings

Conclusion

This is how the complete workflow should look like:

ApprovalWorkflow

Now we can publish it and see that when we create a new item the workflow will start automatically and correctly approve it.

ApprovalItem

References

MSDN

StackOverflow

How to Create a Folder in a SharePoint 2013 List Using a SharePoint Designer 2013 Workflow

In this post I’m going to show you a workaround to an issue with SharePoint Designer 2013 workflows in correctly creating a list folder.

Scenario

We have a custom list and we need to dynamically add folders to it through a SharePoint Designer 2013 workflow.

create_list_folder12

Why

A first question could be:

Why on earth would you need to create a folder in a list? Folders are intended for libraries and files!

There are scenarios when you need to have folders in your list, one of this is to manage permissions at a finer level than the list level, avoiding having to set unique permissions for each list item.

A second question could be:

Why on earth do you need to create folders with a workflow?

There could be scenarios in which folders need to be created dynamically in reaction to some event.

Problem

A list folder is simply a special type of list item.
So it seems quite simple… In the workflow use the “Create List Item” action and set its content type to “Folder”. Yes, it works! The folder is created!

But wait… Clicking on the folder an ugly system name appears in the breadcrumbs (ID_.000)… Oh no! What’s that?!?

create_list_folder2

This happens because there is a mismatch between the title of the folder that we can set in the workflow and its name that is assigned automatically (respectively “Folder 1” and “66_.000” in the example above).

Unfortunately we can’t set the name of a list folder directly using the create or update list item workflow actions.

Solution

We can use the SharePoint REST API to change the list folder name after having created it.

Now we only need to know which particular property of the list folder sets its name…
We can find that by inspecting the folder properties through a rest call via a browser like this:

https://site/_api/web/lists/getbytitle('listname')/items(ID)/FieldValuesAsText

create_list_folder3
You can see that value “ID_.000” is hold by the field “FileLeafRef”.
So now we need to update this field of our newly created folder using REST.

Let’s see how to do that in detail…

Steps

1. Get the list item type

To get the list item type we need to use the SharePoint REST API via a browser like this:

https://site/_api/web/lists/getbytitle('listtitle')

and get the value for the “d:ListItemEntityTypeFullName” element.
In the example below the list item type is “SP.Data.TestFolderListListItem”

create_list_folder4

2. Create the headers dictionary

We must build a dictionary to store some headers, specifically the following ones:

Accept: application/json;odata=verbose
Content-Type: application/json;odata=verbose
Content-Length: <length of post body>
X-HTTP-Method: MERGE
If-Match: *

create_list_folder5

3. Create the metadata dictionary

Next we need to create a dictionary to store our metadata.
This dictionary will contain only one entry for the list item type we’ve obtained previously:

type: SP.Data.TestFolderListListItem

create_list_folder6

4. Create the data dictionary

Next, we need one more dictionary that will contain our request data.

The dictionary must have an entry with key “__metadata” (note the double underscore before the “metadata” string) with its value set to the previously created metadata dictionary.

We also need to add another entry in the data dictionary with its key set to “FileLeafRef”.

__metadata: <metadata dictionary>
 FileLeafRef: Folder 1

create_list_folder7

5. Call the SharePoint REST service

Finally, we are ready to call the SharePoint REST service to update our folder name field.
To do this we need to insert the “Call HTTP Web Service” action in our workflow.
The action url must be constructed dynamically to point to the newly created folder like this:

https://site/_api/web/lists/getbytitle('listtitle')/items(n)

create_list_folder8

The action verb must be set to POST.

create_list_folder9

Set the request headers to the headers dictionary.
Set the request content to the data dictionary.

create_list_folder10

Conclusion

This is how the complete workflow should look like:

create_list_folder11

Now we can publish our workflow and run it. Once it is over we’ll have our folder correctly created with its title and name matching.

create_list_folder12

create_list_folder13

References

StackExchange

CSR and MDS: 4 Steps to get the Best from both Worlds

I’m going to show you how to create a CSR (or JSLink) script that works with and without MDS and follows coding best practices.

Scenario

Both CSR (Client-Side Rendering) and MDS (Minimal Download Strategy) are new concepts in SharePoint 2013.
CSR lets you control how your list data is displayed using Javascript.

CSR

MDS improves rendering performance where large parts of the page do not change, providing a more fluid navigation experience.

MDS.png

Problem

The problem is that CSR works fine with MDS only on the first page request or after a refresh. As soon as the user switches between views or navigates around and then comes back to the original page, the CSR customization vanishes and the default rendering takes place again.

I’ll try to explain what happens here.
When the page is first rendered, the MDS system receives the custom script for the first time and puts it in a list of already executed scripts. When subsequent pages are requested, MDS checks its own list of already executed scripts and will simply not execute them again.

However you want the CSR script to execute every single time… So, how to achieve that without disabling MDS feature and keeping its performance gains?

Solution

The following CSR script comprises 4 steps:

  1. Following Coding Best Practices
  2. Addressing MDS Garbage Collection issue
  3. Addressing MDS Script Reload issue
  4. Making it work without MDS as well
Type.registerNamespace('Client');
Client.Project = Client.Project || {};
Client.Project.Templates = Client.Project.Templates || {};
Client.Project.Functions = Client.Project.Functions || {};

Client.Project.Functions.Display = function (context) {
var currentValue = context.CurrentItem.MyFieldInternalName
if (currentValue > 0) {
return currentValue;
}
else {
return '<span style="color:red">' + currentValue + '</span>';
}
}

Client.Project.Templates.Fields = {
'MyFieldInternalName': {
'DisplayForm': Client.Project.Functions.Display,
'View': Client.Project.Functions.Display,
'NewForm': null,
'EditForm': null
}
}

Client.Project.Functions.OverrideTemplates = function () {
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(Client.Project);
}
Client.Project.Functions.MdsOverrideTemplates = function () {
var scriptUrl = _spPageContextInfo.siteServerRelativeUrl + "/SiteAssets/CSRScript.js";
Client.Project.Functions.OverrideTemplates();
RegisterModuleInit(scriptUrl,Client.Project.Functions.OverrideTemplates);
}

if (typeof _spPageContextInfo != "undefined" && _spPageContextInfo != null) {
Client.Project.Functions.MdsOverrideTemplates();
} else {
Client.Project.Functions.OverrideTemplates();
}

Let’s examine the above script in more details.

1. Following Coding Best Practises

Define your own namespaces to avoid polluting the global one.

Client.Project = Client.Project || {};
Client.Project.Templates = Client.Project.Templates || {};
Client.Project.Functions = Client.Project.Functions || {};

2. Addressing MDS Garbage Collection issue

Register your root namespace to avoid MDS Garbage Collection to wipe it away during page transitions.

Type.registerNamespace('Client');

3. Addressing MDS Script Reload issue

Register your script to have MDS system reload it every time, even after a page transition.

var scriptUrl = _spPageContextInfo.siteServerRelativeUrl + "/SiteAssets/CSRScript.js";
RegisterModuleInit(scriptUrl, Client.Project.Functions.OverrideTemplates);

Append a revision number to the JSLink attribute of your custom field definition and update it at any script change/deploy in order to prevent browsers from using the old cached version.

<Field
      ID="{ce3d02df-d05f-4476-b457-6b28f1531f7c}"
      Name="MyFieldInternalName"
      DisplayName="My field display name"
      Type="Number"
      Required="FALSE"
      JSLink="~sitecollection/SiteAssets/CSRScript.js?rev=1.0.0.0"
      Group="My Custom Group">
</Field>

4. Making it work without MDS as well

If MDS is disabled the _spPageContextInfo object is undefined in the script inline code.
If that is the case there is no need to register your script and you can call the template override function directly.

if (typeof _spPageContextInfo != ‘undefined’ &amp;amp;amp;amp;&amp;amp;amp;amp; _spPageContextInfo != null) {
   Client.Project.Functions.MdsOverrideTemplates();
} else {
   Client.Project.Functions.OverrideTemplates();
}

References

Wictor Wilén

Martin Hatch

Technet

Why SharePoint 2013 Calculated Columns will Save your Life – Filtering Lookup Values

I’m going to show you a no-code, out-of-the-box, simple way to create a filtered lookup column which will work with SharePoint 2013 On Premises and Online.

Scenario

A client comes up with the following requirement:

We have two lists

  • List A (Title, IsSelectable)
  • List B (Title, Lookup to List A)

We want to filter lookup values so that only selectable items are shown to users to choose from.

List A and B

Solution

Use a calculated column in List A and write the formula like this:

=IF(IsSelectable,Title,””)

Tip: if you receive a syntax error try and replace the “,” (comma) with “;” (semi-colon). Site regional settings can affect the syntax used in SharePoint.

List A and B - Filter Applied

Pros

  • Out-Of-The-Box only
  • No InfoPath forms
  • No SharePoint Designer
  • No JavaScript/JQuery/XSLT
  • No Visual Studio code

Time to implement: 3 minutes.

Cons

If you should change an item to be no longer selectable then in the list where you have used it its value disappears. That behavior could be ok for most scenarios but be aware of it.

Alternative Solutions

There is a solution on Codeplex which creates a custom site column to handle this. However that solution is not compatible with SharePoint Online because created using server side code.

References

MSDN

StackExchange

Balestra

April Dunnam