Friday, October 23, 2009
Could not find GUID for server...
All of a sudden our development server stopped working. We got the following error in CRM:
Microsoft CRM Error Report:
Error Details:
Could not find GUID for server: CRMSERVER$
Full Stack:
[ActiveDirectoryObjectNotFoundException: The local computer is not joined to a domain or the domain cannot be contacted.]
at System.DirectoryServices.ActiveDirectory.Domain.GetComputerDomain()
at Microsoft.Crm.BusinessEntities.SecurityUtils.GetGuidFromServerName(String serverName)...
It seems that it was caused by the fact the the domain servers DNS was down. I have read of other causes for the same error, but make sure that the DNS service of the domain server is up and running.
Gustaf Westerlund
Microsoft Dynamics CRM Chief Architect
Logica
www.logica.com
Tuesday, October 20, 2009
SharePoint 2010
You can read a lot about it on the SharePoint Team Blog here: http://blogs.msdn.com/sharepoint/archive/2009/10/19/sharepoint-2010.aspx
From a CRM perspective, this is very interesting. The new version of CRM is rumored to have a much tighter integration with SharePoint, so from that perspective it is almost essential to learn a bit more about SharePoint.
There are some areas of SharePoint that are of special interest from a CRM perspective:
- Enhance BI functionality including performance Point server now included in the new SharePoint
- Business Data Catalog enhanced - an excellent tool to work with in relations to CRM
- Enhanced Document Management - one of the weaker parts of Dynamics CRM 4 is the built in document management so integration with SharePoint in regards to document management is often used.
- Enhanced API and development capabilities. When integration with SharePoint development is often required to create the necessary functionality. The SharePoint API is currently ok but compared to the excellent API of Dynamics CRM, it can do with an upgrade. Hopefully to the mark set by Dynamics CRM.
However, there is still one major issue from a CRM - SharePoint integration perspective that Microsoft REALLY need to address and that is the licensing issues of SharePoint users working in a very slight way with CRM data by, for instance, looking at data from a data warehouse in SharePoint with drill-down functionality. Today, all users with access to this need to have a full read only CRM license. Similarly, if there is a webpart used in SharePoint to create leads in CRM, but with no other need for access, a full CRM CAL is required for all users of the Intranet since they are employees and hence cannot use the external connector license. This is preposterous! Please Microsoft, we really like CRM, and we want to pay our dues, just make it fair!
Gustaf Westerlund
Microsoft Dynamics CRM Chief Architect
www.logica.com
Sunday, October 18, 2009
Strange problem with Country and Country Code
I was confused. The country code was supposed to be according to the so and so ISO standard, where Sweden is SE. Hence 1 was very incorrect. I started looking if some tired programmer might have added some javascript or similar but found nothing out of the ordinary. Then I though I'd just like to see what happens when I run IE directly from the CRM server, and praise and behold, it didn't change the field! That narrowed it down. First I thought it was google Toolbar, so I uninstalled it, but the error was still there. Then I tried running IE in InPrivate surfing and then it didn't change it! So, there must be some plugin in IE that causes it. After some uninstalling and testing I found it, Swedbank has this "e-kort" or e-card program that let's you generate a one-time VISA number, this was the program that caused this error. It is probably some program this Swedish bank has bought from some american company and it has this weird feature.
Well, that just teaches you, don't install a lot of crap into IE. :)
Gustaf Westerlund
Microsoft Dynamics CRM Chief Architect
Logica
www.logica.com
Create replacement entities
The solution is quite simple, change the displayname of the built in product to "Old product" and remove all rights for all users to it and it will disapear for all normal users. You can then create your new entity with the displayname "Product".
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Friday, October 16, 2009
CRM Customization Manager
Daniel writes a bit more about it on his blog: http://blog.halan.se/post/CRM-Customization-Manager.aspx
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Friday, October 02, 2009
Off topic: P1i playlist creator
I like to listen to audio books on my Sony Ericsson P1i but it is a bit problematic since the phone doesn't support normal playlists with relative addresses and you cannot select a directory and play all files in the directory either. This is problematic when you have an audio book that you have ripped from your bought CD box since you don't want to play the tracks in any order.
The phone also requires the absolute path of the playlist to use the drive "D:" and nothing else. This is very strange and I think it is a big flaw with an otherwise good phone.
To ease my troubles I created a small program that can create the m3u files in the correct format to support the P1i and I don't see any reason why I shouldn't share it since it only took an hour or two to create.
Just copy the directory with the mp3:s to the memory stick and run the program and select the directory and enter a fitting name for the m3u file and click run. The m3u file is created. Enjoy.
http://www.westerlund.info/files/P1iM3UMaker.zip
Not much CRM or SharePoint but it's free! Right?
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Friday, September 11, 2009
VPC:s and Shared Networking
As you might know, each windows machine has a SID, which is supposed to be a unique identifier for the machine. Exactly when it is used, I am not certain of but I know that you can get into a whole lot of strange trouble if you have two computers with the same SID on the same LAN.
It can easily be changed on any normal windows machine or server with forinstance the tool newsid. However, there is one type of machine that you cannot change SID on and that is a domain controller.
To ease the managing of normal VPC:s it is usually best to create VPC:s that contain everything in one. That is, Windows server, Domain controller, SQL Server, Reporting Services, Visual Studio 2008 and, of course, CRM.
So, we have several VPC:s that have the same SID and we have people sitting next to eachother running them at the same time. The only network setting that can be selected, to still conect to the internet in the VPC is hence, Shared Networking.
This setting is actually a small local network in it self with a NAT (Network Address Translation)/ DHCP server/switch in the VPC host program.
I noticed a problem today when I started up a VPC and noted that I had forgotten to connect the network cable. After I had inserted the network cable and my host PC had full access, I still had problems getting an IP address and a propper connection to the internet from my VPC.
After some fiddeling with trying to run ipconfig /release, ipconfig /renew and sleeping the VPC, I finally shut down the host program and opened the VPC again after which I ran ipconfig /renew, finally it worked.
My interpretation of this is that the NAT/DHCP server need to have a working network connection in the VPC to intitialize properly. Since I didn't, it just miss started and my VPC:s never got any proper IP:s. The only possible way of restarting it is to restart the VPC host program. After it has been started, you just have to run ipconfig /renew to get a new IP from the VPC DHCP/NAT server.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Wednesday, September 02, 2009
Problems with referenced assemblies in plugins
Here at Logica Sweden we have developed a CRM Framework with lots of nice tools to aid your everyday CRM development. The architect behind this hasn't been me but my fellow CRM architect Daniel Halan. This framework is contained in a dll and it is used in plugins.
The problem I was facing was that when I updated the framwork and rebuilt my plugins, to the assembly directory, it also put the framework dll in this directory. But when I tried to run it I got the most weird errors saying that it could not find methods, constructors and similar. If I installed the framework dll in the GAC then it would all work fine. This is something that I would like to avoid, however, since it is a bit cumbersome when developing.
After some collective debugging we found the error, plugins don't use the dll:s in the assembly directory, but the dll:s in C:\Program Files\Microsoft CRM\CRMWeb\bin (or similar depending on where you have installed CRM).
So, make sure you put your referenced dll:s in the correct directory and happy coding!
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Tuesday, July 07, 2009
One of my managers is number 9!
Have a look at the list you self here: http://dynamicsworld.co.uk/index.html
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Friday, June 05, 2009
Workflow scope and security roles
- User
- Business Unit
- Parent: Child Business Unit
- Organization
Bellow the selection of the scope, there are some workflow triggering options, like starting the workflow when on create or when an attribute has changed.
The meaning of this is that dependent on the scope you have selected, the triggering will be set up, based on the user who owns the workflow.
So, for instance, if I select "User" in scope and then select triggering on attribute change, and I am the owner of the workflow, then it will only trigger when I make a change to the object.
If scope is set to Organization, it will trigger for all users in the tennant which is why this is most often used. Note however that user is set as default, so you'll have to change this when creating the workflow if you want it to work for someone else than yourself.
This is a useful tool since it can allow powerusers to create their own workflows to help them with their work without actually enabling this for everyone else.
As I have mentioned earlier, the workflow functionality is very powerful, and even thought there are protections agains infinite recursions and such, there is still the risk that semi skilled powerusers creating workflows that put a heavy load on the asyncservice, so I would be a bit reluctant on letting them use this. Thorough training is a must before and try to teach them to keep the workflows simple. If you have several custom workflow activities, for instance, activities for integrating with other systems, I would be very careful since it is hard, if not impossible to restrict a custom workflow activity to just a selected amount of users.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Monday, June 01, 2009
Internet Facing Deployment
So, how do you do it?
There are some good documents provided by Microsoft on how to do it(like this one: http://www.microsoft.com/downloads/thankyou.aspx?familyId=3861e56d-b5ed-4f7f-b2fd-5a53bc71dafc&displayLang=en) but there are some parts of it that are a bit tricky and depend on some of the infrastructure components you have, like DNS, and that might not be Microsoft, or even hosted. So I thought I'd describe a bit about how it works and give an example of how to do it.
First of all you have to do a normal installation of CRM 4.0. You can install on port 5555, 80 or any other TCP port. It doesn't matter for IFD deployment, however, port 80 or 443 have to be made available from the Internet so the physical placement of the CRM server is critical. You can use normal redirect techniques or reverse proxy lookup (as in ISA-server). The important fact is that the server must be placed so that it can be reached from the outside.
The next thing you have to set up is an external name that you can use when adressing the server from Internet, hence it has to be a real name and not just an internal name. If you have registered a domainname you could set a hostname to point to the organizations you want to access.
I used dyndns.com and registered a name like "mydomain" and my CRM organization was mycrm, when using IFD that would make the address for this org:
http://mycrm.mydomain.dyndns.com
Make sure that everything bellow mydomain.dyndns.com also gets redirected to you CRM servers external URL (port 80 or 443 depending on if you are using SSL or not)
The next step is to run the IFD Tool as referenced in the Microsoft Document. It can be downloaded from here.
Use it to set up CRM by setting the right stuff in the web.config and setting some keys in the registry. It is a lot easier than doing it yourself. You have to set it up to the external name you have chosen.
Now the final step is to add a host header in the IIS website for CRM for *.mydomain.dyndns.com or explicitly for mycrm.mydomain.dyndns.com for port 80 or 443 depending on your setup.
Now you should be able to browse to mycompany.mydomain.dyndns.com and get to the logon screen.
To get reports working you have to install the report connector software found on the CRM CD/DVD.
There are of course lot of variations to this, using reverse proxy of ISA Server and all the options of setting up certificates for SSL but I'll leave that out of this posting to keep it simple.
Menno has recently published a posting on this as well (http://www.tekoppele.com/Blog/post/2009/05/30/How-to-configure-an-Internet-Facing-Deployment-for-Microsoft-Dynamics-CRM-40.aspx, and there are several other blogs and MS KB articles concerning this. If you have problems, try taking it a step at a time and analyse and design the setup first so that you have got it figgured before you start configuring!
Good luck!
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Friday, May 08, 2009
Enhancing performance in virtual machines
When I tried to apply the change to the file they mentioned (%appdata%\Microsoft\Virtual PC\options.xml) I had a problem since I didn't have the parent tag in my file. I checked around a bit and found that it could just be added to the main structure. So, after modifications, my file looked like this:
----
</virtual_network>
<virtual_machines>
<enable_idle_thread type="boolean">true</enable_idle_thread>
</virtual_machines>
<window>
---
I would recommend adding it, if you most of the time have the computer connected to the power outlet, since it will probably increase the performance of your virtual machines!
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Monday, April 20, 2009
Importing large customization files to vanilla system
Since the workflows are dependant on the entities of the CRM system, a workflow cannot be imported if the entities it depends on do not exist.
This is usually not a problem in English based systems since Workflow starts with the letter "W" and hence is quite far down the list. In Swedish it is named "Arbetsflöde" and hence will be placed at the top. So, when importing the customization file, if importing everything, the system might give you an error if any workflow is dependant on an entitiy with a displayname after "A".
The solution is simple, just import everything but workflows first and then import workflows.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Monday, February 23, 2009
Errors when programmatically attaching events in forms
I attached an event in the onload by using
crmForm.all.date1.attachEvent("onchange", myFunc1);
and for the date2 field:
crmForm.all.date2.attachEvent("onchange", myFunc2);
The weird part was that if I changed date1 first and then date2, myFunc2 didn't fire. But if I first changed date2 and then date1, both fired as they should.
So I tried just adding an alert instead using the normal onchange method via the form editor in CRM and that triggered every time, just as it should, so something is different.
The simple solution seemed to be to move date2 from the second tab to the first. That got everything working just as it should.
I havn't tried it but I think that one could also write a method that confirms all the event attachments and then calling this at the end of every onchange triggering function and perhaps some extra time when the tabs are changed to make sure the events are bound correctly.
I have checked the documentation in CRM for if this kind of runtime event attachment might not be supported but my interpretation of the text is that is should be all right according to it. The closest paragraph in the unsupported customization section is the following:
"The use of custom HttpModules to inject HTML/DHTML into the Microsoft Dynamics CRM Forms. "
As I wrote, my interpretation is that attaching events (not even overwriting the onchange), should be ok according to this since we arn't injecting anything, just adding an event listener. Hence I feel that this must be interpreted as a bugg in CRM.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Tuesday, February 17, 2009
Unicode conversion
http://rishida.net/scripts/uniview/conversion.php
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Really cool way of getting FetchXML from advanced find
http://ronaldlemmen.blogspot.com/2009/02/using-advanced-find-for-fetchxml.html
He describes how to, without any code or customizations to a system, easily get the fetchxml code from an advanced find by just putting some javascriptcode in the url of the window. Beautiful in all it simplicity!
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Force update of Report
However, I got a puzzeling bug. The data in the quote is of course based on data in some CRM entities and when a quote was generated and the user found something was wrong, changed this in CRM, saved and then tried to regenerate the PDF, the change didn't show. I restared Internet Explorer, and regenerated the quote-pdf and now it showed correctly. Hence some sort of caching error.
I looked around the internet a bit and found that by adding the following parameter to the querystring, I could refresh the sessions variables and I would get a fresh and correct report.´
&rs:ClearSession=true
So, the entire url was (except for the report id etc.):
&rs:Command=Render&rs:ClearSession=true&objid={D1F1FD5D-D7F8-DD11-967B-005056AA29F5}&rs:Format=PDF
So, if you get a similar error, just try setting this.
It is also possible that IE caches the pdf and then you'll have to add some variable to the querystring, like a tick or datetime that is unique for every call (from that client) and that will bypass IE:s caching.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Tuesday, February 03, 2009
Workflow warning
After a bit of looking around I found a workflow that was set to trigger on attribute change on an entity and then later in that same entity, changed some attributes. This caused an infinite loop which was the cause for the hogging. By first unpublishing the workflow and then removing all started systemjobs, I got the machine back up to speed.
My conclusion from this is that even though the workflow GUI seems easy it is deceitfully powerfull and you quite easily create workflows that hogg the server (the server hosting the async-service anyway). This should be taken into consideration when putting this tool into the hands of non-programmers since the risk for the entire system stability is large. This can be either application consultants or power users at the customer.
There is also no standard way of locking some of the triggers on workflows so that you can eliminate this risk. I you know of any, please leave a comment!
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Monday, February 02, 2009
Process implementation strategy
1. Fully supported customizations only
2. Flexible and non-static.
3. Power user modifiable
4. Movable from environment to environment (dev-test-production)
I will discuss the different aspects bellow and why I feel they are important.
Fully supported customizations only
This is almost so self-evident that it doesn't even need mentioning but I have seen several CRM implementations that not only nudge this paradigm in the corner, but bite big large chunks out of it. For me, the number one reason why this is so important is that all customers will sooner or later want a hotfix or upgrade to a later version and when Microsoft, in their greatest foresight and benevolence definied the concept of supported customizations, they made this possible without costly maintanance agreements or hostile customer relationships. So, not following it should be punishable by death or something close to it.
I would presume that you CRM implementations are meant to last for your customers and that you want a long a fruitfull relationship with your customer, why you just cannot underestimate this point. If you have to make unsupported customizations, make sure they are as light weight as possible, document them thouroghly and how to validate them when installing a hotfix or upgrade.
A business process must hence be made as supported as possible.
Flexible and non-static
The solution has to be flexible and non-static. We, and our customers live in an ever changing world and to cope with this, business processes have to change as the world around a company changes. Just imagine the changes the telecom industry has gone through the latest 20 years. To fully accomodate this the customer implementations have to be flexible and changable. Microsoft Dynamics CRM is one of the most versatile and flexible systems available, especially now that it incorporates windows workflow foundation. Implementing a business process in code entirly, locks the processes down, and builds a dependance on the CRM delivering partner, something every customer-value focused consultant must despise. I do not need to force my customers to use my services when I can get them to choose me because of the value I deliver to them every hour of every day.
Instead, use the power of Windows Workflow Foundation combined with custom workflow activities. If needed, trigger these workflows using plug-ins and isv.config customizations.
Power user modifiable
A business process often involves users of different sorts and communicates with these using emails, tasks and other activites. The content of these and the recipients, sometimes dynamically set, sometimes static (like the CFO), might need to be changed. Therefore it is not very flexible for the customer if code has to be re-written everytime a mail has to be changed. It is a lot more flexible to create a workflow that creates the activity and then just execute the workflow from code, apart from the fact that it is a lot quicker to implement. Just write a method that enables you to execute the workflow by name instead of Guid.
Movable from environment to environment (dev-test-production)
All implementations of any notable size always includes at least three environements, not seldom up to 5 or 6 depending on how you count (local dev, central dev, test, training, production). Business process implementations have to be easy to move between solutions without any or with as little as possible environement specifics. Hence, try to use names (or some other environment indpendant value) instead of guids since guids are environment specifics when relating to workflows and other similar things. Try to get all environement specific settings in one place, one file or create an entity for them. This can be a challange but try, it will pay off in the long run.
These are my thoughts on the business processes implementation stategy concerning Dynamics CRM 4. I would be happy to discuss the topic so please leave comments!
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Thursday, January 29, 2009
Problem opening workflows in CRM GUI
However, today I had just installed some stuff and was going to review one of the workflows, and the CRM GUI crashed with the Exception "System.NullReferenceException" in a really nasty way when I tried to open it. By looking at the stack trace I got a feeling it had something to do with my custom activities so I just reregistered them and then everything started working again. So if you come across this error, this might be the problem. I couldn't find any reference to this error on the net why I thought it might be a good idea to blog about it.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Wednesday, January 28, 2009
Workflow activity development and file locking
To handle this, I tried a good program called Unlocker 1.8.7 which you can find here: http://ccollomb.free.fr/unlocker/ - it is freeware and it will tell you which processes are locking a file and can help you kill them.
So, what is locking my dll? Well, actually several processes are involved both the MSCRMAsyncService and the IIS worker process (w3wp).
The most obvious way of handling it is just restarting the two services and that will work. However, restarting the w3wp process will require the entire CRM application to be recompiled which will create extra long and annoying waits. Instead, just recycle the applicationpool involved (CRMAppPool).
There are no problems restarting the MSCRMAsyncService.
And if you want to do it all in the pre-build event just add the following lines:
net stop "MSCRMAsyncService"
net start "MSCRMAsyncService"
cscript C:\Inetpub\AdminScripts\adsutil.vbs stop_server W3svc/AppPools/CRMAppPool cscript C:\Inetpub\AdminScripts\adsutil.vbs start_server W3svc/AppPools/CRMAppPool
And if you are still having problems, use the unlocker program!
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Tuesday, January 13, 2009
List Web Part for Dynamics CRM 4.0
If you have any experience of it, please let me know.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Thursday, December 18, 2008
How to make a workflow wait until a certain date
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Sunday, November 23, 2008
AT&T Wireless self destructs...
Please read it and leave a comment here if you want to discuss it!
http://www.cio.com/article/32228/Project_Management_AT_T_Wireless_Self_Destructs
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Wednesday, November 19, 2008
Automatic restart of Async Service
net stop "MSCRMAsyncService"
net start "MSCRMAsyncService"
Please note that you might inflict any ongoing workflows by doing this so take care if it is a live production server.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Friday, November 14, 2008
Quick create
In CRM 4 this feature is not used any more and the complete form will always be shown in this case, however, the functionality hasn't been removed. I tried to find some kind of reference to it in the SDK but couldn't so I had a look in the original CRM code in CRM 3 and found how to do it.
To open a form as quick create, find the object type code (1 = account etc) and go to the URL:
http://[server:port]/[organisation]/_forms/QuickCreateReturn/quickcreate.aspx?crmFormSubmitObjectType=[objecttypecode].
For example, for account on the local machine when CRM is installed on port 5555 and the company is named "company":
http://localhost:5555/company/_forms/QuickCreateReturn/quickcreate.aspx?crmFormSubmitObjectType=1
Since I couldn't find any official reference to this in the SDK it is very possibly unsupported to do this but it works for now. Any hotfixes or similar might, but are not very likely to change this functionality, new versions of CRM like v.next are more likely to change/remove this functionality.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Tuesday, November 11, 2008
Putting all javascript code in external files
However, it is only fine as long as these scripts arn't that many and not too complex. When they start getting more and more complex, bigger and their numbers start increasing it soon becomes an impossible tast of maintaining them all and there is no built in function to handle javascript includes without special code. It also takes a long time developing more complex code in the javascript form window in CRM and then running the preview or publishing it each time before testing.
There is light in the tunnel however, by using the technique specified bellow, you can both
get global javascript files, save and refresh only to see you changes, and keeping all scripts for one entity in one file (and not one for onLoad, one for onSave and one for each onChange that is needed).
A lot of credit for this has to go to Michael Höhne at Stunnware since he added a posting on his blog on how to manage javascript includes in runtime.
So, first of all we have to put some standard code in the onLoad. This should only be done once and it shouldn't really be changed until you need to publish it for release (switching of the caching).
var LoadFile = function(url, cache)
{
var httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
httpRequest.open("GET", url, false);
if (!cache)
{
httpRequest.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
}
httpRequest.send(null);
return httpRequest.responseText;
}
eval(LoadFile("/isv/mycrmext/javascripts/MyGlobal.js", false));
eval(LoadFile("/isv/mycrmext/javascripts/Account_onload.js", false));
This code simply loads and executes the two files MyGlobal.js and Account_onload.js located in the directory C:\Program Files\Microsoft Dynamics CRM\CRMWeb\ISV\myCRMEXT\JavaScripts
and can be found at the url: :port/isv/mycrmext/javascripts/">http://
So, copy-paste it into the onload event form in the account form editor in CRM. Activate the event, save and publish.
Then you can create the two .js-files at the specified directory. Start with just some test code like:
alert("This is the MyGlobal.js-file");
or something similar.
The next step is to try to avoid using the onsave and onchange events. This is quite easy when you know how but it is sort of in the gray area of supported customizations so be aware of this.
In the file Account_onload.js add the following code:
var MyOnSave = function()
{
alert("onSave");
}
var MyOnChange = function()
{
alert("onChange");
}
alert("onload)
crmForm.attachEvent("onsave", MyOnSave);
crmForm.all.name("onchange", MyOnChange);
This attaches a simple function to the onsave event of the form and another to the onchange event of the attribute "name".
The functional defintions/declarations can be put into any of the includefiles.
Now, just save the file, and open an account to see the magic! (It should show a dialog when loading with the text "onload", when the accountname is changed, it should show "onchange" and when saved, it should show "onsave".
Please note that I would advise you to put the javascript files in the same "host" from a IE perspective since you will get less problems with cross domain scripting and similar IE related security issues. This doesn't however mean that you cannot put the js-files anywhere on the internet where the clients can access it.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Wednesday, November 05, 2008
How to get the URL to the reportserver programmatically
Finally I gave up and tried to find some other way to get it and I remembered that it is set in the registry so I wrote some code to get it from there and here it is in all it's simple glory:
RegistryKey regkey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\MSCRM");
string reportserver = regkey.GetValue("SQLRSServerURL").ToString();
And it worked but only just since it uses the server name and my VPN connection to the customers environment didn't bother with sending it to me why I have to manually add it to the hosts file.
The host file can be found at the path: C:\WINDOWS\system32\drivers\etc\hosts
and I added the row:
192.168.0.100 CRMTESTSRV
Now my button which point to my aspx that redirects to a the report in pdf-format works just fine and is independant of the CRM server it is installed on.
The solution isn't supported as far as I know since you never know if they might change the registry value in the future (not very likely in an update but might happen in an upgrade). If anyone has any supported way of getting this URL, please comment this post.
This could also be done using javascripts but I find it easier to manage server side code and it doesn't add that much overhead considering the report has to be generated independantly of if I use aspx or javascript to open the report.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Sunday, October 26, 2008
Attribute problems
As I am reading the book "CRM as a rapid devleopment platform" by David Yack at the moment, I will probably be commenting a bit on some of it's content. Even though I might not fully agree to all that is said in the book, I must really give it my best recommendations since it is really a good tool and has a lot of very useful tips and code libraries that can be used.
One of the things Yack treats in the book is the how to import and export customizations and his recommendation is to take as few entities as possible. My personal view is still that the best way to maintain "mirrored" systems and avoid any problems when importing, is to almost always move the entire set of customizations (using the import/export all entities) since I believe that will reduce the risk of anything bad happening and that the systems might accedentally not be the same until someone notices that an entity hasn't been moved from one system to another. Yack's manner of handling this is not directly wrong in my opinion, and it might work well but there might be need for more extensive documentation of exactly what customizations exist in all the different systems (for those who didn't read that posting, most commonly: Production, Test, Central Dev, Educational and Distributed VPC Devs)
A similar problem that might give rise to some headaches is that you might have seen to it that all systems have the same customizations and then notice that some attribute might have the wrong data type, and since you can't just change the datatype, you remove it and then recreate it using the same name as before. Now you try to export and import this entity to some other system, it will crash due to the fact that CRM will try to append the changes made but it doesn't log that you have removed the attribute and will just export an image of the exisitng system. When importing, it will try to find any changes and add these. When it find's a missmatch in the datatype of the entity in question, it will simply stop the importing.
So, try not to change attributes or entities this way. If you have created an attribute with the wrong datatype, remove it and create the new one with another name.
It is probably also possible to first remove the attribute, export the customization and import it to ALL system and then add the attribute, export and import it to all systems. Then you might get it to work but it's easy to miss one development VPC system and then you'll have to remove the attribute(s) by hand and that just isn't very good since these kinds of changes should be limitied to the customization master system.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
The application platform on which CRM has been built
Today, since it is Sunday and I have some "free time" I took the opportunity to read The CRM Teams Statement of Direction and I found a very interesting part in the end that give a very direct statement on how Microsoft views this:
"Thus the underlying technology platform becomes an application engine to run a whole range of business applications. Microsoft Dynamics CRM simply becomes one business application that is run on the application platform."
So, perhaps we have put our card on the right horse this time (compared to Lehman and Brothers investors) since the future might show that experience in CRM development isn't only going to be a great asset when working with CRM but also a great asset since it will allow us to have a good heads start on the application platform, and the new applications that will be built ontop of it.
Discussions has also been around what is going to happen with NAV and AX. This is mere speculation, but it might just be that theses applications are the first to be migrated to the new application platform. So, get you MPC (Material and Production Control) books out and dig into ERP since that might just be the right skill to have.
If you havn't noticed, the work name for the new version of CRM is V.Next. Let's see if it will be 5.0 or perhaps 8.0? :)
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Sunday, October 19, 2008
CRM as a Rapid Development platform
He focuses a lot on viewing the Dynamics CRM platform as a development platform instead of just an application with a powerful API. This is a view I believe many Dynamics CRM developers share with him. The book also focuses a lot on working as a consultant and the choices that one needs to take when working as a consultant, like, is this sort of solution going to be working independantly of new hotfixes, patches and upgrades or are we going to create a cause for conflict with our customer.
As many of you know, Dynamics CRM addresses these issues as a platform in the definition of "supported" customizations and this is discussed throughout the book.
Included in the price of the book is also an entire library of useful tools that can be used when developing and that Yack recommends using in real life projects and that are licenced as such.
It is THE book for all Dynamics CRM developers looking for both tips and tricks on how to for instance, actually create a plug-in and also great use in understanding that Dynamics can be used as a platform for developing almost any kind of application. Yack has deliberatly left out information concerning how to use CRM out-of-the-box and other normal application based aspects of Dynamics CRM, something I find very good since it focuses the book on what it is meant to do.
If you work with development in Dynamics CRM, this is probably going to be one of the best investments you can do, since it will help you create better customizations faster with more customer value, and who doesn't want that?
Thanks David Yack for a great book!
Read more here: http://www.thecrmbook.com
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica Sweden
www.logica.com
Friday, September 26, 2008
Handle customization.xml (.zip) with care
It is not uncommon for a project to involve more than 3 or 4 depoyments that need to be identically set up. Usually the following are used:
- Production environment
- Test environment
- Central development environment
- Distributed developent environment(s) - usually locally run VPC:s
- Educational environment
If care isn't taken when creating customizations and exporting and importing the customization files it is not hard to imagine problems arising when the same entity has been edited by different people at more or less the same time. This can result in the several of the different systems not being identical and perhaps even dataloss or the worst possible case, a system might crash. Therefore it is very useful to define a strict path for all customizations and the distribution of all customizaton files.
We usually handle this by simply defining the central development environment as the customization-master. This means that all entity customizations are done on this environment only. Then customizations are exported by almost always exporting all customizations and importing into all other systems from this file. This will hopefully reduce the risks of creating environments that are non-identical or erroneous.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica
www.logica.com
Monday, September 22, 2008
The power of SharePoint
At the time I was involved in several SharePoint implementations and we also had SharePoint as our Intranet at Humandata.
A couple of months ago Microsoft Office SharePoint Server was released at Logica as the global Intranet tool and I must say it is amazing to see the true power of SharePoint in a large organisation. At Humandata we were less than 20 employees and we all knew each other and more or less lived in the same City. The story at Logica, however, is drastically different, with around 40 000 employees in 41 countries.
With SharePoint we can now all communicate, I have taken the initiative to start a Logica-Global Dynamics CRM community to connect all the very talented Logica employees that in some way or another work with Dynamics CRM. We are still in start-up mode but the future looks bright and SharePoint is the enabler that makes it all possible.
Apart from this, it is also a great tool for running projects in. Instead of millions of post-its with all the questionsmarks in the project, we use a discussion board. Instead of impossible hierarchies of folder for SureStep documents, we use one Document Library with Meta-data describing what part of SureStep the document should be connected to. This way, documents are easier to find, easier to work with, easier to share and we get version control all for free.
So, if you are part of a large organisation, try to push for the use of SharePoint especially if this organisation is spread over a large geographical area and the use of virtual meetingspaces and focalpoints is of great use.
If you are employed at Logica and would like to be part of the community, just send me an email or leave a comment bellow - they are moderated so I don't have to publish them if they are private.
One thing to be remembered though, even if SharePoint is easy, nobody is born to understand it or the concepts in it, especially if you are used to working in a normal directory based filesystem, so make sure all your users get a good basic run-through and give them the possibility to dig deeper on their own. If you neglect the educating of the users you risk spoiling most of your investment, and the users view of SharePoint, something that is both sad and bad.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica Sweden
www.logica.com
Monday, September 15, 2008
Mark appointment as complete from Outlook?
We did find a workaround however. We told the salespeople that when an appointment was to completed, to just add the words "DONE" or some other special character like "§" after the normal text in the description field. A workflow had been created that is triggered on changes to the description field and it checks to see if the text inside the field ends with "DONE" or the designated character. If so, the status of the appointment is changed to "Completed".
I realise that this isn't a perfect solution, a button would be suitable and less prone to error, but at least it is one way of handling it. I would be very intrested in any other solutions that you might have experience of, please leave a comment if you have any suggestions.
(Of course you can always open the CRM window and click "Save as completed" but that introduces a new GUI and complicates things for the users, we are looking for a simpler approach, just using Outlook)
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica Sweden
www.logica.com
How to create a phonecall directly from Outlook

Microsoft Dynamics CRM Architect
Logica Sweden
www.logica.com
Tuesday, September 02, 2008
Nominated to .NET-Awards
My parental leave is soon at it's end and this thursday I will be back to work in full force! Hopefully I will also have lots of interesting topics to write about aswell!
.NET Awards will be held this thursday here in Sweden in the Stockholm suburb of Kista. The solution I have been working with since I started at Logica, ICE42 for ICE.NET, a Swedish Telecom Operator, has been nominated and we will hopefully win!
If you are fluent in Swedish you can watch a move with my colleague Mats Jonasson and the CIO of ICE.NET Thomas Norberg as they talk about the solution. Unfortunatley, I was ill the day of the recording, hence, I am not in it.
.NET Awards 08 Nominerad: Logica Sverige AB
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica Sweden
www.logica.com
Tuesday, April 22, 2008
Upgrading callouts to CRM 4.0
Busy as ever, I am still working on this large CRM project with lots of integrations. Very complex and very interesting.
At the moment we running CRM 3.0 but we are currently looking at upgrading to CRM 4.0 since we need to implement a new datamodell containing more than 40 very tighly connected entities with multiple relationships, self referentail relationships and much more! We are very lucky to be working with CRM 4.0.
The first part of the upgrade we are doing will be to just get everyting working in CRM 4.0 with as little or no code review as possible. We will lift all the code later (i.e. converting callouts to plug-ins, using the new web service and so on) when we have got everything running.
In other words, I first of all needed to get callouts from CRM 3.0 to work properly in CRM 4.0. After the "sleep-inducing-upgrade", I found that they simply didn't work. As I had made a few callouts that generated custom accountnumbers and other similar tasks, it became clear that it crashed because of the callouts.
I activated CRM tracing (http://support.microsoft.com/kb/907490/en-us) and soon found that the reason for the callouts not working was that CRM could not find the dll containing callout.base. So, I added the dll (Microsoft.Crm.Platform.Callout.Base.dll, found on the CRM 3.0 CD 1) to the GAC (C:\windows\assembly) and then ran iisreset.
After this, the callouts worked just like they should!
On the same topic, if you have a custom virtual directory bellow the CRM website, just add the file: C:\Program Files\Microsoft CRM\CRMWeb\bin\Microsoft.Crm.WebServices.dll to the GAC aswell, since it won't work otherwise. There are other ways of getting around this problem aswell, but I prefer this solution.
On the first of may I will be on parental leave for 4 months, so that I can really get to know my daughter without my wife getting in the way! ;)
Hopefully I will have time to write a bit on this blog aswell.
Gustaf Westerlund
Microsoft Dynamics CRM Architect
Logica Sweden
www.logica.com
p.s.
If you havn't tried Bio Shock on XBOX 360, you really should!
Tuesday, March 04, 2008
isv.config in CRM 4.0
First of all, in CRM 4.0 there is no isv.config in the _resources folder any more. Not very strange since CRM 4.0 is multi-tennant and each tennant can have a unique isv.config. So, where is it? The most obvious place to look for it is in the "export customization" view, and surprise, there it is!
So, to change it in short, select isv.config from the export customizations and press select "export selected customizations" from the action menu. Save the zip-file somewhere, unzip the xml and open it in your favortite xml editor or notepad. When done, just import the xml directly (yes, it supports importing the xml file directly without zipping it first). If you have done your job correctly, there should be no problem, if not, you might get an error. Revert to an older version of the file (always save a backup before doing any changes) and then try a smaller change.
To activate isv.config customizations you have to "enable" these customizations from the web.config in the CRM web folder. Please see the SDK for more info.
To make things easier for you, here is the code in an empty/minimal isv.config (that has worked for me any how). There might be things that can be removed still, to make it smaller. Please leave a comment if you know of any. Please note the lcid:s (language code id:s) that appear all through, as you can see, this is the english version (1033).
<ImportExportXml version="4.0.0.0" languagecode="1033" generatedBy="OnPremise">
<Entities>
</Entities>
<Roles>
</Roles>
<Workflows>
</Workflows>
<IsvConfig>
<configuration version="3.0.0000.0">
<Root>
<NavBarAreas>
</NavBarAreas>
<!-- The main Global Menu Bar located at the top of all root level areas -->
<MenuBar>
<!-- Custom Menus that appear between the Goto Menu and the Help Menu -->
<CustomMenus>
<Menu>
</Menu>
</CustomMenus>
</MenuBar>
<!--
Application Level Tool Bar
-->
</Root>
<!-- Microsoft Customer Relationship Management Entities (Objects) -->
<Entities>
<Entity name="account" />
<Entity name="contact" />
<Entity name="lead" />
<Entity name="opportunity" />
<Entity name="list" />
<Entity name="campaign" />
<Entity name="campaignactivity" />
<Entity name="campaignresponse" />
<Entity name="incident" />
<!-- Case -->
<Entity name="quote" />
<Entity name="salesorder" />
<!-- Order -->
<Entity name="invoice" />
<!-- Custom Entities -->
<!-- <Entity name="new_myentity"/> -->
<!-- End Custom Entities -->
</Entities>
<!-- Microsoft Customer Relationship Management Service Management Customization -->
<ServiceManagement>
<AppointmentBook>
<SmoothScrollLimit>2000</SmoothScrollLimit>
<TimeBlocks>
<!-- All CSS Class mapping for Service activities -->
<TimeBlock EntityType="4214" StatusCode="1" CssClass="ganttBlockServiceActivityStatus1" />
<TimeBlock EntityType="4214" StatusCode="2" CssClass="ganttBlockServiceActivityStatus2" />
<TimeBlock EntityType="4214" StatusCode="3" CssClass="ganttBlockServiceActivityStatus3" />
<TimeBlock EntityType="4214" StatusCode="4" CssClass="ganttBlockServiceActivityStatus4" />
<TimeBlock EntityType="4214" StatusCode="6" CssClass="ganttBlockServiceActivityStatus6" />
<TimeBlock EntityType="4214" StatusCode="7" CssClass="ganttBlockServiceActivityStatus7" />
<TimeBlock EntityType="4214" StatusCode="8" CssClass="ganttBlockServiceActivityStatus8" />
<TimeBlock EntityType="4214" StatusCode="9" CssClass="ganttBlockServiceActivityStatus9" />
<TimeBlock EntityType="4214" StatusCode="10" CssClass="ganttBlockServiceActivityStatus10" />
<!-- All CSS Class mapping for Appointments -->
<TimeBlock EntityType="4201" StatusCode="1" CssClass="ganttBlockAppointmentStatus1" />
<TimeBlock EntityType="4201" StatusCode="2" CssClass="ganttBlockAppointmentStatus2" />
<TimeBlock EntityType="4201" StatusCode="3" CssClass="ganttBlockAppointmentStatus3" />
<TimeBlock EntityType="4201" StatusCode="4" CssClass="ganttBlockAppointmentStatus4" />
<TimeBlock EntityType="4201" StatusCode="5" CssClass="ganttBlockAppointmentStatus5" />
<TimeBlock EntityType="4201" StatusCode="6" CssClass="ganttBlockAppointmentStatus6" />
</TimeBlocks>
</AppointmentBook>
</ServiceManagement>
</configuration>
</IsvConfig>
<EntityMaps />
<EntityRelationships />
<Languages>
<Language>1033</Language>
<Language>1036</Language>
<Language>1031</Language>
<Language>3082</Language>
<Language>1053</Language>
</Languages>
</ImportExportXml>
And the one containing only one button:
<ImportExportXml version="4.0.0.0" languagecode="1033" generatedBy="OnPremise">
<Entities>
</Entities>
<Roles>
</Roles>
<Workflows>
</Workflows>
<IsvConfig>
<configuration version="3.0.0000.0">
<Root>
<NavBarAreas>
</NavBarAreas>
<!-- The main Global Menu Bar located at the top of all root level areas -->
<MenuBar>
<!-- Custom Menus that appear between the Goto Menu and the Help Menu -->
<CustomMenus>
<Menu>
</Menu>
</CustomMenus>
</MenuBar>
<!--
Application Level Tool Bar
-->
</Root>
<!-- Microsoft Customer Relationship Management Entities (Objects) -->
<Entities>
<Entity name="account" />
<Entity name="contact" />
<Entity name="lead" />
<Entity name="opportunity" />
<Entity name="list" />
<Entity name="campaign" />
<Entity name="campaignactivity" />
<Entity name="campaignresponse" />
<Entity name="incident" />
<!-- Case -->
<Entity name="quote" />
<Entity name="salesorder">
<MenuBar>
<!-- Custom Menus that you may add -->
</MenuBar>
<!-- The Account Tool Bar -->
<ToolBar ValidForCreate="0" ValidForUpdate="1">
<Button Icon="/_imgs/ico_18_debug.gif" Url="http://www.microsoft.com" PassParams="1" WinParams="" WinMode="0">
<Titles>
<Title LCID="1033" Text="asdf" />
</Titles>
<ToolTips>
<ToolTip LCID="1033" Text="Info on Test" />
</ToolTips>
</Button>
</ToolBar>
<!-- The Account Left Nav Bar -->
</Entity>
<!-- Order -->
<Entity name="invoice" />
<!-- Custom Entities -->
<!-- <Entity name="new_myentity"/> -->
<!-- End Custom Entities -->
</Entities>
<!-- Microsoft Customer Relationship Management Service Management Customization -->
<ServiceManagement>
<AppointmentBook>
<SmoothScrollLimit>2000</SmoothScrollLimit>
<TimeBlocks>
<!-- All CSS Class mapping for Service activities -->
<TimeBlock EntityType="4214" StatusCode="1" CssClass="ganttBlockServiceActivityStatus1" />
<TimeBlock EntityType="4214" StatusCode="2" CssClass="ganttBlockServiceActivityStatus2" />
<TimeBlock EntityType="4214" StatusCode="3" CssClass="ganttBlockServiceActivityStatus3" />
<TimeBlock EntityType="4214" StatusCode="4" CssClass="ganttBlockServiceActivityStatus4" />
<TimeBlock EntityType="4214" StatusCode="6" CssClass="ganttBlockServiceActivityStatus6" />
<TimeBlock EntityType="4214" StatusCode="7" CssClass="ganttBlockServiceActivityStatus7" />
<TimeBlock EntityType="4214" StatusCode="8" CssClass="ganttBlockServiceActivityStatus8" />
<TimeBlock EntityType="4214" StatusCode="9" CssClass="ganttBlockServiceActivityStatus9" />
<TimeBlock EntityType="4214" StatusCode="10" CssClass="ganttBlockServiceActivityStatus10" />
<!-- All CSS Class mapping for Appointments -->
<TimeBlock EntityType="4201" StatusCode="1" CssClass="ganttBlockAppointmentStatus1" />
<TimeBlock EntityType="4201" StatusCode="2" CssClass="ganttBlockAppointmentStatus2" />
<TimeBlock EntityType="4201" StatusCode="3" CssClass="ganttBlockAppointmentStatus3" />
<TimeBlock EntityType="4201" StatusCode="4" CssClass="ganttBlockAppointmentStatus4" />
<TimeBlock EntityType="4201" StatusCode="5" CssClass="ganttBlockAppointmentStatus5" />
<TimeBlock EntityType="4201" StatusCode="6" CssClass="ganttBlockAppointmentStatus6" />
</TimeBlocks>
</AppointmentBook>
</ServiceManagement>
</configuration>
</IsvConfig>
<EntityMaps />
<EntityRelationships />
<Languages>
<Language>1033</Language>
<Language>1036</Language>
<Language>1031</Language>
<Language>3082</Language>
<Language>1053</Language>
</Languages>
</ImportExportXml>
Gustaf Westerlund,
Microsoft Dynamics CRM Architect,
Logica Sweden
Wednesday, February 13, 2008
SQL error when importing customization.xml
Well, things only got worse when I tried to access CRM with the common url, I got a really nasty error saying that some custom attribute wasn't at all like some other custom attribute. No dah, my worst fears just got real and I could only face the fact that the CRM meta database had somehow become corrupt. Really bad, testing was supposed to begin today, so really bad timing (and timeplan).
So, what to do? Since I couldn't access CRM I couldn't get to the import cusomtizations page, so I had a look in the SDK for the direct URL to the import customizations page (it is under sitemap customizations). I tried to reimport the customizations file from my development server, only to find that CRM now found the xml "malformed" in this file. After some testing, I found that it acctually found all customizations files to be malformed, not only the first one. So, something really sinister is at hand. Time to log into the SQL-Server.
The first message that faced me was that one of the drives had to litte disk space... I had a look and found that the main data disk had only 2 MB free. So, this was probably it. First, there probably hadn't been space enough for commiting all the changes that were in the new customization file and then there hadn't been space enough to upload the new customizations file. The error message of mal-formed xml had just been an erroneous error message.
So, I freed some space on the disk and just to be sure, I re-installed CRM entirely (it was just a test environment after all) and imported the customization file, and all went well!
Well, there was acctually one thing, when I imported the customization file I got an error saying that at some row XXX in the file there was something wrong. At the specific row, was a tag for setting "displayInApplication" or something like that for the entity "Bulk Operation". I tried to remove the tag and import it, which worked. I later remembered that I hadn't installed Update Rollup 2, which I later confirmed was the cause of this error. I had exported from a CRM 3 UR2 system and imported into a standard CRM 3, hence the error. So, I would suggest not removing the tag but instead updateing your CRM :).
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Installation of Virtual Server 2005 R2
Not so easy. I connected with remote desktop and tried to install virtual server, but I just couldn't get it to work properly, so I went out on the net and looked for a solution.
The result was that I found that you cannot install or run the virtual server web interface using remote desktop if you do not connect as "console". Weird, but that's what I found.
So, how to connect with remote desktop as "console" (that is ID 0). If you like your command prompt, you'll know that Remote Desktop is called "mstsc" just, use that with the switch "/console" and you will connect as console.
Personally I like to make things simple, why I have made a small bat-file with the following content:
mstsc %1 /console
Then I just drag-n-drop my rdp-file ontop of the batfile. This will result in a console connection to the server set in the rdp-file.
I hope this helps!
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Friday, February 08, 2008
Jonas Deibes pictures and Q&A from the CRM 4.0 launch
http://blogs.msdn.com/jonasd/archive/2008/02/07/blog-corner-report-from-the-swedish-crm-4-0-release.aspx
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
CRM 4.0 launch and more...
At the launch there was a blog corner where both Jonas Deibe and Michael Höhne were answering questions and drinking som coke. I took the opportunity to have a look at Michael's (http://www.stunnware.com/crm2/) filtered Lookup a bit closer. I had read some about it on his page and it is really a great piece of software. In short it enables filtering of lookups and in CRM even the intellisense. It is not yet fully supported by Microsoft, but Michael mentioned that it soon will be.
I also got the opportunity to talk to Jonas Deibe a bit and we talked mainly on what the differences in developing extensions are between CRM 3 and 4.0. I will go deeper into some of this in later postings.
I also got meet some of my Microsoft Dynamics CRM friends from Cybernetics, Cinteros, SysTeam and of course, Microsoft Sweden. I hope we'll meet again soon.
As I have described earlier, we at WM-Data (Will formally be named Logica from feb 27:th) are so busy at the moment that we have to de prioritize some customers, which I find very sad, since I don't like to disappoint anyone in general and customers, in particular. So, if you are a good Microsoft CRM developer and interested in working with really big customers with very interesting projects, concerning complex integration and other interesting customizations in an international organization that is really employee-friendly, please contact me and we can have a chat! My office is in Stockholm, but Logica has offices all over Europe and even in India (Bangalore).
Thats that for this time, hope to be back soon!
Stay in touch!
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Friday, January 25, 2008
Adding SQL Reporting Services reports to CRM
The part that is the major deviation from normal SQL RS work, is how to deploy reports. Even though normal deployment might seem to work, the report won't be recognized by CRM properly.
So, what do you need to do?
1. Create a report that does NOT use a Shared Datasource. Instead create an embedded datasource called CRM that connects to the CRM database server. If you are going to use the Filtered Views, you have to use Windows Authentication. Save and build the new report (DO NOT deploy to the server).
2. Open CRM, Go to "Workplace" and select "Reports". Click "New" in the list. You will see a form where you can select the rdl-file and also select where in CRM the report will be available. Select the rdl-file that resides in the VS Project folder. Press Save.
If, at this point, you had used a Shared Data Source, you would be getting a very complicated error, as described by Menno here: http://blogs.msdn.com/mscrmfreak/archive/2006/04/27/584595.aspx
If all went well, the report will be uploaded correctly.
When uploading the report, CRM will replace the datasource in the rdl with the standard shared datasource. So, your report will still be movable.
Also, when uploading reports like this, CRM will hide all parameters starting with "CRM_". There are several parameters that CRM will fill with data for you if they exisit. For instance the parameter "CRM_URL" will be set to "http://
Please note that all deployment of reports to CRM should be done in this manner and never directly from Report Designer. This is a bit of a hazzle since it is a bit tedious compared to just deploying from the Report Designer.
In CRM 4 there is a very nice wizard in the application for generating reports, even though it is not nearly as advanced as the report designer.
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Thursday, January 24, 2008
Integration basics
As I have been writing, I am currently involved in a rather large integration project. For those of you who don't have experience of working with larger integration projects I would like to just share my view of how to set up the architecture connecting Microsoft Dynamcis CRM with the integration engine (BizTalk, WebSphere, Sonic and more). Bellow, I will refer to the Integration Engine, simply as "the Bus"First of all, communication with the bus can be divided into two different classes:
- CRM Initiated Communication
- Bus initiated communication
In the picture to the right, these two are represented with a connection each (connection 1 = CRM Initiated Communication, connection 2 = bus initiated communication)
CRM initiated communication might be considered the simplest, since it mainly controlled from CRM. It can be code that runs from aspx-pages, callouts/plug-ins or workflow addons. It simply consumes the webservice at the CRM adapter on the bus.
Bus initiated communication is set up by the bus consuming a customly created web service. This "proxy" webservice translates from integrationmessages and datastructures to CRM native communication.
Integration is then definied by defining messages that uses either connection 1 or 2. Each message has two parts, Request and Response, and the response is the syncronous answer to the request. Assyncronous messages should be set up as two individual messages one for each adapter.
So, why create a proxy web service? Why cannot the bus connect directly to the CRM web service? The reason for this is usually that the communication is defined using messages (as described above) and the person responsible for the CRM Adapter on the bus cannot be expected to have any CRM knowledge in general or specificly CRM Webservice knowledge, hence that is the CRM developers job.
These are some of my experiences and reflections on integration with a centralized integration architecture. If you have any comments or you have some other method that you use, please leave a comment, so that we can discuss it further.
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Wednesday, January 09, 2008
Busy, busy + SDK
Personally, I havn't had much time to dig into CRM 4.0 yet but I noticed that the SDK has been released. You can find it here: http://www.microsoft.com/downloads/details.aspx?FamilyId=82E632A7-FAF9-41E0-8EC1-A2662AAE9DFB&displaylang=en
Happy coding!
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Thursday, December 20, 2007
CRM 4.0 Trial ready for download
I would also like to point out that there are (at least) three different media types in general released from Microsoft.
MSDN - Can use MSDN or Trial keys.
Normal - Can use Trial or "Normal" license keys.
Volume Licence - Can only use volume license keys. If you want a trial, use it for a maximum of 90 days and uninstall it.
This is important when installing a trial environment that might be upgraded to production environment in the future at a customer. Consideration has to be taken to their current license agreement with Microsoft, and you can never use the MSDN media to install a production server.
There might be exceptions to this rule, for instance, a MSDN media might accept a normal license key, but I would not expect it.
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Friday, December 07, 2007
Problems with ViewState in aspx page residing in Virtual Directory beneith MS CRM
Well, my code used ViewState to store some data between requests and this had worked fine on my local machine. But, when I moved it to the virtual directory bellow the CRM site, the ViewState handling stopped. I tried to override the LoadViewState-method but it was never fired. I tried to explicitly set the page to have the property EnableViewState = true, but that didn't help either.
Then I remembered some things about how settings are inherited from the master website to all virtual directories bellow (i.e. the web.config settings are inherited). This is the reason for why you have to add some <remove assembly="...">tags to the virtual directory's web.config when deploying bellow the CRM website.
I thought that there might be some swith in the CRM web.config that disabled the ViewState and there was; the tag I found was the following:
<pages buffer="true" enablesessionstate="false" enableviewstate="false" validaterequest="false">
So, I copied the entire tag to the web.config in the virtual directory, changed enableviewstate="true" and magic, it worked!
So, specific advice: This is why viewstate might not be working in a Virtual Directory bellow the CRM site.
General advice: All settings in a websites web.config are automatically inherited to all virtual directories bellow. If you want to change anything, set this explictly in a local copy of web.config.
Over and out.
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Friday, November 30, 2007
WebServiceStudio 2.0
A good thought but does, however, seem to have it's limitation. To be able to thoroughly understand the communication between the client and webservice a good program is needed that can help you study how the web service works and how the SOAP-messages are sent and received.
When working in a .NET environment, a .NET based client is very advantageous and a business partner of mine at the company Lemontree, Oskar Mattsson, suggested a very good application. I havn't had time to try it out fully yet, but it is also a GotDotNet-project so you can also read the code of how it is doing it's calls.
The name of the program is WebServiceStudio 2.0 and you can download it here:
here
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Thursday, November 29, 2007
Fake lookups = unsupported
When I was at convergence I asked this explicit question to, I think it was, Clint Warriner (CRM Escalation Engineer at Microsoft Support) and he gave me confirmation on what I had suspected, that these customizations are not supported. They fall under the last point concerning unsupported customizations described in the SDK:
"The use of custom HttpModules to inject HTML/DHTML into the Microsoft CRM Forms."
Since the HTML DOM is modified, this is not supported.
The reason for this is most probably that Microsoft might choose to change this in future releases of CRM (for instance CRM 4.0) and might also have internal scripting references to objects they expect to be there according to the standard HTML DOM.
I don't know if these fake lookups will upgrade from CRM 3.0 to 4.0 without problems. If you have any experience of it, please let me know.
If you have any comments on this subject, please feel free to comment bellow. I always publish comments that concern the subject and are not directly offensive. (I have activeted moderation on comments just to avoid comment spamming).
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Problems consuming webservices when developing locally
Presently I am working in an integration project with an integration engine and needed to consume one of it's webservices. I did so and I added some code to handle it (intellisense working fine) but when I tried to run it, it crashed on the constructor or the service class with the following error:
System.IO.FileNotFoundException: File or assembly name gvy6umsk.dll, or one
of its dependencies, was not found.
There was also a reference to the Windows\Temp-directory. The filename is obviously some temporary filename for the proxy-object.
After checking the web a bit, I found the error to be caused by the fact that the user running the software, did not have read/write access to the c:\windows\temp directory. I fixed it and the program ran like it should!
So, if you are in the same situation, just fix the right for the windows\temp directory and you should be fine.
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Friday, November 23, 2007
Great solution concerning javascript include files
http://www.stunnware.com/crm2/topic.aspx?id=JS18
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
CRM License specification
I have also been involved in some discussions concerning what the specifics of the external connection licese are. The following URL describes the details of this for CRM (and also other products). Please have a look:
http://www.microsoftvolumelicensing.com/userights/ProductPage.aspx?pid=161
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Monday, November 19, 2007
ASP page LifeCycle
In order to do this properly, it is vital to understand the execution model of aspx-pages and the lifecycle of the page. I found this page that describes this, in an easy and understandable fashion. Please review it, if you are in similar needs as I.
http://www.15seconds.com/issue/020102.htm
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Thursday, November 08, 2007
Nice video of Microsoft CRM use!
Check it out!
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
WM-Data/Logica CMG
www.logicacmg.com
Wednesday, November 07, 2007
Problems with Aug 07 VPC
Menno has written some about this on his blog, please read it. http://blogs.msdn.com/mscrmfreak/archive/2007/11/02/august-2007-vpc-fix.aspx
I personally liked "Method 1" described in the references KB-article. The details are described bellow. I have tested it for 5 minutes and it seems to be working. Here is a copy of the text:
Method 1: Disable the loopback check
Follow these steps:
1.
Click Start, click Run, type regedit, and then click OK.
2.
In Registry Editor, locate and then click the following registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
3.
Right-click Lsa, point to New, and then click DWORD Value.
4.
Type DisableLoopbackCheck, and then press ENTER.
5.
Right-click DisableLoopbackCheck, and then click Modify.
6.
In the Value data box, type 1, and then click OK.
7.
Quit Registry Editor, and then restart your computer.
Gustaf Westerlund
Microsoft Dynamics CRM Consultant
Logica CMG
www.logicacmg.com
Tuesday, November 06, 2007
Installed at Logica and a bit about CRM and Mobility
At Convergence, I had the opportunity to browse around and talk to some of the ISV (Independent Software Vendors) that supply 3:rd party addons for Microsoft CRM. One of the companies I found was CWR Mobility that have developed a very competent mobile client for Microsoft CRM 3 (and soon 4). It is a much more mature product (from what I could judge) than the normal Mobile Client, and it supports sometimes-connected scenarios and offline sync, as well as an offline SDK. So, if you are facing a customer with mobility demands, be sure to check out their products.
Gustaf Westerlund
CRM Consultant
Logica CMG
www.logicacmg.com
Wednesday, October 31, 2007
In general the same licensing limitations apply as I described in my previous post. The new "light"-userlicense I described bellow will be a read-only user license with a reduced price. The external connector will also be available at a substanially lower cost.
However, we found some technical workarounds that the Microsoft representative actually said were ok but I havn't asked a Microsoft sales rep and I don't know if I should...
If you want to create dynamic reports based on CRM data, what you must do is to replicate all the data that you want to base your reports on to your own database. Then create all the reports based on this database, which can be called datawarehouse or something similar.
The same "intermediary" database can also be used when you have, for instance, an internal support page where you can submit your support issue, which is then added to the CRM as a Service case. Let the application write to a database and then create an service that periodically (like every minute or so) reads the new data, and writes to the CRM webservice. There are other similar ways of doing this (like sending emails from the form to a support que) and I think you get the general idea.
The legal workaround using a subsidary company with an external connector license is also valid, but will require you to buy the external connector which has a non trivial price.
I feel this is very strange, that you using a technical solution can bypass some licensing rules that actually should be changed.
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Tuesday, October 30, 2007
Licensing of CRM and external software
According to what I have heard, it boils down to the fact that a user license is required for all users who interact with the data in any way. This means that no user license is required for a static report displayed in, for instance, SharePoint, but, as soon as there is any interactivity with the data, as for instance, drill down, a separate user license is needed.
So, if you want to display data in SharePoint that comes in whole, or part, from the CRM-database, make sure it is a static report, so that no special user license is required. Creating a report (with for instance SQL Reporting Services) that has drill-down, will require each user to have a user license.
This rises the question of what kind of licensing is needed to access OLAP cubes that are assembled from a data warehouse based on data from many different system, among these, Microsoft CRM. As far as I have understood a full user license is required for all users who can access the OLAP-cubes.
This issue will probably change in CRM 4.0 when there will be a new "light-user-license". Exactly what this means is still unclear and I have heard no details from Microsoft.
When creating any outside interaction with CRM, like a web based tool for submitting service cases, the separate "External Connector" license is needed. This is independant of the magnitude of the application or number of external users (company employees cannot use the external connector). It is also independant of if each user actually uses a named user or some common system user. I am unsure of the exact price for the external connector, but I believe it is somewhere around $40 000.
So, what does this mean for CRM-developers? That we have to have some basic understanding of the licensing modell and what limitations there are to it, so that our customers don't have to pay unnecessary license fees just because we thought that drill-down was a nifty feature in our report that is published on a SharePoint portal.
As many of you, I think these limitations are non benificial for Microsoft since they greatly restrict the possibilities of creating nice Mash-up applications and portals, something Microsoft technology is very good at and something I would view as a great advantage in comparison to Microsofts competitors.
I would also like to point out that I might be wrong in understanding some of these details, and I would be greatful if you could leave a comment if you know or think that I might have understood something wrong.
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB / Soon Logica CMG/WM-Data
www.humandata.se
Friday, October 26, 2007
Common performance problem
During one of the sessions Clint Warriner, an Escalation Engineer (he writes hotfixes) held a very interesting chalk-&-talk about CRM system maintanance and performance best practices.
It was crammed with goodies, and I will write more about it later, but one interesting thing he talked about was the possible delay in loading forms. He said this, most often, depends on the SQL server. When loading forms the list of which reports are relevant and so on, are also loaded from the Reporting server. This can, in some cases, take some time, and in some bad cases, really afect the load time of forms.
They have created a hotfix for this, that can be requested from Microsoft support (no cost) if you give the referece: KB 941592. It simply caches the information on what reports exist so that the request to the SQL-server doesn't have to be executed every time.
I will get back to the subject of performance tuning later.
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Wednesday, October 24, 2007
Convergence EMEA 2007 in Copenhagen
As you can expect, a lot of focus is on the new CRM 4.0 and all the brilliant new features of it. The other Dynamics products, mainly NAV and AX are also getting some of their well deserved air-time, but for me, it is all CRM! :)
So, anything interesting? Yes, a lot. I will be digging deeper into some of the topics later but would just like to write a little bit about everything I have seen and thought about thus far.
CRM 4.0 - What's new?
Well, a lot.
Multi, multi, multi!
Most of the focus is of course on Multilanguage, multicurrency and multitenancy. I personally feel that from the perspective of where I usually work, the two foremost "multis" are the most interesting. Multitennancy is maybe most interesting from a large enterprise view or hosting environment view
Internet facing
Microsoft Dynamics CRM 4.0 (as it formally is called, I will just call it CRM) has the ability to work as an Internet facing application, which CRM 3.0 wasn't (appart from CRM 3.0 SPE). This is really good news since it is really to be expected of the product. I havn't been able to get any info yet on how this works with MS ISA running reverse proxy (like you do in SharePoint) but I would imagine that it works (it didn't work very well in CRM 3.0).
Better Outlook client
The outlook client has gotten a major workover, and is now more stable and has a better interface. Some features to mention in that it will show a progressbar when loading and won't block the usage of Outlook during this time. It also allows for syncronization of other activity entities that just email, contacts, calendar and todo's, like for instance phonecalls and faxes. I havn't seen this in action yet.
The email subject tokens are gone! Exaclty how it is handled in CRM 4.0, is still unclear for me, but it is probably handled in the mail header somehow. Emails can also be converted to leads and cases directly.
Autocomplete in lookup fields
One very timesaving feature is that the well know autocomplete/intellisense function has been enabled on all lookupfields, vastly reducing the clickyness of CRM. Good job!
Multi language
A lot can be written about this, but in general it uses one server installation language (default language) and then MUI-packs can be installed on either the server or the client to be able to run CRM in another language. Multi language is supported for picklists, sitemap, isv.config and more but not for CRM content. This might not seem like a problem but it is, since for instance products and subjects are CRM Content. If you are looking into using this feature you had better look into it thoroghly.
Multicurrency
The tenant is set up with one Base currency that will be used as such and will for instance be the currency used in reports. Pricelists are in several currencies and exchangerates can be set using the webservice.
Mail Merge
The mail merge has been improved a lot. Web client based mail merge has been activated (is only available using the Outlook client in CRM 3.0). Language specific mail merge templates can be created. Mail Merge has been enabled for custom entities.
CRM - Excel
Excel connections can be either iqv (like CRM 3.0) or ODBC. Refreshing of data is supported and a very very powerful feature of enabling updating back to CRM is also supported including creating new objects. This is very powerful since it can be used to clean data and do imports in a simple manner.
Office Communication integration
Office Communicatior has been integrated and you will get presence info in CRM. Very useful!
Resource Center
A new feature called the resource center has been added that allows role based info to be shown to the client. I don't know the specifics of what can and cannot be done yet.
New licensing
Two new licensing methods will be supported. Per device licensing and a "simple" user license, to be used for users who might only need to view CRM data shown in SharePoint in an agregated view. The details of the later licensing method, and what it will be called, is still very unclear. There will be no price difference between the per user and per device license. I will get back to you when I know more about this.
Diagnostics and troubleshooting
There are some additional tools to facilitate diagnostics and troubleshooting. Tools mentioned are Office Client Diagnostics and the possibility to switch on tracing in CRM (very performance demanding). There is also some sort of "self-healing" going on in the client. Exacly what it does is still unclear.
Entity relationships
This has been discussed before in this and other blogs. In short, all that you wanted from CRM 3.0 is there in CRM 4.0 - many - many, 1-1, self referential, system - system. I havn't heard of any limitations to this yet, but I am sure there are some (like maybe relationsships to "order product" or "quote product".
Workflow
The workflow handling is totally new and has been moved to Windows Workflow Foundation. (.NET 3.0). This is great news since this allows for REALLY advanced workflows.
In short, you don't have to go to the server to create simpler workflows, they can be created from the web gui. A lot more creation options are supported, publishing of workflows is supported, there is a nice wizard, you can create workflow templates.
Also, the "sales process" has been removed and "stages" has been added to normal workflows, and can hence be used in all workflows, for all entities.
Workflows can now traverse relationships, the date-time handling is a lot better and the branching options has been improved a lot with support of "else" and "wait" statements. Dynamic data in forms is also supported using Workflow.
Reporting
This is also an area that has been re-written a lot. Reports are now run directly on the CRM server, which gets rid of all Kerberos delegation issues. There is a nice end-user report wizard that is about as hard as creating an advanced find saved view. Reports are language specific (if you want them to be). There is support for scheduling directly from CRM. There is support for offline reports.
Importing
There is now a nice wizard for importing data and it is possible to save and export the datamapping. It supports all entities. It supports backing out of an import, even several weeks later.
Duplicate detection
An advanced and customizable dupicate detection exists that allows for cleaner data.
Uppgrading
As promised, all supported customizations are supported for uppgrading, including callouts. There is a nice wizard that checks for possible problems before. Even though there are nice promises, I would really like to see this work before I believe it! It is almost to good to be true!
Exporting
The exporting of security roles is now supported. When exporting all customizations, even all dlls that have been added as plug-ins are included. This will enable the movement of an entire solution from one server to another. (Ex. test to production).
Scale out possibilities
CRM has been split up into several services that can be distributed to several machines, so workflow, for instance can be moved from the front-end web servers to dedicated servers allowing for better scale-out options and greater performance.
RW-Metadata
The metadata webservice now enables writing, so new attributes, entites etc. can now be created programatically!
Create users in bulk
This has also been discussed in other blogs, but in short, it allows for the bulk creation of many users at once.
Well, that was a short overview of some of the new features in CRM 4.0 and I have hardly mentioned plug-ins.
I have attended lots of different sessions and will write a lot about it soon! As for now, the dinner table is calling (as is my belly!).
Over and out!
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB (soon WM-Data)
www.humandata.se
Tuesday, October 16, 2007
Microsoft Virtual Earth Demo
I havn't checked it out, and it is used for SharePoint in the example but I believe that a lot of the code probably can be used to develop a Virtual Earth driven map service in Microsoft CRM.
So, without further ado, please read the posting and check out the codeplex project if you are interested!
http://blogs.msdn.com/sharepoint/archive/2007/10/14/announcing-community-kit-for-sharepoint-virtual-earth-maps-on-sharepoint.aspx
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Monday, October 15, 2007
Mr Dave writes some about Plug-In development
http://crm.davidyack.com/journal/2007/10/14/using-prepost-images-in-plug-ins.html
I hope to be writing some postings of my own concerning CRM 4.0 soon, but I still havn't got my hands on a version :(
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Stunnware on CRM 4 development
http://www.stunnware.com/crm2/topic.aspx?id=CRM4MultiTenancy
Next week is Convergence EMEA in Copenhagen. If you are planning to go there and would like to meet, please drop me a line and we can arrange it.
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Tuesday, October 09, 2007
CRM 4 VPC available on partner source
Here is the link if you have access: http://blogs.msdn.com/benriga/archive/2007/10/08/titan-now-available-to-all-dynamics-partners.aspx
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Thursday, October 04, 2007
crm 4, Crm 4, CRM 4, CRM 4!!!!
Lots of CRM 4 related information all around. Microsoft UK CRM Blog has some interesting information:
http://blogs.msdn.com/ukcrm/
And if you havn't been there already, keep a steady eye on The CRM Teams blog:
http://blogs.msdn.com/crm
They have added some links to videos of CRM 4.
Here is also an interesting blog containing info about CRM 4. It details a bit more the Import/Migration tool included in CRM 4.
I still have some unanswered questions:
1. Is there support for global javascript includes (like globalcustom.js)?
2. Is there a VPC out yet?
3. Other javascript support?
I would also like to look deeper into the offline API that is included.
And callouts have been renamed to plug-ins, and are "Tennant"-specific, meaning that they can be assigned to only one of the installed companies on the CRM server. Very nice indeed!
When I know more, I'll get back to you!
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Wednesday, October 03, 2007
I'm moving on!
This means that I will focus even more on MS CRM and less on SharePoint. Don't get me wrong, I really like SharePoint and will probably use it for integration purposes with Microsoft CRM many times. This new employment will give me the opportunity to work with some of the largest MS CRM implementations and I will also be assisting in building a team of (hopefully) excellent MS CRM developers and consultants. If you feel that is you, please let me know, and we'll meet and discuss it. It will also be very interesting and instructive to work with the very experienced CRM professionals at WM-Data.
I will still be activly blogging to get you the latest and most useful tips, but I will focus more on Microsoft CRM, even though an occational SharePoint posting might occur.
I would also like to say that I feel very sad to leave the company Humandata which is a great place to work and it has been a privilage to work with some of the most competent people in the business. If you are a skilled SharePoint consultant (or want to be), please contact Göran Husman (SharePoint MVP and owner of Humandata), I promise you wont regret it!
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Problems with Peoplesearch in MOSS 2007
Well, my customer just couldn't get the people search to work, and I had heard that there were some problems with using it on a site that runs on https (http with ssl), so I wasn't very surprised. As a bit of backgroud, the peoplesearch is set up as a contentsource in the search using sps3://servername. The server in this case should be the web application hosting mysite.
Well, how to solve it. First of all I tried to just create and extension of the sharepoint application on http port 2000 (http://servername:2000). It worked just as it should, when I browsed it, it worked and I was also tranfered to the default site (https://servername).
I tried adding this as a content source instead of the old one, in other words:
sps3://servername:2000, sadly it didn't work.
As a matter of fact, we had got it to work previously in the same environment. We had first installed sharepoint on http://home and then added https://home.company.com with an extension and an alternate access mapping. The later was used as the address to be used from the outside, by using MS ISA as a reverse proxy. After a while, the customer complained about problems with people copying/emailing url:s that didn't work from the outside. (the url was http://home/... and not https://home.company.com/..., so not very strange. This resulted in the action of removing the alternate access mapping of http://home (the original one) so that only https://home.company.com remained (and was hence set at the default). This worked great, people could enter "home" in their IE and they would reach the site https://home.company.com.
However, when I removed the alternate access mapping for http://home, people search started failing.
So, the remedy, I added an alternate access mapping for http://home:2000 in the zone Intranet (doesn't really matter, which zone as long as it isn't the default), re-indexed the search server and made a full crawl that worked like a charm! The point of using TCP port 2000 was that is is very unlikely that a user might just happen to enter that, it could just have been 1234 aswell...
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Tuesday, October 02, 2007
First Titan/CRM 4 screenshots!
If you, like me, havn't been able to test or have a look at the new Microsoft Dynamics CRM 4 (code named Titan), you can now. I havn't seen any VPC out yet public but will let you know as soon as I know.
In the meantime, have a look at Stunnwares/Michael Höhnes site where he writes about the many new features of CRM 4. It's been a long wait, but it has been worth it! CRM 4 seems to be great!
http://www.stunnware.com/crm2/topic.aspx?id=TitanBeta3
Gustaf
Monday, October 01, 2007
MS CRM 3 and Exchange 2007
As many of you probably have customers running Exchange 2007 or are considering running Exchange 2007, careful consideration has to be taken to the fact that the current CRM Email router does not support Exchange 2007 due to the fact that it is a 64 bit program, and the router is not. This is going to be fixed in CRM 4. For more information have a look at the CRM Team Blog: http://blogs.msdn.com/crm/archive/2007/09/27/crm-3-0-and-exchange-2007-compatibility.aspx
Gustaf
Interesting feature in SharePoint webpart properties
I am currently developing a customized search webpart for a customer and I wanted to move all environment specific variables to the webpart public properties so that they can easily be changed and the same dll can be used in all environments (dev and production). One of the properties was Scope, which defined search scope to use. It was defined in the following way:
private string scope;
[Browsable(true), Category("Settings"),
DefaultValue("Documents"),
WebPartStorage(Storage.Shared),
FriendlyName("Scope"),
Description("Define Scope of search"),
XmlElement(ElementName = "Scope")]
public string Scope
{
get
{
return scope;
}
set
{
scope = value;
}
}
Well, just to be on the safe side, I set the web part property to "Documents", which is also what I had used as default. I was very confused the next time I opened the "Modify Shared Webpart" menu and found that the field was blank! First I thought there was some error in how the property was defined, but after a while I found that it actually blank the field if it is set to the value defined as default (in this case "Documents").
So, if you run into this little "feature", don't be afraid, everything is just as it should be!
Gustaf
Wednesday, September 26, 2007
Creating custom views for MS CRM 3
Any modification made to the main or META-database is considered unsupported customization. Not very strange, since you never know what new fields or tables Microsoft might add to the database.
Sometimes, however, especially when working with reports, it can be useful to create your own views that aggregate data to your needs. Adding a view to the CRM database might be the easiest and you will probably be ok as long as you are a bit careful when naming the view. However, you don't have to do it this way. You can also create a new database on the same server and create your view in this database. To be able to access the views (and if you still want to play unsupported, tables), just add the database name in front of the viewname. For instance:
SELECT * FROM MyCompany_MSCRM.dbo.SystemUser;
The "dbo" part doesn't have to be there but is added by the view creator GUI, so I left it there to avoid any misunderstandings.
Unfortunately, the "Add Table" function in the GUI doesn't support selecting views from other tables even though it is supported in the database.
As far as I know, it is also possible to connect to a view in a database on another server. I don't know how this is done and don't really see the relevance from a MS CRM 3 perspective, but if I find out how, I will let you know.
Gustaf
Friday, September 21, 2007
Silverlight and Dynamics CRM
- Vector based graphics built on the new Windows Presentation Foundation (a part of .NET 3.0). The definition of the GUI is definied in a langauge called XAML.
- From Silverlight version 1.1, you can write managed code. In other word you can finally start working with C# or VB.NET on the client side instead of the irritating language javascript.
These two features make the foundation for a more user friendly and more rich experience of using the web, something at least I have been longing for. Being able to use managed code/CLR-code, will make web development a lot easier and speed up the process of making more robust and reusable programs.
On the CRM Team Blog you can download an example of how silverlight can be used in conjunction with Dynamics CRM 3. I saw that some people had had some problems with it, and I havn't had time to test it myself yet, so be advised.
Here is the link: http://blogs.msdn.com/crm/archive/2007/06/19/crm-and-silverlight-integration.aspx
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Sunday, September 16, 2007
Office 2007 + CRM = true
Have a look: http://www.stunnware.com/crm2/topic.aspx?id=OpenXml
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Request hotfixes online
https://support.microsoft.com/contactus2/emailcontact.aspx?scid=sw;en;1410&WS=hotfix
Gustaf Westerlund
Microsof CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Fake lookups
The blog http://andrewn23.blogspot.com/ describes in detail how this is done, so, if you are interested, please have a look. This technique can of course be enhanced in many ways.
Also, I am not certain if this technique is supported since Microsoft might decide to change the way some of these more internal pages (lookupsingle.aspx) work, hence breaking the customization.
The blog also mentions some hazards of using allcolumns that are very worthwhile reading (I like to use them to increase development time and change to specific attributes when I get performance issues).
So, take some time and have a look at it, I am however sad to see that the last entry was in october 2006.
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Thursday, September 13, 2007
Selecting multiple records from a grid in MS CRM 3
Using isv.config customizations you can add buttons to the menybar above a grid (next to "More Actions"). A special technique has to be used to fetch the selected records and Ronald Lemmen wrote some about it this summer, here: http://ronaldlemmen.blogspot.com/2007/06/fetching-selected-records-in-grid.html . I had reason to try it and ran into problems when the url of the file containing the javascript was on a different server (different tcp-port has the same characteristics as a different server from an IE viewpoint). The window.dialogArguments was null. I tried to modify the settings in IE but found no way of getting around the problem except for setting up a virtual directory bellow the CRM-site containing the html-file.
So, if you run into similar problems, try moving the html-file to a directory on the same server (from an IE standpoint).
Gustaf
User based Timezones in SharePoint 3
I have been on the look for this feature for some time. I've heard it's there but never found it. Now my colleague Sebastian Tegel found it. Have a look if you're interested!
http://sebastiant.blog.com/2079971/
Gustaf
Friday, September 07, 2007
Environment independent solutions
Developing CRM solutions that are environment independent is not trivial. By just adding a webservice you are making your code dependant on the CRM customization of that system. If you move the solution/dll:s to another system you will be running a risk of your code not working. This is especially problematic for ISVs since they never know what system their product will be installed on. However, there is a good way to handle it. The best way is to first use a plain out-of-the-box CRM system and use the program
C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin\wsdl /out:c:\temp\myCRMProxyClass.cs http://localhost:5555/mscrmservices/2006/crmservice.asmx?wsdl
This will create a file called myCRMProxyClass.cs is c:\temp.
Add this to your project. This is in essence the same this as adding a web reference and adding "using CRMSDK" or whatever you named it. Hence you can access all the CRM webservice classes.
Then create the service-instance and set the url by using the registry-key added during the installation of Microsoft CRM. Have a look here if you want to know how. http://ronaldlemmen.blogspot.com/2007/08/creating-environment-independent.html
If you have other environment specific data, it should be placed in the web.config / appsettings (if using a normal web-site/virtual directory) or you can use the AssemblySettings.dll (has to be downloaded, not from Microsoft) to create a assembly specific config-file, alternatively, add you own keys to the registry.
I hope I have given you a few hints on how this is done. Happy coding!
Gustaf
Activate a Workflow using Javascript
Workflows are a great tool. They are quite simple to create, even non-programmers can actually create them and they are flexible. However, there are some problems with workflows in CRM 3 (how workflows are handled in Titan/CRM 4 will be interesting to see) for instance, a workflow cannot be activated when an entity is updated, only when it is created, when it changes status, when it changes owner and of course, manually.
Mitch Milam has written an interesting post on his blog concerning how to activate a workflow using javascript. You can read about it here: http://blogs.infinite-x.net/2007/06/15/launching-a-workflow-rule-from-javascript/.
A bit of caution is however advised. I would actually prefer to have the workflow being activated by a PostUpdate-callout instead, to make sure that it is only run once. Activating a workflow on the on-change event can be a bit hazardous since it is very possible that the event is triggered several times on the same form before it has been saved.
Never the less, it is a good posting and can probably give you an idea or two about how to programmatically activate CRM workflows. If I wasn't block from using my own virtual directory or website with my own aspx-files, I would also prefer to trigger the workflow from an aspx-page and call the aspx-page using AJAX. Have a look at the class ExecuteWFProcessRequest in the CRMSDK as it enables you to execute a workflow using the web service.
Gustaf
Wednesday, September 05, 2007
SQL COALESCE
SQL Reporting Services is a great tool. It allows you to create flexible and exportable (to pdf etc) reports. However, things sometimes get a bit complicated and a normal problem that you face is the problem of columns containing null-values that you want to use for calculations. For this there is a nice function called COALESCE which replaces null with the value directed.
For example:
SELECT firstname, lastname, age FROM contacts;
Might return the list:
firstname lastname age
John Smith 5
Peter Jacks null
If you want to use the age-column to do some calculations or you just don't want it to be null, just change your SELECT-statement to:
SELECT firstname, lastname, COALESCE(age, 0) As NewAge, COALESCE(age, 0) * 2 As DoubleAge FROM contacts;
This will give you the result:
firstname lastname NewAge DoubleAge
John Smith 5 10
Peter Jacks 0 0
A useful and simple functions, to get you out of some nasty trouble when doing for instance LEFT OUTER JOIN, RIGHT OUTER JOIN or FULL OUTER JOIN when the risk of returning null is great.
Gustaf
Humandata AB
Saturday, September 01, 2007
Javascript – define functions and programmatically attach events
Since we're on the subject of javascript I might as well add some more stuff. First of all, how to define you own functions, then how to programmatically bind a function to an event, for instance, onchange of a field.
This mainly concerns Microsoft CRM but the technique can also be used in SharePoint, although javascripts aren't as common.
First off, how to define a function.
There are actually several different ways, for instance the normal:
function myfunc1(param1, param2)
{
alert("myfunc1");
}
And the one with a weird sort of syntax:
var myfunc2 = function(param1, param2)
{
alert("myfunc2");
}
In CRM 3, these can actually be defined within the onLoad, hence creating some ways of code reuse. These functions will be available for all event in the page since they have been declared in the onload.
This leads me to another point, that a function has to be defined before it can be used. Hence, in the example above, myfunc2 can call myfunc1, but not the other way around. If you try, you will get strange error messages saying stuff like "null is not 'null'".
Secondly, you can bind events to functions using code. There are several ways of doing this as well (what did you think!?) and they mainly differ based on which browser you are using. If you want to read some more on the subject, if you want some more info on event binding in javascript, please have a look at this very instructive page: http://developer.apple.com/internet/webcontent/eventmodels.html
First the simple method by just setting the onchange attribute.
crmForm.all.ext_myfield.onchange = myfunc2;
or
document.getElementById("objectid").onchange = myfunc2;
This has the advantage of a quite simple syntax and, if you want it, the override of previous events.
The second way of doing this is using the attachEvent method. As far as I've read, there are some differences with syntax between browsers, but since neither MS CRM nor SharePoint work in any acceptable way in anything but IE, this is not a big issue. Here is some example syntax:
crmForm.all.ext_myfield.attachEvent("onchange", myfunc2);
or
document.getElementById("objectid").attachEvent("onchange", myfunc2);
The main advantage of this method is that you can attach several functions to one event, hence, not overriding other handling.
Bellow is an example html-file which demonstrates this:
<html
xmlns="http://www.w3.org/1999/xhtml">
<head>
<script
type="text/javascript">
var myfunc1 = function()
{
alert("myfunc1");
}
var myfunc2 = function()
{
alert("myfunc2");
}
var loading = function()
{
alert("running");
document.getElementById("btTest").attachEvent("onclick", myfunc1);
document.getElementById("btTest").attachEvent("onclick", myfunc2);
}
</script>
</head>
<body
onload="loading();">
<button
id="btTest">Click me</button>
</body>
</html>
If you copy this to a file called test.htm and test it, you will see that when clicking the button, myfunc2 will be executed first and then myfunc1.
So, all put together, this can be used to a great extent in MS CRM 3, where you can now put all code in one place, the onLoad script, define some functions and then bind the onchange event of all the attributes you like in the same code. Remember to define the functions before you attach the events.
I havn't seen CRM 4 / Titan yet, and I don't know what features it will have in this aspect, but if it includes the feature to be able to add a global javascript file that will be included on all pages, this would be very very useful when using techniques like this.
And remember, javascript is an ugly beast, but it is very useful! J
Gustaf Westerlund
Humandata AB
Window.close() in javascript acts a bit strange…
Javascript is a strange language, it is often confused to be Java, which is very far from right, and the syntax can often be thought to be as consistent as C# or Java. Not the case. Javascript is a strange and weird beast, that when tamed can be a very strong ally. Taming it can however really get on your nerves!
There is one method working on the object window, called close(). Hence it is called using window.close(). It does the simple thing of closing the current browser window. No more, no less.
Well, actually, it does it in a strange way. Have a look at the following code that is executed in body onload. What is going to happen?
Alert("Start");
Windows.close();
Alert("End");
I will give you two options:
Option 1:
- A dialog window showing "Start" will show.
- The current browser window will close.
Option 2:
- A dialog window showing "Start" will show.
- A dialog window showing "End" will show.
- The current browser window will close.
Well?
You would think that option 1 is correct, well, it isn't, the script is executed according to option 2. For some reason, window.close() doesn't actually close the window, it signals it to close and when the onload has reached its end, it is closed. I havn't checked to see if this behavior only happens when executed in onload or if it is the same all over. Be aware though, it might not work as you'd expect!
Gustaf
How to add a virtual directory bellow your SharePoint or CRM website
Sometimes you would like to create a virtual directory bellow for instance the SharePoint main site. Lets say you MOSS is installed at http://intranet and you would like to create your own virtual directory with your custom made aspx-pages at for instance http://intranet/custom. Well, if you use the IIS Management Console, add a virtual directory to the website, and add an aspx-page to that virtual directory, you will get errors saying that the application cannot find certain assemblies. This is due to the fact that these assemblies have been referenced in web.config using <add assembly="name-of-assembly" / > tags. Due to the way IIS works, these references are automatically inherited to all sub-directories, in this case, you virtual directory.
So, what to do? The simplest way to handle this is to add <remove assembly="name-of-assembly" / > tags to the web.config that resides in your virtual directory. This will explicitly remove the reference created by the parent website. Remove them one by one and soon you will be able to run your code just as you would like.
One good advantage of using adding your code to a virtual directory bellow the main site instead of using a new website at for instance another host header or TCP port, is that you can address your code using server relative urls. In essence, if you created an aspx-page at http://intranet/custom/mycustompage.aspx. If you address this page from the SharePoint, you can use the url: /custom/mycustompage.aspx without http:// or the servername. This also gives you the benefit that the page is on the same server, hence no security problems with IE.
In SharePoint, make sure that the address of your virtual directory is NOT bellow any wildcard included och explicitly included sites (in the setup of the webapplication in central admin).
Please note that adding virtual directories bellow the CRM root is an unsupported customization. This is due to the fact that Microsoft might release an update that adds a directory with the exact name of you virtual directory. This would give rise to either the fact that your code stops working or more likely that the content of the directory added by Microsoft cannot be accessed.
Gustaf Westerlund
Humandata AB
Friday, August 31, 2007
Showing SQL Reporting Services reports in Microsoft Office SharePoint Server 2007-08-31
Today I was working with a customer who wanted to show their SSRS 2005 reports in SharePoint. As many of you might be aware, from SP 2 of SQL 2005 there is an option of storing you reports on the SharePoint Server. Very nice, but I havn't really tried getting that to work with MS CRM 3, so the SSRS I was using ran in normal mode, i.e. not SharePoint integration. However, I did want to show the reports and the reports explorer in SharePoint. For SharePoint v2 there has long existed RSWebparts.cab, which is a web part package consisting of two web parts; report viewer and report explorer.
After a bit of looking I couldn't find it for SharePoint v3, and found some information saying that the old webparts actually still worked well. So, after the always so tedious work of trying to locate the file, RSWebparts.cab (it cannot be downloaded from the Internet as far as I know) I installed it using stsadm –o installwp -o addwppack -force –filename "C:\temp\RSWebParts.cab" –globalinstall (use stsadm –help addwppack to see possible syntax), and adding the RSWebparts.dll as a safecontroll (with the correct strong name). I got it to work, just as nicely as it did in SharePoint v2. So, my suggestion to you, if you have the same need, use the old one.
And where can you find the file? It is installed in some impossible path bellow c:\program files\SQL Server of a SQL 2000 SP2 (or greater) server. So, get your hands on it, and don't let go. I don't think I can publish it here though, due to copyright reasons.
Gustaf
Thursday, August 30, 2007
Entity and attribute information
I was reading a bit on the CRM Teams blog and read a posting on the fact that there is an aspx-page that lists all entities that can also show all attributes of the entity. Read the post here or just brows to: http://<yourservername>/sdk/list.aspx.
You learn something new, every day J.
Gustaf Westerlund
Humandata AB
How to stop infinite recursions/loops in PostUpdate Callouts
As many of you who work with development in Microsoft CRM know, callouts is a great tool. Lots of different tasks can be left to be handled by callouts and they are even run when offline data is resynced to the main system. However, a common problem when working with PostUpdate callouts is that the code wants to update the object that created the callout, hence executing the calloutcode again, which might result in an endless recursion.
I recently ran into this problem working at a customer of mine, and found a nice generic way of solving it using the thought behind the design pattern object factory or single instance.
I guess you want some code! Well, the basis of this solution is to have a static instance of a collection that keeps track of if a certain key just has been run. I use the collection StringCollection. It is simple, and just what I needed in this case:
private
static System.Collections.Specialized.StringCollection justtreated;
Then I created a method that is supposed to return false the first, third, fifth… time it is called and true every second, fourth, sixth… time it is called with a certain key.
private
bool JustDone(string key)
{
if (justtreated == null)
{
justtreated = new System.Collections.Specialized.StringCollection();
}
if (justtreated.Contains(key))
{
justtreated.Remove(key);
return
true;
}
justtreated.Add(key);
return
false;
}
}
The method is not very complicated. First off, it makes sure that the static variable has been created. Then it checks if it can find the key string in the string collection. If it can it will remove it and then return true. If it cannot find the key, it will add it to the collection and return false.
Since, this method is working with a static variable that will remain in memory between instantiations of the class, hence the data will remain between PostUpdate calls.
Then in the beginning of the PostUpdate method just call JustDone() with the object key (or some other unique key) and if it is true, just "return".
Gustaf Westerlund
Humandata AB
Thursday, August 23, 2007
August 2007 MS CRM VPC is here!
We have all (?!) been waiting for the new MS CRM VPC and it has now been released. It can be downloaded from here:
http://www.microsoft.com/downloads/details.aspx?FamilyID=57394bc8-5ecc-422e-a066-34189f48f8c8&displaylang=en
I havn't downloaded it myself yet, but I will soon.
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Java, javascript, jscript and so forth
Please have a look if you are interested: http://blogs.msdn.com/gauravseth/archive/2007/08/15/the-world-of-jscript-javascript-ecmascript.aspx
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Wednesday, August 22, 2007
Javascript event handling and dynamic lists in SharePoint
This is a quite common task in MS CRM where javascript is a common tool. In SharePoint however, javascript isn't as common. I am currently working with a colleague of mine, Sebastian Tegel, at a large swedish customer of ours. They had this requirement on some metadata in a SharePoint library so Sebastian and I used the same principles as I usually use in MS CRM to create dynamic drop-down lists in SharePoint. Sebastian has a very nice and instructive posting on his blog on how this is done. Please have a look at it: http://sebastiant.blog.com/2023682/
In the posting before that, he also describes how to create your own custom edit form for a document properties page. This is very useful in these cases, so please have a look at that as well. (http://sebastiant.blog.com//custom+forms/).
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se
Tuesday, August 21, 2007
Not so simple SharePoint and CRM integration
A customer of mine wanted to integrate their MOSS Ent with CRM 3. Specifically they wanted to be able to handle documents in CRM with SharePoint, not surprising since the document handling in MS CRM 3 is bad, if you are a bit positive. They were running 2 servers, one with CRM and one with MOSS Ent. Both with their own SQL 2005 installations (to avoid problems with SPN:s and such).
The solution? I based the solution on my previous posting from October 2006 where I described how to do this; Simple SharePoint and CRM integration (http://gustafwesterlund.blogspot.com/2006/10/simple-crm-and-sharepoint-integration.html). That was WSS 2 and both applications were running on the same server. So, not really the same setup, but, I felt, it shouldn't be a problem.
My plan was to create a SharePoint site using a specific custom template on the Account PostCreate callout. The callout would set the websiteurl attribute and an onLoad javascript would show a custom aspx-page that shows a documents webpart using the minimal.master (in order to hide all the SharePoint specific framing).
I started off by creating the PostCreate callout. Not a problem, just create a normal class file with the following content:
using System;
using Microsoft.Crm.Callout;
using Callouts.CRMSDK;
namespace Callouts
{
///
<summary>
/// Summary description for Class1.
///
</summary>
public
class Account: CrmCalloutBase
{
public Account()
{
}
public
override
void PostCreate(CalloutUserContext userContext, CalloutEntityContext entityContext, string postImageEntityXml)
{
CrmService service = new CrmService();
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
service.CallerIdValue = new CallerId();
service.CallerIdValue.CallerGuid = userContext.UserId;
systemuser user = (systemuser)service.Retrieve(EntityName.systemuser.ToString(), userContext.UserId, new AllColumns());
account acc = (account)service.Retrieve(EntityName.account.ToString(), entityContext.InstanceId, new AllColumns());
// Code to create sharepoint site/subweb and set acc.websiteurl to the url.
service.Update(acc);
}
}
}
And add a web reference to the CRM webservice (look in the SDK to find the exact URL). As you probably can see, I called it CRMSDK (strange name actually, but it´s tradition).
And the callout.config.xml looked like this:
<callout.config
version="3.0"
xmlns="http://schemas.microsoft.com/crm/2006/callout/">
<callout
entity="account"
event="PostCreate">
<subscription
assembly="Callouts.dll"
class="Callouts.Account" />
</callout>
</callout.config>
Well, I modified the code and set some attribute of the account to something just to make sure it worked. It did.
How to create a site in SharePoint from one server? Well, first I tried adding Microsoft.SharePoint.dll which contains lots of nice stuff to access the SharePoint data. This was the technique I had used previously and thought it might be good this time too. But no, it can actually only be used on the same server as SharePoint and not on some other server.
There are still a lot of nice web services in SharePoint, I could probably find some web method that would let me create my site. I started by looking in the WssSDK…
But I could find anything useful. The natural webservice would of course be webs.asmx and it contains lot of nice functions to handle SharePoint webs, like GetWeb() and so on. But I found no way of creating a new site. In the admin.asmx I found a method called createSite() but that creates sitecollections which is not really what I wanted. (Each sitecollection has unique security handling, template and sitedefinition setup and a lot of other specific stuff) My customer has around 5000 customer and I would want to create 5000 sitecollections.
I started searching the Internet. But, no. All I could find was a blog entry by Nick Swan describing how to use admin.createSite() (http://weblog.vb-tech.com/nick/archive/2006/03/07/1472.aspx).
I contacted Pontus Haglund and Kalle Becker at Microsoft and my colleague Sebastian Tegel and asked them if they knew how to do this or could assist in any way. They didn't unfortunately.
I was left with only one major option as I saw it; creating my own web service that used Microsoft.SharePoint.dll functions to create the web site (subweb). I had found a blog that described how to do this (http://blogs.ittoolbox.com/km/sharepoint/archives/creating-a-custom-web-service-for-sharepoint-13553). So, I thought that it shouldn't be that big of a hazel. To make sure my SharePointcode worked, I first created a normal windows forms application that created a site according to certain variables. I found that the following code worked very well:
string url = "";
SPSecurity.RunWithElevatedPrivileges(delegate()
{
SPSite siteCollection = new SPSite("http://intranet");
SPWebCollection subSites = siteCollection.AllWebs["customersites"].Webs;
SPWebTemplateCollection templates = siteCollection.GetCustomWebTemplates(1033);
SPWebTemplate template = templates[0];
SPWeb mySite = subSites.Add(name, Title, Description, 1033, template, false, false);
mySite.Update();
url = mySite.Url;
});
return url;
After a suggestion from my colleague Sebastian Tegel, I ran all the code with Elevated Privileges. Not sure it is actually needed but I added it to make sure that there isn't any security issues with the current user and permissions to create sites.
Now, I just had to follow the blog I had found. Again my misfortune struck me, as I was unable to make any sense of it. I got the web service to work as long as I didn't use any SharePoint functions. When I did, I got the following error:
System.IO.FileNotFoundException: The Web application at http://intranet could not be found. Verify that you have typed the URL correctly. If the URL should be serving existing content, the system administrator may need to add a new request URL mapping to the intended application.
at Microsoft.SharePoint.SPSite..ctor(SPFarm farm, Uri requestUri, Boolean contextSite, SPUserToken userToken)
.
.
.
I was using the exact same code as I had tested in the forms test application so there wasn't any problems there.
The blog post mentions a lot about strong naming assemblies but it doesn't reference it with the strong name and my experience told me that this error probably was due to Code Access Security (CAS) problems. The assembly should be strong-named, placed in the GAC and referenced using the strong name.
I think some important pictures has fallen out of the blog why it doesn't make much sense. It did however mention that there was a walk-through on msdn that described the same thing. This was my next lead on the way to my goal of SharePoint document handling In MS CRM 3.
I found the walk-through (http://msdn2.microsoft.com/en-us/library/ms464040.aspx) and followed it minutely. It gave me a lot better feeling than the first blog posting I had found. In short, it described how to create a web service asmx-file that references a strong named assembly with a class of web methods in it. When created, the program disco.exe was used to create wsdl and disco files for the web service. When these had been created, the were renamed to become aspx files so that they can dynamically link to the webservice depending on where in SharePoint it has been referenced. (all sharepoint webservices can be found in the sub directory of /_vti_bin/ of any site, for instance the web service webs.asmx can be found in both the url http://intranet/_vti_bin/webs.asmx and http://intranet/customersites/webs.asmx). When the wsdl and disco files have been properly modified to fit the SharePoint standard of handling this feature, the new web service has to be added to the file spdisco.aspx which is a directory of all web services that are available.
After a lot of hazel, I got this to work, and with a strong named assembly, deployed in the GAC and run in the SharePoint context. I finally got it to work the way I wanted it to.
So, if you also want your own web service in SharePoint. I would suggest following the walk-through very thoroughly and I have a few pointers. My web service was called WebsExtra.asmx, my class was called Service, and the assembly "WSTools.dll".
- First, make sure the asmx-file works. Develop it using a normal web application. You should be able to address it. I had created a new web site using TCP port 8000 and hence tried: http://intranet:8000/WebsExtra.asmx. If that doesn't work, make sure you have strong named your assembly correctly, deployed it the the GAC (i.e. copied it to c:\windows\assembly) and addressed the class using the full strong name ("Service, WSTools, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=037c5a828198ba1e" in my case).
- Ok. That works. Good. Make sure the file also works when you placed it in the _layouts directory according to the walk-through. If not, you might still not be addressing the class with the full strong name.
- After I created the files WebsExtra.wsdl and WebsExtra.disco, copied them to the ISAPI directory, renamed them to WebsExtrawsdl.aspx and WebsExtradisco.aspx, and modified them to be able to handle the dynamic linking that SharePoint uses, I made sure that I could really see the aspx-files using IE. I found them at http://intranet/_vti_bin/WebsExtrawsdl.aspx and http://intranet/_vti_bin/WebsExtradisco.aspx .
Also check the contents of them and make sure they follow the same structure as similar files in the ISAPI directory (for instance Webswsdl.aspx and Websdisco.aspx, which you also can find using IE). Make sure that the referencing to the web service really uses the name you have set and not the name used in the walk-through.At the bottom of this entry is a copy of the files I used and a copy of the result I got in IE. - When both these files work, make the additions to the bottom of spdisco.aspx as well (within the discovery-tag though). This file can also be found using IE. You can probably guess it's url. Have a look at my version at the bottom of this entry, including the result in IE.
- When I got that working, I now tried to access the addresses:
http://intranet/_vti_bin/WebsExtra.asmx?wsdl and http://intranet/_vti_bin/WebsExtra.disco. These aren't actually files that you can find in the ISAPI directory but are mapped to the aspx-files mentioned in 3 above. You can check this by checking one of the out-of-the-box web services like Webs.asmx. - When you've got all these working properly, you should be able to add the web service as a web reference in some test project. (I usually use Windows Forms programs since they are the easiest to debug). If all has been set up properly, you should now be able to add the web service as a web refence. If the dialog gives you problems, it is usually due to errors in the wsdl or disco files. I had lots of these and was quite close to suicide but finally found them all. Most of the problems I had were due to the fact that I had used Visual Studio to edit the wsdl and disco aspx files. VS 2005 had been very "helpful" and had added ="" here and there, making the syntax erroneous. So, my suggestion, use notepad to copy paste the code from the walk-through. When that is done, you can use VS 2005 to modify the code.
WebsExtrawsdl.aspx
<%@
Page
Language="C#"
Inherits="System.Web.UI.Page"
%>
<%@
Assembly
Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%>
<%@
Import
Namespace="Microsoft.SharePoint.Utilities"
%>
<%@
Import
Namespace="Microsoft.SharePoint"
%>
<% Response.ContentType = "text/xml"; %>
<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:tns="http://tempuri.org/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
targetNamespace="http://tempuri.org/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s:schema
elementFormDefault="qualified"
targetNamespace="http://tempuri.org/">
<s:element
name="CreateSharePointSubWeb">
<s:complexType>
<s:sequence>
<s:element
minOccurs="0"
maxOccurs="1"
name="portalUrl"
type="s:string"
/>
<s:element
minOccurs="0"
maxOccurs="1"
name="subsite"
type="s:string"
/>
<s:element
minOccurs="0"
maxOccurs="1"
name="name"
type="s:string"
/>
<s:element
minOccurs="0"
maxOccurs="1"
name="Title"
type="s:string"
/>
<s:element
minOccurs="0"
maxOccurs="1"
name="Description"
type="s:string"
/>
<s:element
minOccurs="1"
maxOccurs="1"
name="templateId"
type="s:int"
/>
</s:sequence>
</s:complexType>
</s:element>
<s:element
name="CreateSharePointSubWebResponse">
<s:complexType>
<s:sequence>
<s:element
minOccurs="0"
maxOccurs="1"
name="CreateSharePointSubWebResult"
type="s:string"
/>
</s:sequence>
</s:complexType>
</s:element>
<s:element
name="HelloWorld">
<s:complexType
/>
</s:element>
<s:element
name="HelloWorldResponse">
<s:complexType>
<s:sequence>
<s:element
minOccurs="0"
maxOccurs="1"
name="HelloWorldResult"
type="s:string"
/>
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
<wsdl:message
name="CreateSharePointSubWebSoapIn">
<wsdl:part
name="parameters"
element="tns:CreateSharePointSubWeb"
/>
</wsdl:message>
<wsdl:message
name="CreateSharePointSubWebSoapOut">
<wsdl:part
name="parameters"
element="tns:CreateSharePointSubWebResponse"
/>
</wsdl:message>
<wsdl:message
name="HelloWorldSoapIn">
<wsdl:part
name="parameters"
element="tns:HelloWorld"
/>
</wsdl:message>
<wsdl:message
name="HelloWorldSoapOut">
<wsdl:part
name="parameters"
element="tns:HelloWorldResponse"
/>
</wsdl:message>
<wsdl:portType
name="ServiceSoap">
<wsdl:operation
name="CreateSharePointSubWeb">
<wsdl:input
message="tns:CreateSharePointSubWebSoapIn"
/>
<wsdl:output
message="tns:CreateSharePointSubWebSoapOut"
/>
</wsdl:operation>
<wsdl:operation
name="HelloWorld">
<wsdl:input
message="tns:HelloWorldSoapIn"
/>
<wsdl:output
message="tns:HelloWorldSoapOut"
/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding
name="ServiceSoap"
type="tns:ServiceSoap">
<soap:binding
transport="http://schemas.xmlsoap.org/soap/http"
/>
<wsdl:operation
name="CreateSharePointSubWeb">
<soap:operation
soapAction="http://tempuri.org/CreateSharePointSubWeb"
style="document"
/>
<wsdl:input>
<soap:body
use="literal"
/>
</wsdl:input>
<wsdl:output>
<soap:body
use="literal"
/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation
name="HelloWorld">
<soap:operation
soapAction="http://tempuri.org/HelloWorld"
style="document"
/>
<wsdl:input>
<soap:body
use="literal"
/>
</wsdl:input>
<wsdl:output>
<soap:body
use="literal"
/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding
name="ServiceSoap12"
type="tns:ServiceSoap">
<soap12:binding
transport="http://schemas.xmlsoap.org/soap/http"
/>
<wsdl:operation
name="CreateSharePointSubWeb">
<soap12:operation
soapAction="http://tempuri.org/CreateSharePointSubWeb"
style="document"
/>
<wsdl:input>
<soap12:body
use="literal"
/>
</wsdl:input>
<wsdl:output>
<soap12:body
use="literal"
/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation
name="HelloWorld">
<soap12:operation
soapAction="http://tempuri.org/HelloWorld"
style="document"
/>
<wsdl:input>
<soap12:body
use="literal"
/>
</wsdl:input>
<wsdl:output>
<soap12:body
use="literal"
/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service
name="Service">
<wsdl:port
name="ServiceSoap"
binding="tns:ServiceSoap">
<soap:address
location=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>
/>
</wsdl:port>
<wsdl:port
name="ServiceSoap12"
binding="tns:ServiceSoap12">
<soap12:address
location=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>
/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
When accessed at http://intranet/_vti_bin/WebsExtrawsdl.aspx or http://intranet/_vti_bin/WebsExtra.asmx?wsdl the following was the result:
<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:tns="http://tempuri.org/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
targetNamespace="http://tempuri.org/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s:schema
elementFormDefault="qualified"
targetNamespace="http://tempuri.org/">
<s:element
name="CreateSharePointSubWeb">
<s:complexType>
<s:sequence>
<s:element
minOccurs="0"
maxOccurs="1"
name="portalUrl"
type="s:string" />
<s:element
minOccurs="0"
maxOccurs="1"
name="subsite"
type="s:string" />
<s:element
minOccurs="0"
maxOccurs="1"
name="name"
type="s:string" />
<s:element
minOccurs="0"
maxOccurs="1"
name="Title"
type="s:string" />
<s:element
minOccurs="0"
maxOccurs="1"
name="Description"
type="s:string" />
<s:element
minOccurs="1"
maxOccurs="1"
name="templateId"
type="s:int" />
</s:sequence>
</s:complexType>
</s:element>
<s:element
name="CreateSharePointSubWebResponse">
<s:complexType>
<s:sequence>
<s:element
minOccurs="0"
maxOccurs="1"
name="CreateSharePointSubWebResult"
type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element
name="HelloWorld">
<s:complexType />
</s:element>
<s:element
name="HelloWorldResponse">
<s:complexType>
<s:sequence>
<s:element
minOccurs="0"
maxOccurs="1"
name="HelloWorldResult"
type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
<wsdl:message
name="CreateSharePointSubWebSoapIn">
<wsdl:part
name="parameters"
element="tns:CreateSharePointSubWeb" />
</wsdl:message>
<wsdl:message
name="CreateSharePointSubWebSoapOut">
<wsdl:part
name="parameters"
element="tns:CreateSharePointSubWebResponse" />
</wsdl:message>
<wsdl:message
name="HelloWorldSoapIn">
<wsdl:part
name="parameters"
element="tns:HelloWorld" />
</wsdl:message>
<wsdl:message
name="HelloWorldSoapOut">
<wsdl:part
name="parameters"
element="tns:HelloWorldResponse" />
</wsdl:message>
<wsdl:portType
name="ServiceSoap">
<wsdl:operation
name="CreateSharePointSubWeb">
<wsdl:input
message="tns:CreateSharePointSubWebSoapIn" />
<wsdl:output
message="tns:CreateSharePointSubWebSoapOut" />
</wsdl:operation>
<wsdl:operation
name="HelloWorld">
<wsdl:input
message="tns:HelloWorldSoapIn" />
<wsdl:output
message="tns:HelloWorldSoapOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding
name="ServiceSoap"
type="tns:ServiceSoap">
<soap:binding
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation
name="CreateSharePointSubWeb">
<soap:operation
soapAction="http://tempuri.org/CreateSharePointSubWeb"
style="document" />
<wsdl:input>
<soap:body
use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body
use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation
name="HelloWorld">
<soap:operation
soapAction="http://tempuri.org/HelloWorld"
style="document" />
<wsdl:input>
<soap:body
use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body
use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding
name="ServiceSoap12"
type="tns:ServiceSoap">
<soap12:binding
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation
name="CreateSharePointSubWeb">
<soap12:operation
soapAction="http://tempuri.org/CreateSharePointSubWeb"
style="document" />
<wsdl:input>
<soap12:body
use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body
use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation
name="HelloWorld">
<soap12:operation
soapAction="http://tempuri.org/HelloWorld"
style="document" />
<wsdl:input>
<soap12:body
use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body
use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service
name="Service">
<wsdl:port
name="ServiceSoap"
binding="tns:ServiceSoap">
<soap:address
location="http://intranet/_vti_bin/WebsExtrawsdl.aspx" />
</wsdl:port>
<wsdl:port
name="ServiceSoap12"
binding="tns:ServiceSoap12">
<soap12:address
location="http://intranet/_vti_bin/WebsExtrawsdl.aspx" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
And the file WebsExtradisco.aspx:
<%@
Page
Language="C#"
Inherits="System.Web.UI.Page"
%>
<%@
Assembly
Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%>
<%@
Import
Namespace="Microsoft.SharePoint.Utilities"
%>
<%@
Import
Namespace="Microsoft.SharePoint"
%>
<% Response.ContentType = "text/xml"; %>
<discovery
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/disco/">
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request) + "?wsdl"),Response.Output); %>
docref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<soap
address=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>
xmlns:q1="http://tempuri.org/"
binding="q1:ServiceSoap"
xmlns="http://schemas.xmlsoap.org/disco/soap/"
/>
<soap
address=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>
xmlns:q2="http://tempuri.org/"
binding="q2:ServiceSoap12"
xmlns="http://schemas.xmlsoap.org/disco/soap/"
/>
</discovery>
And when I browsed the pages http://intranet/_vti_bin/WebsExtradisco.aspx or http://intranet/_vti_bin/WebsExtra.disco was:
<discovery
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/disco/">
<contractRef
ref="http://intranet/_vti_bin/WebsExtradisco.aspx?wsdl"
docref="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<soap
address="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns:q1="http://tempuri.org/"
binding="q1:ServiceSoap"
xmlns="http://schemas.xmlsoap.org/disco/soap/" />
<soap
address="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns:q2="http://tempuri.org/"
binding="q2:ServiceSoap12"
xmlns="http://schemas.xmlsoap.org/disco/soap/" />
</discovery>
Of course, to make the address translation from http://intranet/_vti_bin/WebsExtra.disco actually point to the correct file http://intranet/_vti_bin/WebsExtradisco.aspx and similary http://intranet/_vti_bin/WebsExtra.asmx?wsdl point to http://intranet/_vti_bin/WebsExtrawsdl.aspx the correct setting have to be added to the file spdisco.aspx. This is the version I had (Notice the two last tags before the if-statement):
<%@
Page
Language="C#"
Inherits="System.Web.UI.Page"
%>
<%@
Assembly
Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%>
<%@
Import
Namespace="Microsoft.SharePoint.Utilities"
%>
<%@
Import
Namespace="Microsoft.SharePoint"
%>
<%@
Import
Namespace="Microsoft.SharePoint.WebControls"
%>
<%@
Import
Namespace="Microsoft.SharePoint.Administration"
%>
<% SPSite spServer = SPControl.GetContextSite(Context); SPWeb spWeb = SPControl.GetContextWeb(Context); %>
<% Response.ContentType = "text/xml"; %>
<discovery
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.xmlsoap.org/disco/">
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/alerts.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/alerts.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/alerts.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/Authentication.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/Authentication.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/Authentication.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/copy.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/copy.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/copy.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dspsts.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dspsts.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dspsts.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dws.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dws.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dws.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/forms.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/forms.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/forms.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/imaging.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/imaging.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/imaging.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/lists.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/lists.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/lists.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/meetings.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/meetings.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/meetings.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/People.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/People.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/People.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/permissions.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/permissions.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/permissions.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SharepointEmailWS.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SharepointEmailWS.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SharepointEmailWS.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SiteData.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SiteData.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SiteData.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/sites.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/sites.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/sites.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/spsearch.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/spsearch.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/spsearch.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/UserGroup.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/UserGroup.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/UserGroup.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/versions.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/versions.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/versions.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/views.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/views.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/views.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/WebPartPages.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/WebPartPages.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/WebPartPages.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/webs.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/webs.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/webs.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<contractRef
ref=<% SPEncode.WriteHtmlEncodeWithQuote(Response, spWeb.Url + "/_vti_bin/WebsExtra.asmx?wsdl", '"'); %>
docRef=<% SPEncode.WriteHtmlEncodeWithQuote(Response, spWeb.Url + "/_vti_bin/WebsExtra.asmx", '"');%>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<soap
address=<% SPEncode.WriteHtmlEncodeWithQuote(Response, spWeb.Url + "/_vti_bin/WebsExtra.asmx", '"'); %>
xmlns:q1="http://schemas.microsoft.com/sharepoint/soap/directory/"
binding="q1:ServiceSoap"
xmlns="http://schemas.xmlsoap.org/disco/soap/"
/>
<%
if (spServer.WebApplication is SPAdministrationWebApplication)
{
%>
<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_adm/Admin.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_adm/Admin.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>
<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_adm/Admin.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>
<% } %>
</discovery>
And when browsing to http://intranet/_vti_bin/spdisco.aspx I got the following result:
<discovery
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/disco/">
<contractRef
ref="http://intranet/_vti_bin/WebsExtradisco.aspx?wsdl"
docref="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<soap
address="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns:q1="http://tempuri.org/"
binding="q1:ServiceSoap"
xmlns="http://schemas.xmlsoap.org/disco/soap/" />
<soap
address="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns:q2="http://tempuri.org/"
binding="q2:ServiceSoap12"
xmlns="http://schemas.xmlsoap.org/disco/soap/" />
</discovery>
<discovery
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.xmlsoap.org/disco/">
<contractRef
ref="http://intranet/_vti_bin/alerts.asmx?wsdl"
docRef="http://intranet/_vti_bin/alerts.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/alerts.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/Authentication.asmx?wsdl"
docRef="http://intranet/_vti_bin/Authentication.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/Authentication.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/copy.asmx?wsdl"
docRef="http://intranet/_vti_bin/copy.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/copy.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/dspsts.asmx?wsdl"
docRef="http://intranet/_vti_bin/dspsts.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/dspsts.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/dws.asmx?wsdl"
docRef="http://intranet/_vti_bin/dws.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/dws.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/forms.asmx?wsdl"
docRef="http://intranet/_vti_bin/forms.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/forms.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/imaging.asmx?wsdl"
docRef="http://intranet/_vti_bin/imaging.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/imaging.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/lists.asmx?wsdl"
docRef="http://intranet/_vti_bin/lists.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/lists.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/meetings.asmx?wsdl"
docRef="http://intranet/_vti_bin/meetings.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/meetings.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/People.asmx?wsdl"
docRef="http://intranet/_vti_bin/People.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/People.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/permissions.asmx?wsdl"
docRef="http://intranet/_vti_bin/permissions.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/permissions.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/SharepointEmailWS.asmx?wsdl"
docRef="http://intranet/_vti_bin/SharepointEmailWS.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/SharepointEmailWS.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/SiteData.asmx?wsdl"
docRef="http://intranet/_vti_bin/SiteData.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/SiteData.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/sites.asmx?wsdl"
docRef="http://intranet/_vti_bin/sites.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/sites.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/spsearch.asmx?wsdl"
docRef="http://intranet/_vti_bin/spsearch.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/spsearch.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/UserGroup.asmx?wsdl"
docRef="http://intranet/_vti_bin/UserGroup.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/UserGroup.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/versions.asmx?wsdl"
docRef="http://intranet/_vti_bin/versions.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/versions.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/views.asmx?wsdl"
docRef="http://intranet/_vti_bin/views.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/views.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/WebPartPages.asmx?wsdl"
docRef="http://intranet/_vti_bin/WebPartPages.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/WebPartPages.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/webs.asmx?wsdl"
docRef="http://intranet/_vti_bin/webs.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<discoveryRef
ref="http://intranet/_vti_bin/webs.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />
<contractRef
ref="http://intranet/_vti_bin/WebsExtra.asmx?wsdl"
docRef="http://intranet/_vti_bin/WebsExtra.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<soap
address="http://intranet/_vti_bin/WebsExtra.asmx"
xmlns:q1="http://schemas.microsoft.com/sharepoint/soap/directory/"
binding="q1:ServiceSoap"
xmlns="http://schemas.xmlsoap.org/disco/soap/" />
</discovery>
So, with these example files, and these tips, I hope I can ease your troubles in making you own custom sharepoint web service.
Gustaf Westerlund
CRM and SharePoint Consultant
Humandata AB
www.humandata.se