Using multiple CSR Template Overrides on one Page

SharePoint 2013 ClientSideRendering is a great tool to customize list rendering. But when you put two or more ListViewWebParts on one page and want to use csr, things get complicated. This is because the registered template overrides are executed on all lists, even when you use JS-Link option in WebPart properties.
Microsoft tries to solve this problem by filtering the template overrides by ListTemplateType, BaseViewID and ViewStyle. But when you set up two (or more) custom lists via frontend, the all get ListTemplateType 100. And what happens, when you use two times the same list on one page…?
Now, after more than 2,5 years it was time to solve this problem. (Here is another solution: [1]).

Idea of CSR Framework to support multiple template overrides

My solution is to write one general template override which gets handed over multiple template override objects with filters. By evaluating the filters, the single general template override can decide if the code in an overidden object has to be executed or the default renderer has to be used.

Example: Footer Template Override

I will demonstrate the technique on the footer template. Here is a footer template override:

var leftWebPart_Override = {	
	Templates: {
		Footer: function(ctx) {
			return "Custom WP Footer 1";
		},
	}
};

As you see, this is standard SharePoint notation.
Then I define a CTXFilter:

var leftWebPart_CTXFilters = {		
	ListTitle: "CSR-Test-List"
};

One of my test list has the title ‘CSR-Test-List’.
Now, I register the override using my AtiaNuur.CSR Framework and initialize it:

AtiaNuur.CSR.RegisterTemplateOverrides(rightWebPart_Override, rightWebPart_CTXFilters);
AtiaNuur.CSR.init();

The output is something like this:
AtiaNuur.CSR
You see the customized WebPart on the left and a second uncustomized WebPart on the right. Both are custom lists (with ListTemplateType 100).

AtiaNuur.CSR Framework in detail

So what happens in AtiaNuur.CSR. The framework follows three steps. It doesn’t matter if it’s footer-, header-, body-, OnPreRender, fields- etc. override. Therefore I’ll demonstrate it on the footer template override. You can get the rest out of the code. These steps are:

  1. Filling an array with objects of render-function and filters
  2. Filtering on current context
  3. Calling rendering function

I’ll start in reverse order, as the rendering function is the most interesting and the rest will be easier to understand.

Calling rendering function (templateRenderer)

The CSR Object has two public methods:

  • RegisterTemplateOverrides(templateOverride, ctxFilters): which takes a standard SharePoint template override and CTX Filters.
  • init(): Doesn’t take any arguments. Finally registers the single general template override to the SharePoint SPClientTemplates.TemplateManager.

Let’s have a look at this single general template override. The basic construct is like this (here only for footer template):

var templateOverride = {
	Templates: {
          	Footer: function(ctx) { return templateRenderer(ctx, footers, RenderFooterTemplate); }
     	}
};

You this that it’s always called the templateRenderer function, which takes:

  • ctx: the context
  • footers: an array of objects with footer functions (or a return string) and filters
  • RenderFooterTemplate: SharePoint default rendering function for footer

Now, the templateRenderer iterates over all footers and determines, if the current function in the footer has to be executed or not. This works this way:

function templateRenderer(ctx, templateDefinitions, DefaultTemplate) {
	var returnHTML = null;
		
	templateDefinitions.forEach(function(templateDefinition) {
		if(renderingOnCTX(templateDefinition.CTXFilters, ctx)) {
			if(typeof templateDefinition.Renderer === "string")
				returnHTML = templateDefinition.Renderer;
			else if(typeof templateDefinition.Renderer === "function")
				returnHTML = templateDefinition.Renderer(ctx);
		}
	});
		
	if(returnHTML === null)
		returnHTML = DefaultTemplate(ctx);

	return returnHTML;
}

Filtering on current context (renderingOnCTX)

The renderingOnCTX function determines if the rendering function (templateDefinition.Renderer) has to be executed in current context or not. It simply does this by comparing any parameter in CTXFilters with corresponding parameter in ctx. So you can compare ListTitle, URLs and also WebPart-Number. It is also possible, to execute custom function, by adding them to the CTXFilter object.
Here is the renderingOnCTX function:

function renderingOnCTX(ctxFilters, ctx) {
	var renderingOnCTX = true;
	var ctxFilterNames = Object.getOwnPropertyNames(ctxFilters);
	ctxFilterNames.forEach(function(filterName) {
		if(typeof ctxFilters[filterName] !== "function" && ctx.hasOwnProperty(filterName))
			renderingOnCTX = renderingOnCTX && ctx[filterName] === ctxFilters[filterName] 		
		else if(typeof ctxFilters[filterName] === "function")
		{
			renderingOnCTX = renderingOnCTX && ctxFilters[filterName](ctx);
		}
	});
	return renderingOnCTX;
}

And this is a filter definition with a ctx property comparison and a custom function:

{		
	ListTitle: "Documents",
	FilterFunction: function(ctx) {
		return (ctx.ListTitle.length > 1);
	}
}

You can define as many filter functions as you like. Also the name can be whatever you like.

Filling an array with objects of render-function and filters (RegisterTemplateOverrides)

The last part is now to get create the footers array of objects. This happens in the AtiaNuur.CSR.RegisterTemplateOverrides function (I’m showing just the footer part):

function RegisterTemplateOverrides(curTemplateOverride, ctxFilters) {
	if(curTemplateOverride.Templates !== undefined) {
		if(curTemplateOverride.Templates.Footer !== undefined) {
			footers.push({CTXFilters: ctxFilters, Renderer: curTemplateOverride.Templates.Footer});
		}
	}
}

As you see I create each footers object out of the ctxFilters and the Footer function.

And the other template overrides?

All other template override functions follow the same principle as the footer. The Templates.Fields object has been a little bit trickier, but it also works the same way.

How to use

Add the atianuur.csr.js to your page using masterpage, JS-Link or any other way you like. I used the masterpage, and also added a csr.js file which defines the concrete template override:

<SharePoint:ScriptLink language="javascript" name="~sitecollection/Style Library/atianuur.csr.js" OnDemand="false" runat="server" Localizable="false" LoadAfterUI="false" />
<SharePoint:ScriptLink language="javascript" name="~sitecollection/Style Library/csr.js" OnDemand="false" runat="server" Localizable="false" LoadAfterUI="true" />

I uploaded both files to the style library (any other folder works as well). Then I load the AtiaNuur.CSR framework before and the csr.js file after the UI.

And here is some code out of the csr.js file, which hands over a standard template override and a CTXFilter object:

var leftWebPart_Override = {	
	Templates: {
		Header: "",
		Footer: function(ctx) {
			return "Custom WP Footer 1";
		},
		Fields: {
			"LinkTitle" : {
				View: "This is not the original link text"
			}
		}
	},
	OnPreRender: function(ctx){ alert("I'm prerendering"); }
};
var leftWebPart_CTXFilters = {		
	ListTitle: "CSR-Test-List"
};
AtiaNuur.CSR.RegisterTemplateOverrides(leftWebPart_Override, leftWebPart_CTXFilters);

In this example, I also overwrite the LinkTitle-Field and the OnPreRender function.

Disclaimer

The code to get the default renderer comes from Jim Brown [2].

Sources

And here are the complete sources: atianuur.csr.

References

[1]: http://www.myfatblog.co.uk/index.php/2013/09/listview-web-part-issues-with-jslink-and-display-templates-a-solution/
[2]: http://sharepoint.stackexchange.com/questions/112506/sharepoint-2013-js-link-return-default-field-rendering

Nintex Workflow Activity Template for Visual Studio

Nintex offers some project templates for Visual Studio. One for an activity and one for an action adapter. You can download them from Nintex SDK: Installing and using Visual Studio templates.

I installed and used them to develop a new activity. After creating a new solution with adapter and activity using Namespaces you get a bunch of errors.
My Namespace is “AtiaNuur.NintexWorkflows”. Here are my steps to setup und correct the two projects:

  • Create new Project of type Nintex Adapter
    • Name “AtiaNuur.NintexWorkflows.HelloWorld”
    • Solution HelloWorld
  • Set SolutionId in package to new Guid (Nintex creates allways the same SolutionId)
  • Add Project of Type Nintex Activity using Name “AtiaNuur.NintexWorkflows.HelloWorldActivity”
    • Change default Namespace to “AtiaNurr.NintexWorkflows.HelloWorld
  • In File AtiaNuur.NintexWorkflows.HelloWorldActivity.cs
    • remove Namespace in class Name
    • Also remove Namespace in PropertyDependencies
    • Remove ‘Activity’ from namespace

The File AtiaNuur.NintexWorkflows.HelloWorldActivity.cs should look like this now:
Nintex-Template-Repair

  • In AtiaNuur.NintexWorkflows.HelloWorldDialog.aspx.cs
    • Remove namespace in classname
  • In AtiaNuur.NintexWorkflows.HelloWorldDialog.aspx
    • Shorten Filename
    • Set new filename in NWAFile
    • Set new filename in .aspx prefix tags
    • Remove duplivate namespace in tag Page
  • Add new EmptyElement “NWAFile”
    • Delete Element.xml
    • Move *.nwa file into EmptyElement
    • Set DeploymentType to ElementFile
  • In .nwa File
    • Set Name, Category and Description
    • <ActivityType>AtiaNuur.NintexWorkflows.HelloWorld.HelloWorldActivity</ActivityType> insert Activity classname
    • <ActivityAssembly>AtiaNuur.NintexWorkflows.HelloWorldActivity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=529ebf25b8a73f63</ActivityAssembly> get your own full filename using ILSpy
    • Remove duplicated Namespace in AdapterType
    • Correct icon filenames
    • Correct ConfigurationDialogUrl
  • In package > Advanced
    • Add Activity Assembly
  • In File “AtiaNuur.NintexWorkflows.HelloWorldAdapter.cs”
  • Create Feature according to https://cann0nf0dder.wordpress.com/2013/09/12/nintex-workflow-custom-actions/
    • Add Event Receiver
    • Change NWAFile Name and Path
    • Change Adapter Class Name

SharePoint Survey – Where People and Group field data is stored in database

In general survey data is saved in SQLs AllUserData Table in the Content Database. But the data for a “people and group” field is not. This data is stored in the AllUserDataJunctions table.
You can use the following SQL statement to get the People for a survey list with title “MySurvey”. You should also filter the result by FieldID, if you have more than one people and group field:

SELECT
tp_Title
,tp_RootFolder
,tp_Modified
,AllUserData.tp_Created
,[AllUserDataJunctions].[tp_SiteId]
,[AllUserDataJunctions].[tp_DeleteTransactionId]
,[AllUserDataJunctions].[tp_IsCurrentVersion]
,[AllUserDataJunctions].[tp_DocId]
,[tp_FieldId]
,[AllUserDataJunctions].[tp_Id]
,[tp_Ordinal]
,[tp_SourceListId]
FROM [SP230_Inhalt_Inet].[dbo].[AllUserDataJunctions]
Join [SP230_Inhalt_Inet].dbo.AllUserData on AllUserData.tp_DocId = AllUserDataJunctions.tp_DocId
Join [SP230_Inhalt_Inet].dbo.AllLists on AllLists.tp_ID = [AllUserDataJunctions].tp_SourceListId
Where tp_Title = 'MySurvey'

Impersonation in CRM Plug-In and Workflow – the easy way

Some time ago I wrote a post about impersonation in dynamics crm plug-ins.

Today I found a much easier solution: use the impersonated organizationService from localcontext.

IOrganizationService service = localContext.OrganizationServiceImpersonated;

Ok, first you have to create this property. Do this in Plugin.cs, which gets generated by Visual Studio. Create the property:

internal IOrganizationService OrganizationServiceImpersonated
{
    get;
    private set;
}

Add the following line to the constructor of LocalPluginContext:

// Use the factory to generate the impersonated Organization Service.
this.OrganizationServiceImpersonated = factory.CreateOrganizationService(null);

Instead of null you can also pass a systemusers GUID. Null will impersonate as systemadministrator.

And when you are just there, also set the ServiceProvider correctly, so that it’s not null. Then the whole constructor looks like this:

internal LocalPluginContext(IServiceProvider serviceProvider)
{
    if (serviceProvider == null)
    {
        throw new ArgumentNullException("serviceProvider");
    }

    // 1. extra line: Set LocalContext ServiceProvider
    this.ServiceProvider = serviceProvider;

    // Obtain the execution context service from the service provider.
    this.PluginExecutionContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

    // Obtain the tracing service from the service provider.
    this.TracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

    // Obtain the Organization Service factory service from the service provider
    IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

    // Use the factory to generate the Organization Service.
    this.OrganizationService = factory.CreateOrganizationService(this.PluginExecutionContext.UserId);

    // 2. extra line: Use the factory to generate the impersonated Organization Service.
    this.OrganizationServiceImpersonated = factory.CreateOrganizationService(null);
}

To get impersonated service in Workflows is a little easier:

var serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
var service = serviceFactory.CreateOrganizationService(null);      //Create impersonated service

Impersonation of Service Executions in Plug-Ins and Workflows

A service execution can be impersonated by instantiating a ServiceProxy an setting the CallerId.
The Plug-In (and maybe Workflow) has to be registered as full trust (IsolationMode=”None”) to use the DefaultNetworkCredentials, otherwise they will be null. Set the isolationmode in registerfile.
If you use Early Binding, don’t forget to enable proxy types by calling serviceProxy.EnableProxyTypes().

protected void ExecutePreAccountUpdate(LocalPluginContext localContext)
{
    if (localContext == null)
    {
        throw new ArgumentNullException("localContext");
    }

    IPluginExecutionContext context = localContext.PluginExecutionContext;

    var clientCredentials = new ClientCredentials();
    // Using custom credentials
    credentials.Windows.ClientCredential = new NetworkCredential(, , );

    // Using Default Credentials
    // credentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;

    var orgUrl = "http:///XRMServices/2011/Organization.svc";
    var organizationServiceUri = new Uri(orgUrl);

    // To use SSL
    if (!string.IsNullOrEmpty(orgUrl) && orgUrl.Contains("https")) {
	ServicePointManager.ServerCertificateValidationCallback = delegate(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; };
    }

    using (var serviceProxy = new OrganizationServiceProxy(organizationServiceUri, null, clientCredentials, null))
    {
        serviceProxy.EnableProxyTypes();
        serviceProxy.CallerId = new Guid("AAB4EBA7-BDD1-E211-A6C5-00155D0B2738");    //Impersonating serviceProxy

        var newTask = new Task();
        newTask.Description = "ServiceProxy created Task";
        newTask.Subject = "ServiceProxy created";

        var newTaskGuid = serviceProxy.Create(newTask);
    }
}

Blog post about the different userIds, when impersonating Plug-Ins, Dialogs and Workflows: https://community.dynamics.com/crm/b/develop1/archive/2013/08/09/user-impersonation-in-plugins-workflow-and-dialogs.aspx

Debugging JavaScript that executes on Ribbon Button clicks

When you click a button in the ribbon, dynamics uses ajax to load the file where the js function is in (only once). Therefore you cannot set a breakpoint on the function, before the button click. Here I will present some solutions to debug such functions anyways:

  1. Set incorrect library in Ribbon definition
  2. Use “debugger;” statement
  3. Throw an error
  4. Use sourceUrl property

Set incorrect library in Ribbon definition

Interestingly dynamics loads the file where the function for the ribbon is in at page load. So the file is there, but won’t be used. By putting an incorrect library in the ribbon definition the ajax call will result in an 401 error. Luckily the function from the already loaded file will be used.
In this solution you can set a breakpoint before clicking on the button. Unfortunately this will also result in an error screen by dynamics.
Works in FF, Chrome. Not tested in IE.

Use “debugger;” statement

When you put

debugger;

into your sourcecode the debugger will pause on this line.
Works in Chrome. Not tested in IE.

Throw an error

You can also throw an catch an error in your code:

        try
        {
            throw new Error('breakpoint');   //debugger will break here
        }
        catch (e)
        {
        };

Then set your debugger to pause on all errors.
Works in Chrome, but not in FF. Not tested in IE.

Use sourceUrl property

By putting a

//@ sourceURL=filename.js

property into your javascript file, it will be listet as file in Chrome debugger, after it has been loaded. As it will be listet after you have clicked the button, you can only set a breakpoint after execution of the js function.
Works in Chrome, but not in FF. Not tested in IE.

Getting E-Mail and Attachments in Plug-In using LINQ

When updating an e-mail entity, only updated metadata is available in InputParameters.
Here is some code to get the whole entity and attachments using LINQ:

protected void ExecutePostEMailUpdate(LocalPluginContext localContext)
{
    if (localContext == null)
	{
		throw new ArgumentNullException("localContext");
	}

	IPluginExecutionContext context = localContext.PluginExecutionContext;
	IOrganizationService service = localContext.OrganizationService;
	ITracingService tracingService = localContext.TracingService;

	OrganizationServiceContext serviceContext = new OrganizationServiceContext(service);

	if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
	{
		Entity targetEntity = (Entity)context.InputParameters["Target"];
		if (targetEntity.LogicalName != "email")
			return;

		try
		{
			Email email = targetEntity.ToEntity();

			email = (from mail in serviceContext.CreateQuery()
				where (mail.Id == email.Id)
				select mail).FirstOrDefault();

			var attachmentQuery = (from a in serviceContext.CreateQuery()
				where (a.ObjectId == email.ToEntityReference())
				select a);

			foreach (var attachment in attachmentQuery)
			{
				var fileName = attachment.FileName;
			}
		}
		catch (FaultException e)
		{
			throw new InvalidPluginExecutionException(e.ToString());
		}
	}
}