Monday, 30 December 2013

Actions (Custom Messages) in MS Dynamics CRM 2013

Actions are another new feature in MS Dynamics CRM 2013. They are an intriguing new feature, which I must admit don't see much of a use case for right now.

In essence, they are a custom message, something like Create, Update, etc... but the actions that take place when the custom message is invoked are defined as a sort of Workflow, which allows custom code via Custom Workflow activities.

It then it gets a bit weird in that this can only be invoked via code. A potential workaround is to use a workflows with a custom Workflow activity or presumably, JavaScript hooked up to a button or other event.

I thought I would try to create a sample action. The action will create a task related to a Stamp (a custom entity), which is a mandatory input to the request. There is another non-mandatory input variable, if set to false, the action will not trigger. Finally, there will be an output variable, which will effectively confirm whether it's been actioned ...

At any rate, this is how one would create such an action. Navigate to Settings -> Processes -> New

I created this action to be global, it can be limited to an entity.
I added a few arguments and there are a few things that are annoying about this. The first one is that unless the Entity Type is set for Entity or EntityReference, you will not be able to use the argument, at least not from the GUI, so this limits a bit what can be done. Although I suspect no such limitations apply to code ... The second annoying thing is best explained by the next screenshot:
Essentially, 0 will be false, everything else is True, if the field is boolean, not sure why not just have True or False?



Make sure that the action is activated before calling it from code.

This is how you would call it:
private static void supadupaaction(IOrganizationService service, EntityReference target, Entity stamp, bool carryOut=true)
        {
            var parameters = new ParameterCollection();
            parameters.Add(new KeyValuePair<string, object>("Target", target));
            parameters.Add(new KeyValuePair<string, object>("RealTarget", stamp));
            parameters.Add(new KeyValuePair<string, object>("AreYouSure", carryOut));

            OrganizationRequest request = new OrganizationRequest()
            {
                RequestName = "new_SupaDupaAction",
                Parameters = parameters
            };

            var result = (OrganizationResponse)service.Execute(request);

            foreach (var item in result.Results)
            {
                Console.WriteLine("Result - Name: {0}. Value: {1}", item.Key, item.Value);
            }
         }
Target is an entity reference to the, yep, target entity, i.e. the entity against which the action/custom message is running. Stamp is the instance of the Stamp entity against which the task will be created.

Note that the RequestName is not all lower case, not sure if this is only for actions or a more generic shift in MS Dynamics CRM 2013 in general.

Frankly, I need to think what the benefit of this is/can be, because with this noddy example I am seeing none. I guess defining a type of operation can be quite handy as stuff can happen pre and post operation, e.g. register plug-ins to do this, so nothing ground breaking but perhaps simplifies things ...

Feel free to enlighten me...

Friday, 27 December 2013

Business Rules in MS Dynamics CRM 2013

I finally gave MS Dynamics CRM 2013 a whirl last night and found an interesting new feature: Business Rules.

Not sure what the exact definition is, but the idea is that these rules can be used to modify the form without the need to resorting to JavaScript. This is not only good because it means that non-developers can do it but also because it means no more JavaScript !!!!!, well maybe.

This is one of the myriad of features in MS Dynamics CRM that are always mentioned as non-developer feature, but in reality it's always the developers that work with these, but I digress.

Business rules seem to be entity wide, which is a bit of a shame as it would be handy to have business rules that apply to various entities, not entirely sure I see a reasonable business case, but, generally, the fewer limitations the better.

At any rate, I created a new Entity, added a few fields and edited the form, from where I added a Business rule.

The rule will make the Class field mandatory if the Cost field is greater than £10, for instance if this were an expense you could make a explanation/justification field become mandatory when a certain threshold was exceeded.


The conditions, as well as actions, can be chained, e.g. in the invoice example, make the explanation/justification field become mandatory if it's above a certain threshold AND it's a certain type of expense only. 


Not 100% what the description is supposed to do, as it does not appear to be displayed anywhere, probably it's just another field that will never be filled in.

An immediate downside is that in this example, you would need another business rule to set it back to not required if the field is changed to a value below £10.

Anyway, here's the result:


Another downside is the lack of OR on the rules. Is it really that complicated for non technical staff to understand the or operation? or is it complex for Microsoft to implement? I don't know but there is an or feature in Advanced Find. At any rate, the lack of or in the Check Condition Actions was really annoying on Dialogs and Workflows in 2011 and this seems to be still missing in 2013.

It's of interest to note that for numeric fields, simple formulas can be used instead of fixed values, no regex for string fields though :(, again not surprising given the audience, but it would've been nice.

Also the comparison can be made against another field in the entity.

The range of actions is interesting:
  • Show Error Message - Yahoo!!! no more javascript validation (in your dreams, sunshine)
  • Set Field Value 
  • Set Business Required
  • Set Visibility
  • Lock or Unlock Field
I guess this feature is a typical MS Dynamics CRM feature, where it seems like a great idea in principle but then in practice it's too limited or cumbersome but for the most basic of tasks. Designed for Power Users, used and hated by developers, more of the same.

All in all not a bad feature, I just need to start working on some MS Dynamics CRM 2013 projects now :).

Thursday, 26 December 2013

Add/Remove Programs Product Icon with Wix

This is a very simple change that can help your application look that little bit more professional.

Just add a Add/Remove Programs Product Icon with Wix:

<Icon Id="ProductIcon" SourceFile="Icon\myicon.ico"/>    
<Property Id="ARPPRODUCTICON" Value="ProductIcon" />

See What I mean:


You need to make sure that you store myicon.ico on a directory called Icon.

Friday, 20 December 2013

Get user group membership in Sharepoint from Powershell

A few days back I needed to find out whether the System account (SharePoint\System) was a member of a particular group and it turns out that this cannot be done from the GUI as it doesn't show up in the list of accounts that are members of that group so ... PowerShell to the rescue.

The following snippet will provide a list of users belonging to the desired group <Group Name>:
$site = Get-SPSite -WebApplication "<url>"
$mainsite = (Get-SPWeb -Site $site)[0]
$group = $mainsite.SiteGroups | ?{$_.Name -match "<Group Name>"}
foreach($user in $group.Users){$user}
As usual it needs to be run from the SharePoint PowerShell console or the Sharepoint PowerShell snap in needs to be loaded before running the above commands.

Monday, 25 November 2013

TFS Error - There is not enough space on the disk

I've been working on trying to automate our development workflow or in other words I've been banging my head against the TFS wall trying to set up CI.

I thought i had everything working when I encountered this little beauty for one of the ASP.NET projects:

The build server had over 20 GB of free space, so I was somewhat baffled by this error message, so I did what every self respecting IT professional does: I started bouncing stuff.

After I bounced, the build agent, build service and the server itself, all to no avail, I hit the interwebs where I found the solution.

This error has got nothing to do with disk space, it's related to tempory files not being deleted in this folder:
C:\Windows\Microsoft.net\Framework64\<version>\Temporary ASP.NET Files
I checked the folder and sure enough there was a subfolder with the name of one of the projects. I deleted this and the builds started to complete again.

Wednesday, 20 November 2013

ADFS - Turning Debug Tracing on

I was struggling last week with some ADFS issues and I decided to turn debug tracing on to see if it threw any light as to why it wasn't working, it didn't, the issue was somewhere else, but it might be useful for future reference, so here it goes:

1. Start the event viewer:
Start | Run | eventvwr
2. Disable ADFS event log:
3. Run the following command with elevated permissions (i.e. Run as Administrator):
wevtutil sl "AD FS 2.0 Tracing/Debug" /l:5
4. Enable ADFS event log.
5. Ensure that Analytic and Debug logs are enabled
  View | Show Analytic and Debug logs
6. Enjoy all the debug output goodness.

Friday, 15 November 2013

Exit codes in msi installers created with Wix

There was (is?) a bug in Wix that prevented successful creation of an SSL binding in IIS 7.5, so in order to get around this issue, I wrote a custom action to do this.

A failure in this custom action will not stop the installation*, which means that the exit code will be driven by the custom action failure, which will lead to the exit code being 1603 but the product installing, since the failure is relating to setting the certificate for a webpage, this actually has no effect as the newly installed website picks up the old certificate.

The problem was in our install scripts, where we check on the exit code to ascertain whether the installation was successful, which it wasn't as the exit code was 1603 but since the app was actually there this lead to a lot of confusion and head scratching.

Thought I'd share in case somebody does something as a stupid as I did.

*I've edited the custom action code so that it does not always return success now. There was a perfectly valid reason for the code to always return a success value and I will talk about it as soon as I find it.

Selected output from the install log file:

CustomAction UpdateBinding returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
UpdateBinding. Return value 3.
INSTALL. Return value 3.

Product:  Portal -- Installation failed.
Windows Installer installed the product. Product Name: Portal. Product Version: 1.3.3.7. Product Language: 1033. Manufacturer: Yo! Ltd. Installation success or error status: 1603.

Wednesday, 30 October 2013

Delete documents from SharePoint document library using PowerShell

So today we had to delete a bunch of documents from SharePoint, so I wrote this script to accomplish this task.

A couple of notes about the script:

  1. By default it will not delete anything, just list the urls that contain the $document argument.
  2. The $document argument is matched with like as it's matching urls, so be careful that it doesn't delete more documents than you want.


param
(
[string]$url = "https://sp.dev.local",
[string]$document = "Welcome"
[bool]$delete = false
)

$site = Get-SPSite $url

foreach($web in $site.AllWebs) {
    foreach($list in $web.Lists) {
              if($list.BaseType -eq "DocumentLibrary") {
                     foreach($item in $list.Items){
                           if($item.url -like "*$document*")
                           {
                                  if($delete)
                                  {
                                   Write-Host "Deleting $item.url"
                                   $item.File.Delete()
                                  }
                                  else
                                  {
                                   Write-Host "$item.url"
                                  }
                         }               
                      }
              }
       }
}

 

Friday, 25 October 2013

Issues with TurnOffFetchThrottling in MS Dynamics CRM 2011 - FetchXml Record Limit

I think i found a bug in MS Dynamics CRM 2011 today. We have a few UR 12 installed on our servers and for various reasons too long too explain, we had Turned off Fetch throttling.

This is done by adding a registry key to HKEY_LOCAL_MACHINE\Software\Microsoft\MSCRM called TurnOffFetchThrottling and setting the value to 1. This needs to be a DWORD type.

If you do this, then this FetchXml query will not work:
<fetch mapping="logical" count="2147483647" version="1.0"> 
 <entity name="account">
  <attribute name="name" />
 </entity>
</fetch>
It seems that by default MS Dynamics CRM 2011 will add one to the number of results that one is trying to get. So with Fetch Throttling on, i.e. the default, the above query will be parsed to the database like this:
select top 5001 "account0".Name as "name" , "account0".AccountId as "accountid" from AccountBase as "account0" order by "account0".AccountId asc
But if, FetchThrottling is off, when we pass Int.Max32 as the number of results required, like in the fetchxml query above, then when Dynamics CRM tries to add one to this value and parse it, it overflows and the query isn't run.

Admittedly it is extremely unlikely that this will be a problem, if you have 2147483647 instances of an entity in your database, I rather suspect that you need to be doing some archiving, but still it looks like a genuine issue.

It's interesting that the count value uses a signed integer rather than an unsigned one, which would give us twice as many records, i.e. 4294967295, or one record per every two human beings on Earth.

Sunday, 20 October 2013

Developers Vs Developers

I have been meaning to write this post for ages as this is something that I have encountered time and time again during my career.

We have an integration layer on our application with our telephony system. A third party wrote this integration layer, in essence they have a web services that we communicate to and we expose a web service so that they can communicate with us, so far so simple, this is a staple of enterprise development and as I said I have had to deal with situations like this many times, both inside and outside the company, and if you've not done it inside your company and think that none of the problems that you are having would occur if you didn't have to deal with the idiots at Third Part Ltd., let me tell you that you will just have to deal with the idiots at the Other Department.

At any rate, integration was working fine when somebody realized that the spec called for SSL/TLS usage rather than using clear text. In theory this requirement made sense when the various sides of the integration equation where hosted by two different companies, but a change of architecture had been made, which meant that they weren't any longer, so using a secure website for an internal web interface that contained little more than phone numbers and UUIDs seemed like overkill, but the spec is the spec, agile be dammed.

So both Web Services and client apps on either side of the integration equation where reconfigured to use SSL/TLS and this is were the problems started and as it's normally the case in these situations, we started blaming each other. 

Furthermore, a supplier customer relationship had been allowed to develop. This is a relationship in which the supplier has the technical know how, or believes he does, and the customer doesn't, or is at least believed not to have it by the supplier. Needless to say that this wasn't the case as we shall see, but for various personnel reasons, i.e. developers on our company leaving, this relationship had taken hold, which meant that our evidence carried less weight as it were, because they were already predisposed to assuming that they were talking to yet another developer who didn't have a clue about how the integration was supposed to be working, which wasn't true but it was true that they knew their system and the history, and while I knew ours but could not comment on historical decisions as this had landed on my lap without handover from somebody that left the company suddenly, not so suddenly but I digress.

We both went back to basis to prove our respective thesis, i.e. it was the other party's fault, because history is written by the victors, you know how this will turn out, but I will continue anyway. The first thing that we discovered was another disagreement in the spec, regarding authentication, which I remedied after a lot of hair pulling.

After that, I found that our side was working fine, furthermore, I used Wireshark to prove that nothing was coming through over the wire on port 443, while stuff was going through on port 80 from the client PC that hosted the client app, which meant that failures on their side were not due to our Web Service, this despite the fact that the app was throwing a 404 error and pointed me to this link.

I mentioned the supplier customer relationship above, because it helps to explain why this evidence, our Wireshark evidence was ignored, they knew what they were doing we didn't so anything coming from our side was tainted.

To further compound the confusion, the client app would sometimes work when tested by them, which made us extremely suspicious and not work for us, which surely made them more suspicious of our general competence. It was working for them and they were dealing with a bunch of idiots so they probably were doing something stupid.

At this point they were willing to discuss the code that they were using for the first time and we realized that the source of the issue was their code, to be fair it was the usage of our proxy server, when it should not have been used, there were other issues but they are not important.

So I asked them to add this to the system.net element of the client's app.config:

<defaultProxy  enabled="false"/>

Lo and behold, everything started to work fine.

Not sure what the lesson is here, I guess if there is one, it's that appearances do matter even in a supposedly analytical field like this one.

This link seems tangentially related.