Author Archive
SharePoint: Javascript from asynchronous to synchronous with Deferred and Promise
by mysticslayer 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.
Time for renewal and change.
by mysticslayer 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!
Powershell: Return SPWebTemplateNameByWebTemplateId
by mysticslayer on Nov.10, 2011, under Powershell, SharePoint 2010, SharePoint Foundation
Sometimes you it’s easy to script some stuff, and you redo some stuff. For example: You want to manually script your export-import action in SharePoint automatically.
Something you need by importing the site is the webtemplate before you can start the import. So, what you can do before deleting the site is to extract the WebTemplateId. The WebTemplate in $web.Url is not sufficient, because it doesn’t translate the correct Configuration.
So, what I’ve did is to use the WebTemplateId and get the according WebTemplate Name. And use it to create a SPSite.
function Get-SPWebTemplateNameBySPWebTemplateId($WebTemplateId) { $templates = Get-SPWebTemplate | Sort-Object "Name" $templateValues | ForEach-Object { if($_.ID -eq $WebTemplateId) $templateName = $_.Name return; } return $templateName } $templateName = Get-SPWebTemplateNameBySPWebTemplateId -WebTemplateId 53 Write-Host = $templateName
Above will return BLANKINTERNET#2
Use Content by Query Web Part on a Fixed List created dynamic in an onet.xml
by mysticslayer on Nov.07, 2011, under features, Programming, SharePoint 2010
There is nothing as worse as a Content By Query Web Part that you can’t use when you want to fix it against a List which will be made automatically for example.
I would like to create a Team Site based on a Custom Web template with an extra sub-site with blogging functionality. This blogging functionality is used for exposing news of the Team or Department.
But when using the functionality like
<Property name=”WebUrl” type=”string”>~site/News</Property>
and using the property
<Property name=”ListName” type=”string”>Posts</Property>
and you removed the value of the property
<Property name=”ListGuid” type=”string”></Property>
it won’t render the CQWP web part by default. The reason is that the ListGuid is needed to render the Web Part.
But also an issue is that, when your list is created it has a changed ListGuid. The ListGuid is never the same when you create a new List.
So basically I created the follow code which will fix that issue.
using System; using System.ComponentModel; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using Microsoft.SharePoint; using Microsoft.SharePoint.WebControls; using Microsoft.SharePoint.Publishing; using Microsoft.SharePoint.Publishing.WebControls; namespace AK.Custom.CQWP { [ToolboxItemAttribute(false)] public class AKCQWP : ContentByQueryWebPart { protected override void OnLoad(EventArgs e) { if(!string.IsNullOrEmpty(this.WebUrl) && !string.IsNullOrEmpty(this.ListName) && string.IsNullOrEmpty(this.ListGuid)) { SPWeb webRoot = SPContext.Current.Site.OpenWeb(); if (this.WebUrl.StartsWith("~site")) { this.WebUrl = this.WebUrl.Replace("~site", ""); using(SPWeb web = SPContext.Current.Site.OpenWeb(webRoot.ServerRelativeUrl + this.WebUrl, true)) { SPList list = web.Lists[this.ListName]; this.ListGuid = list.ID.ToString(); //this.ListUrl = webRoot.ServerRelativeUrl + this.WebUrl; this.WebUrl = webRoot.ServerRelativeUrl + this.WebUrl; }; } else if(this.WebUrl.StartsWith("/News")) return; else { using(SPWeb web = SPContext.Current.Site.OpenWeb(this.WebUrl, true)) { SPList list = web.Lists[this.ListName]; this.ListGuid = list.ID.ToString(); this.WebUrl = this.WebUrl; }; } } base.OnLoad(e); } protected override void OnInit(EventArgs e) { base.OnInit(e); } protected override void CreateChildControls() { base.CreateChildControls(); } } }
When you have created this part of your solution ensure that you Export the standard CQWP and edit it like you want to. First of all, what you need is to ensure that it takes your assembly instead of the default CQWP assembly.
<metaData> <type name="AK.Custom.CQWP.AKCQWP, $SharePoint.Project.AssemblyFullName$" /> <importErrorMessage>$Resources:core,ImportErrorMessage;</importErrorMessage> </metaData>
Change the properties as following:
<Property name=”WebUrl” type=”string”>~site/News</Property> <Property name=”ListName” type=”string”>Posts</Property> <Property name=”ListGuid” type=”string”></Property>
sharepoint 2010: localized resource for token ‘direction’ could not be found for file with path
by mysticslayer on Nov.07, 2011, under features, Programming, SharePoint 2010, SharePoint Foundation
A quick blog about the ‘Localized resource for token ‘direction’ could not be found with path ‘feature path’ is an issue when you have generated a List Definition. When you deploy a list definition in a feature without a Default Core Resource file you will run against this message. It seems to be introduced by the 2010 December update, and it also seems to be solved by the SP1 of 2011 June Refresh update, but I can’t say for sure.
I have installed some language packs on my dev machine, and it does work without any problems, but the environment I want to deploy to seems to have this issue for one reason or another.
Feature Deployment SharePoint 2010 (List Instance and List Definition)
by mysticslayer on Nov.07, 2011, under features, General, maintenance, Powershell, SharePoint 2010, SharePoint Foundation
So I started out today debugging some weird issues regarding feature deployment. And well sometimes I don’t know why I get errors, but what’s new. So first of all, let’s start where to start.
cannot complete this action.please try again
Starting point:
A colleague of mine had written a feature for a customer of us, which worked perfectly. The customer had never issues like it should. After almost a year we (me, and the customer) decided to change the full Site Templates. Like a developer should do is making and changing the solution work. Well then comes the burden. After days of work, testing everything out the solution worked by me. (Owwh yeah, the known starting point: well on my computer it works).
So after deployment at the production server it didn’t work. I didn’t knew why, because everything should work like my notebook. The reason is simple, I had the same configuration, databases, etc. Well that didn’t work that out to.
So I thought well, let’s start fresh with new content databases from production. Ahh yes, there we go. Well, at my notebook it didn’t work either. Whoehoe, nice, we’ve got a good starting point for debugging.
Visual Studio 2010:
So I worked with Visual Studio 2010 and the deployed the feature again to debug. Well. Strangely, the solution worked after that. So Microsoft did some tricks while deploying. So I searched and called some people, yes on sunday. And telling me that Visual Studio 2010 is deploying asychronously. Yeah I knew that, but after a minute and some conversation telling that Powershell is deploying synchronously. Well that the trick I thought. So I decided to help my self out with some times, running the admin jobs, etc.
Even that didn’t worked out.
Go debugging old fashioned style: disabling Web and Site scoped features in the onet.xml.
After the first Web Feature (Deploying some List Instances and List Definitions) disabled the solution worked by powershell. I didn’t have a clue, but ohh well, I decided to change the ordering of the feature. Instead of List Definitions first, I decided to set the List Instances first. And you know what, It worked.
After enabling the Web Feature again, I could run the New Site creation perfectly.
Sharepoint 2010 Search: Internal Server Error.
by mysticslayer 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.
SharePoint 2010: (pilot , poc) Sites, webs, contentdb’s, what you should(n’t) do
by mysticslayer on Oct.28, 2011, under SharePoint 2010, SharePoint Foundation
Before I begin my blog I will thank everyone who thought this allready over and made it available thru all types of conferences worldwide.
First of all, this is my own comment about how to do work around with Sites, Webs, content database, etc. This is my own opinion based on what I’ve learned the last years and my based on my own experience combined with the experience from others.
So, let’s start with an example which causes me to write this blog:
A mid-size company started of with a simple pilot of SharePoint 2010. This pilot was to discover how SharePoint works, and does it fit within the organization. We agreed before we started to start all over again when the company decided to go further with SharePoint. Well after an half year the company decided to go further with SharePoint, but yet the pilot environment was allready promoted to production before we could started all over again.
Allright I thought, when everything is ok, and we don’t have any issues regarding the environment we can do that, because the physical as the logical architecture was future proof designed. Multiple Content Databases, multiple site collections, etc. The only thing we had to was give some servers more memory. That’s all.
I had installed, configured and designed the infrastructure, so I knew that kind of issues can happen, when a pilot environment will be promoted as a production environment. I wasn’t committed when others had the intranet designed with sites,templates, etc for the Pilot. But well, I’ve made some critical decisions in the infrastructure, but maybe I forget to mention it correctly. My bad.
In februari I was asked to go further with the environment, and we attached Reporting Services, Datawarehousing, etc. No issues there.
But now, when the point arrives we grow larger and larger, and when I checked the databases it was all good, because all databases where used. But then I saw one big problem. Yes, they didn’t use the Site Collections I’ve made in the start, but created just sub SPWebs. Ouch, yes here we go.
So, now we have to migrate 25 GB of subwebs to one of the Site Collections I created at the start. Well. it takes days to do it well, everything you have done in the early stages are allready critical in the organization, so you can’t make mistakes. Everything has to work as before when your migrated. Well convert to SPSite is one hell of task to do.
I think I’ve tested the migration over and over again. Used the information given by Gary Lapointe, and went further with it. Came up with more then 257 errors. Well okay, that isn’t much, but it doesn’t tell the errors you’ll get functional. Yes functional!!
Every site, list, workflow, CQWP has to be tested. And I mean, really tested if it works as before. I can tell you, I have to replace every CQWP, some lists are getting corrupted, content types are missing, list pages are gone, pages are not behaving as they should.
So before you decide to start of with a pilot think as a production site, because it will help you a lot and other a lot. SharePoint 2010 is scalable, ensure that your sites are scalable as well. Think scalable act scalable.
Do’s
Don’ts
Restore Site SharePoint 2010 issues with site collection owners
by mysticslayer on Sep.27, 2011, under SharePoint 2010, SharePoint Foundation
We’ve had some multiple issues regarding the Site Collection owners, and we had to fix these in a fashioned time. After restoring we had the issue that we couldn’t change the primary and secondary site collection Administrators. So it was difficult where the bug was for us. But it wasn’t a bug.
After a new fresh install of the SP farm we still had these problems… so we had to search it out. After a long search it seemed that the UserAccountDirectoryPath was the issue. When the User Account Directory Path is filled in the table [dbo].[AllSites] of your content database you can do what you want, but you can’t change the owners.
So we had to remove the UserAccountDirectoryPath: Set-SPSite -Identity “http://site” -UserAccountDirectoryPath “”
And voìla it was fixed. With two Quotes it removes the UserAccountDirectoryPath. You have to this allways if you’re restoring your Site Collection to another farm.
Restoring SharePoint databases with other schema (SharePoint 2010)
by mysticslayer on Aug.17, 2011, under SharePoint 2010, SharePoint Foundation
I just started out today to restore some Content Databases to test out my new created features on my development environment, to check if everything works as planned. But yet, I’ve figured out that I had some issues creating and deleting sites.
So I checked the ULS logs and nothing to see, I checked event viewer and also there no errors containing the message which I had: “Supported method not found” or something in that other.
Well I remember when I installed Service Pack 1 of SharePoint Server 2010 and tried to remove a site I had the same message. This was related to some bug or something. So I installed June CU Refresh and that worked perfectly. Well in this case I had both installed, so that shouldn’t be the problem.
So I checked the status of my Content Databases and what I noticed was that the schema version of the database wasn’t the same version as June CU Refresh. And the message was that I could update. Also the strange notice is that my Content Databases aren’t updated when you restore a database and attach it to SharePoint.
So what I did was run with powershell the following script:
$contentdb = Get-SPContentDatabase | Where-Object {$_.Name -match "WSS_ContentDB" }
Upgrade-SPContentDatabase -Identity $contentdb
Voila, content database is getting updated.