André Krijnen

blog

SharePoint: Javascript from asynchronous to synchronous with Deferred and Promise

by on Jun.14, 2016, under blog, javascript, SharePoint

The last few years it has become more and more common that we use JavaScript for our SharePoint Projects. Whether it’s Angular, Knockout or another preferred framework it’s all very usable. But sometimes JavaScript can be a pain to use. For example, handling data in a synchronous way in Custom Actions. Recently I had to write code for sending document data to Dynamics AX. Since we use Business Connectivity Services to send the data to Dynamics AX and we are still using farm solutions I had to write a Custom Action.
When selecting multiple documents and handling the data in two for loops it’s a pain, because if you are using variables in your class that must be used between functions it will be overwritten while your process is still running. Let’s give a code example:

var ctx = '';
var web;
var list;
var listOrg;
var item;
var listItem;
var props;
var xmlDoc;

function startWork() {
ctx = new SP.ClientContext.get_current();
    	var items = SP.ListOperation.Selection.getSelectedItems(ctx);
	if (items.length >= 1) {
        	for (idx in items) {
			var listId = SP.ListOperation.Selection.getSelectedList();
    			listOrg = ctx.get_web().get_lists().getById(listId);
			
    			web = ctx.get_web();

    			list = web.get_lists().getByTitle('AX Documents');

    			var camlQuery = new SP.CamlQuery();
    			camlQuery.set_viewXml('<View><RowLimit>100</RowLimit></View>');
    			listItem = list.getItems(camlQuery);
    			item = listOrg.getItemById(id);
    			props = web.get_allProperties();

    			ctx.load(web);
    			ctx.load(listOrg);
    			ctx.load(props);
    			ctx.load(listItem);
    			ctx.load(item, 'EncodedAbsUrl', 'AX_Nummer', 'AX_Nummer2', 'AX_Nummer3', 'AX_Nummer4', 'AX_Nummer', 'LookupSoort', 'LookupSoort2', 'LookupSoort3', 'LookupSoort4', 'LookupSoort5');

    			ctx.executeQueryAsync(Function.createDelegate(this, onQuerySucceeded, Function.createDelegate(this, onQueryFailed));

		}
	}
}

Function onQuerySucceeded() {

	var myProps = props;
var myPropValues = myProps.get_fieldValues();
	var myValue = myPropValues['Sil.AA.DMS.Common.Configurations'];
	var xmlDoc = $.parseXML(myValue);
	
if (xmlDoc) {
		var areaId = $(xmlDoc).find('Configuration').find('AreaId').text();

		for (var j = 0; j <= 6; j++) {
		       if (j === 0) {

				var lookupField = item.get_item('LookupSoort');
			       var lookupValue = lookupField.get_lookupValue();
		              var updateItem = listItem.itemAt(0);
				updateItem.set_item('DocumentIdentificationType', lookupValue);
				updateItem.set_item('DocumentIdentification1', item.get_item('AX_Nummer'));
				updateItem.set_item('AreaId', areaId);
				updateItem.set_item('DocumentUrl', item.get_item('EncodedAbsUrl'));
				updateItem.update();

				ctx.executeQueryAsync(onUploadSucceeded, onQueryFailed);

}
			else {
				var lookupFieldName = 'LookupSoort' + (j + 1);
				var lookupField = item.get_item('LookupSoort' + (j + 1));
				
if (lookupField !== null) {
					var updateItem = listItem.itemAt(0);
				       var lookupValue = lookupField.get_lookupValue();
				       updateItem.set_item('DocumentIdentificationType', lookupValue);
				       updateItem.set_item('DocumentIdentification1', item.get_item('AX_Nummer' + (j + 1)));
				       updateItem.set_item('AreaId', areaId);
				       updateItem.set_item('DocumentUrl', item.get_item('EncodedAbsUrl'));
				       updateItem.update();

				       ctx.executeQueryAsync(onUploadSucceeded, onQueryFailed);
			        }
			
	              }
	      }
	}
}

Function onUploadSucceeded() {
	Alert(‘File has been processed’);
}

Function onQueryFailed(sender, args) {
	Alert(‘File not processed: ‘ + args);
}

Let’s say I run the above code with two items, it will see both items, but the processing will be asynchronous. Since executeQueryAsync will be run the first time, but the first for loop will be processed at the same time. I have two items:

FileLeafReg Title AX_Number LookupSoort AX_Number2 LookupSoort2 AX_Number3 LookupSoort3 AX_Number4 LookupSoort4 AX_Number5 LookupSoort5
http://sp2013/Documents/picture1.png Picture 1 VN12345 Sales VN12346 Sales VN12347 Sales Null Null Null Null
http://sp2013/Documents/picture2.png Picture 2 VN67890 Sales VN67891 Sales Null Null Null Null Null Null
                       

It will result in processing the second items 5 times, with the properties of the second file. Because the for loop will put the values of the second file over the variables of the first file, since the first for loop is faster in processing. And the onQuerySucceeded will only be processed when the first for loop is done. So it will break every piece of code in the chain.
I talked with several people, experts in JavaScript and even on StackOverflow, and I never got a decent answer how to fix it properly. It always resulted that the chain was broken. I tried promise and deferred in different manners, still not as it should be. I was getting headaches because of it, since none of the answers sufficed in a proper way.
Normally I am absolutely not a morning person, and the breakthroughs are normally at night when everybody is asleep. But two weeks ago, when it was still very early in the morning I woke up and started to code, just before driving to the office, I found my answer in a way that I could live with it.
I shuffled and refactored all my notepad++ tabs in one singular tab and there it was. A solution that was clear to read. Let’s go through the promise and deferred part. The steps are marked in Red from Step 1 ‘till 6. So that the code is easy to follow.

var ctx = '';

function startWork() {
ctx = new SP.ClientContext.get_current();
var items = SP.ListOperation.Selection.getSelectedItems(ctx);
   	if (items.length >= 1) {
       	 for (idx in items) {
			fixLinkInAxapta(items[idx].id).then(	// Step 1: Instead of running the first clientContext executeQueryAsync. We first go into the function and prepare the Promise
				function (listItem, item, props) { // Step 4: is running this in synchronous mode.
					var myProps = props;
				    	var myPropValues = myProps.get_fieldValues();
				    	var myValue = myPropValues['Sil.AA.DMS.Common.Configurations'];
				    	var xmlDoc = $.parseXML(myValue);
				    	if (xmlDoc) {
				        	var areaId = $(xmlDoc).find('Configuration').find('AreaId').text();
						for (var j = 0; j <= 6; j++) {
				            if (j === 0) {
				                var lookupField = item.get_item('LookupSoort');
				                var lookupValue = lookupField.get_lookupValue();
				                var updateItem = listItem.itemAt(0);
				                updateItem.set_item('DocumentIdentificationType', lookupValue);
				                updateItem.set_item('DocumentIdentification1', item.get_item('AX_Nummer'));
				                updateItem.set_item('AreaId', areaId);
				                updateItem.set_item('DocumentUrl', item.get_item('EncodedAbsUrl'));
				                updateItem.update();

				                ctx.executeQueryAsync(onUploadSucceeded, onQueryFailed);

				            }
				            else {
				                var lookupFieldName = 'LookupSoort' + (j + 1);
				                var lookupField = item.get_item('LookupSoort' + (j + 1));
				                if (lookupField !== null) {
				                    var updateItem = listItem.itemAt(0);
				                    var lookupValue = lookupField.get_lookupValue();
				                    updateItem.set_item('DocumentIdentificationType', lookupValue);
				                    updateItem.set_item('DocumentIdentification1', item.get_item('AX_Nummer' + (j + 1)));
				                    updateItem.set_item('AreaId', areaId);
				                    updateItem.set_item('DocumentUrl', item.get_item('EncodedAbsUrl'));
				                    updateItem.update();

				                    ctx.executeQueryAsync(onUploadSucceeded, onQueryFailed);
				                }
				                else {
				                    break;
				                }
				            }
				            if (j === 6) dfd.resolve(); // Step 5: Resolve the Deferred
				        }
				    }

				},
				function (sender, args) {

				});
        }
    }

    alert('All items have been processed to Dynamics AX');
}
function fixLinkInAxapta(id) {
    var dfd = $.Deferred(); // Step 2: Setup the Deferred method
    var listId = SP.ListOperation.Selection.getSelectedList();
    var listOrg = ctx.get_web().get_lists().getById(listId);
    var item;
    var props;
    var list;
    var listItem;
    var web;

    web = ctx.get_web();

    list = web.get_lists().getByTitle('AX Documents');

    var camlQuery = new SP.CamlQuery();
    camlQuery.set_viewXml('<View><RowLimit>1</RowLimit></View>');
    listItem = list.getItems(camlQuery);
    item = listOrg.getItemById(id);
    props = web.get_allProperties();

    ctx.load(web);
    ctx.load(listOrg);
    ctx.load(props);
    ctx.load(listItem);
    ctx.load(item, 'EncodedAbsUrl', 'AX_Nummer', 'AX_Nummer2', 'AX_Nummer3', 'AX_Nummer4', 'AX_Nummer', 'LookupSoort', 'LookupSoort2', 'LookupSoort3', 'LookupSoort4', 'LookupSoort5');
// Step 3: Resolve the listItem, item and the properties and call the function(listItem, item, props) in the previous method and wait for it to finish. If an error occurs go to reject
    ctx.executeQueryAsync(Function.createDelegate(this, function () { dfd.resolve(listItem, item, props); }), Function.createDelegate(this, function (sender, args) { dfd.reject(sender, args); }));

    return dfd.promise(); // Step 6: When the ClientContext.executeQueryAsync is resolved make it promise and return to the For Loop
}

Of course if you have any questions, don’t hesitate to comment on this blogpost and I will get back to you as soon as possible. More blog posts will follow soon.

Leave a Comment :, , , , , , more...

Time for renewal and change.

by on May.05, 2016, under blog, Personal

It has been a long time since I have been writing on my blog. A lot of factors came into play since the last 4 years, and now it is the time renew my writings. I have multiple sites that I run on, for example https://www.andrekrijnen.com, but also other sites. This site needs also some fresh content, but since this is on Office365 and SharePoint Online it is harder to write good blogs that can be found by the common search engines.

So after all these years I come back on my old blog http://www.mysticslayer.com. Since I started in the early days with online content I never wrote on my own name, always by my custom name Mystic Slayer. This name I inherited from the days like Unreal Tournament, Quake 3 Arena, etc. I am still using this name on Xbox Live. Playing all kinds of games next to work. My work is still my passion, and I want to enable it more to show what I am doing during my days at work. What I came cross.

Thanks to my supporters and colleagues it is time to write! And I can say the least it will be a lot that I have to write!

Leave a Comment more...

Sharepoint 2010 Search: Internal Server Error.

by on Nov.07, 2011, under blog

Sometimes users receive the following message when attempting a search:

Before that, it will take some seconds before you can see your search page. After watching the ULS logs, I found the following messages:

SearchServiceApplication::Execute–Exception: System.Runtime.InteropServices.COMException (0x800703FA): Illegal operation attempted on a registry key that has been marked for deletion. (Exception from HRESULT: 0x800703FA)

SearchServiceApplicationProxy::Execute–Error occured: System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: Illegal operation attempted on a registry key that has been marked for deletion. (Exception from HRESULT: 0x800703FA) (Fault Detail is equal to An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is: System.Runtime.InteropServices.COMException: Illegal operation attempted on a registry key that has been marked for deletion. (Exception from HRESULT: 0x800703FA)

I knew this error before, when I developed a WCF Service, so I searched the registry in order to find the following message:

The reason is when the WCF service stops working and it runs under a particular service account SharePoint restarts the WCF service, and it works again.
The cause of it all is when the service account is logged on when the Search Service Application is launched in this case. It happens all when the service account logs off and the COM+ application can no longer read registry keys in the profile.
If you see the event viewer message, you’ll see that the User Profile Service is a new functionality added in Win Vista and Windows Server 2008. When a user or Service account is logged of, the profile Service forces the unload of the User Profile on the server. But when this happens the User Profile Service can break an application if registry keys are not closed.
The resolution is to make a workaround this issue to disable this feature in the Software Policy on the local system. Btw. For each SharePoint Server you should do this.
Go to the Group Policy Editor and then go to Computer Configuration -> Administrative Templates -> System -> User Profiles.
Edit the setting ‘Do not forcefully unload the registry at user logoff’ to enabled.
And voilà. Workaround created.

Leave a Comment more...

Powershell: Update all Document Libraries with MajorVersionLimit and MajorWithMinorVersionsLimit

by on Jun.15, 2011, under blog, Powershell, SharePoint 2010

I had to write a Powershell script to run thru all sites in a Web Application to enable Versioning. As well Major as Minor versions. With of course a limit on Major and Minor versions.

Next script should do the trick:

Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue
$siteURL = $args[0]
$site = Get-SPSite($siteURL)
foreach($web in $site.AllWebs) {
 Write-Host "Inspecting " $web.Title
 foreach ($list in $web.Lists) {
  if($list.BaseType -eq "DocumentLibrary") {
  Write-Host "Versioning enabled: " $list.EnableVersioning
  $host.UI.WriteLine()
  Write-Host "MinorVersioning Enabled: "  $list.EnableMinorVersions
  $host.UI.WriteLine()
  Write-Host "EnableModeration: " $list.EnableModeration
  $host.UI.WriteLine()
  Write-Host "Major Versions: " $list.MajorVersionLimit
  $host.UI.WriteLine()
  Write-Host "Minor Versions: " $list.MajorWithMinorVersionsLimit
  $host.UI.WriteLine()
  $list.EnableVersioning = $true
  $list.EnableMinorVersions = $true
  $list.MajorVersionLimit = 2
  $list.MajorWithMinorVersionsLimit = 5
  $list.Update()
  Write-Host $list.Title " is updated with MajorVersionLimit 2 and MajorwithMinorVersionsLimit = 5"
  }
 }
}

 

Leave a Comment :, , , , , , more...

MSS 2010 / .NET 3.5 / Farm

by on Dec.12, 2009, under blog

Well I installed a complete new infrastructure to test the first beta of Microsoft SharePoint Server 2010. So configured a SQL Server 2008 R2 CTP with Windows Server 2008 R2 EE on Hyper-V.

Everything done. And let’s play with MSS 2010. Well I was happy to say that I had the option of installing a farm. Well when the installation was running I could say I was disappointed by the fact that SQL Server 2008 was installed on the APP server. So I concluded that the first installation I had no choice that SQL Server was running on the same machine.

The second conclusion is the unforsaken Ribbon of Office 2007. You have alot more options, but if you can say that everything will be worked out better? Well I can say that MOSS 2007 was allready full with bugs with the initial RTM release, and how many bugs can we have with MSS 2010? Well hopefully not that much like MOSS 2007 RTM, because we can wait again for along time to see the first SP. The naming of WSS to Foundation isn’t not too bad, but if you can say if it is a Foundation?

The next thing is the fact that not ASP.NET 4.0 Beta 2 is used by MSS 2010. So we’re going not upwards, but standing still with the same framework. Also noticed that you have to
use some regfixes to fix some bugs for MSS 2010. Too bad!

First conclusion:

MSS 2010 is alot faster.
MSS 2010 has .NET Framework 2.0/3.5 and not .NET 4.0
Beta and farm installation is too bad.
MSS 2010 has alot of new features and options (also Lotes Notes included).

And yet more to come.

Leave a Comment :, , , more...

Server went down for some reason

by on Aug.03, 2009, under blog, maintenance

A strange thing happend last day is that for some reason my Web Server went down in action… I couldn’t explain why it happend, but I managed to gave it a rebirth of the server.

After 30 days without reboot, wow :-S It went down, and yet I cannot figure out why. Nothing changed, and I saw my logfiles and even there it didn’t show anything. So, what happened then? I don’t know.

Leave a Comment :, , , more...

how to exclude paths in your #sharepoint farm

by on Jul.03, 2009, under blog

Allright let’s get started. Because a normal SharePoint farm will not allow you to request pages with logged in on your website. This is because MOSS will intercept every page you will ask even when you are using an Web Application. In WSS 2.0 and SPS 2003 you had to the possibility to exclude certain paths from being captured by SPS or WSS.

Well in SharePoint 2007 you can’t make an exclusion in Central Administration, but you can do it by manual. How to do this is easy or not, but it’s possible. You can’t access pages directly in a Web Application, but you can create a Virtual Directory. Still when you access this virtual directory, SharePoint won’t allow you to access this virtual directory.

Now we have to do it another way. Let’s modify the web.config of your Web Application.

this is probably how your section will look like:

  1. <httpHandlers>
  2.       <remove verb="GET,HEAD,POST" path="*" />
  3.       <add verb="GET,HEAD,POST" path="*" type="Microsoft.SharePoint.ApplicationRuntime.SPHttpHandler, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
  4.       <add verb="OPTIONS,PROPFIND,PUT,LOCK,UNLOCK,MOVE,COPY,GETLIB,PROPPATCH,MKCOL,DELETE,(GETSOURCE),(HEADSOURCE),(POSTSOURCE)" path="*" type="Microsoft.SharePoint.ApplicationRuntime.SPHttpHandler, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
  5.       <add verb="*" path="Reserved.ReportViewerWebControl.axd" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  6.     </httpHandlers>

modify it to this:

  1. <httpHandlers>
  2. <!–      <remove verb="GET,HEAD,POST" path="*" />
  3. –>
  4.       <add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory, System.Web, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  5.       <add path="trace.axd" verb="*" type="System.Web.Handlers.TraceHandler" validate="True" />
  6.       <add path="WebResource.axd" verb="GET" type="System.Web.Handlers.AssemblyResourceLoader" validate="True" />  
  7.       <add verb="GET,HEAD,POST" path="*" type="Microsoft.SharePoint.ApplicationRuntime.SPHttpHandler, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
  8.       <add verb="OPTIONS,PROPFIND,PUT,LOCK,UNLOCK,MOVE,COPY,GETLIB,PROPPATCH,MKCOL,DELETE,(GETSOURCE),(HEADSOURCE),(POSTSOURCE)" path="*" type="Microsoft.SharePoint.ApplicationRuntime.SPHttpHandler, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
  9.       <add verb="*" path="Reserved.ReportViewerWebControl.axd" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  10.     </httpHandlers>

Change the trust level:

  1. <trust level="Full" originUrl="" />

Ensure that your virtual directory has it’s own web.config. Without an own web.config it will inherit from your web.config in your website (of course your web.config of your web application).

Leave a Comment :, , , , , , , , , , , , more...

MOSS 2007 with something else

by on Jun.25, 2009, under blog

The situation is following.

I have a page located in the /_Layouts/ dir. This page is the CustomLogin page and validates two ways. The first way is by a post to a security validation with a token.

  1. <form name="LoginForm" action="<% =requestURL %>" method="post" enctype="application/x-www-form-urlencoded" id="LoginForm">

When we hit the submit button it should post data to this validation server and returns data formatted in the header.

The problem is that the posted data is not submitted, because for someone reason MOSS blocks it.

This validation server has it’s own SSL certificate and the MOSS site has it’s own SSL certificate.

Leave a Comment :, , , , , , more...

WordPress posts published directly at Twitter

by on Jun.22, 2009, under blog

Yes, this is fun to know for me. I saw that my blogposts are directly posted to twitter as soon as I publish them on WordPress.

1 Comment :, , more...

Bug wordpress 2.7.1 rss feeds of sitemap

by on May.10, 2009, under blog

Well I found out that pages that have the Status ‘Draft’ and are not published still will be read by google and other sites. Probably something is wrong with the sitemap or rss feeds from WordPress.

I shall look at this one, because I don’t want to make posts that aren’t ready allready are published world wide. Yet I shall find it out

Leave a Comment :, , , , , more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Blogroll

A few highly recommended websites...