Saturday, October 20, 2012

Dynamics CRM 2011 Form Script Analyzer

Dynamics CRM 2011 Form Script Analyzer

Few weeks ago i needed to get all the Javascript that is running in one of our customers Dynamics CRM 2011 Organization, I needed to get out all the Web Resource that are used, where are they used, what are the function that are running and on what are they running on (Onload, OnChange, OnSave, Ribbon buttons).
It was sisyphean work but after going over each Web Resource and their dependencies, I got the list out. This is what made me think, maybe I should build such a tool that takes it all out to an CSV file.

Yesterday, while surfing the web I've found the tool that does excetly that.

In TechEd New Zealand 2012 Gayan Perera presented a talk named:
Advanced Bag of Tips & Tricks for Microsoft Dynamics CRM 2011 Developers

I've enjoyed the presentation very much.
It was about:
T4 Code generation
MSCRM JS
Script#
How to convert managed solution to unmanaged solution
ILMerge
and Form Script Analyzer.

Back to the tool:
The Form Script Analyzer is Console Application tool that exports the Web resources and where their functions are called.

You can download it here : Download
For detailed description go to the TechEd Video and skip to 39 minutes (My recommention is to see the whole thing)

I haven't tried the tool yet, but it seems promising.

Friday, October 12, 2012

Dynamics CRM Interactive network visualization

A Jquery library to create interactive network visualization
http://flowingdata.com/2012/08/02/how-to-make-an-interactive-network-visualization/

If you have ideas of what to do with it in Dynamics CRM 2011, Please comment,  I'll see what i can do about it and I promise to post it in this blog.

Tuesday, October 9, 2012

The Evolution of a Programmer

A funny joke I've heard long ago and loved..

The Evolution of a Programmer

High School/Jr.High

  10 PRINT "HELLO WORLD"
  20 END

First year in College

  program Hello(input, output)
    begin
      writeln('Hello World')
    end.

Senior year in College

  (defun hello
    (print
      (cons 'Hello (list 'World))))

New professional

  #include <stdio.h>
  void main(void)
  {
    char *message[] = {"Hello ", "World"};
    int i;
 
    for(i = 0; i < 2; ++i)
      printf("%s", message[i]);
    printf("\n");
  }

Seasoned professional

  #include <iostream.h>
  #include <string.h>
 
  class string
  {
  private:
    int size;
    char *ptr;
 
  string() : size(0), ptr(new char[1]) { ptr[0] = 0; }
 
    string(const string &s) : size(s.size)
    {
      ptr = new char[size + 1];
      strcpy(ptr, s.ptr);
    }
 
    ~string()
    {
      delete [] ptr;
    }
 
    friend ostream &operator <<(ostream &, const string &);
    string &operator=(const char *);
  };
 
  ostream &operator<<(ostream &stream, const string &s)
  {
    return(stream << s.ptr);
  }
 
  string &string::operator=(const char *chrs)
  {
    if (this != &chrs)
    {
      delete [] ptr;
     size = strlen(chrs);
      ptr = new char[size + 1];
      strcpy(ptr, chrs);
    }
    return(*this);
  }
 
  int main()
  {
    string str;
 
    str = "Hello World";
    cout << str << endl;
 
    return(0);
  }

Master Programmer

  [
  uuid(2573F8F4-CFEE-101A-9A9F-00AA00342820)
  ]
  library LHello
  {
      // bring in the master library
      importlib("actimp.tlb");
      importlib("actexp.tlb");
 
      // bring in my interfaces
      #include "pshlo.idl"
 
      [
      uuid(2573F8F5-CFEE-101A-9A9F-00AA00342820)
      ]
      cotype THello
   {
   interface IHello;
   interface IPersistFile;
   };
  };
 
  [
  exe,
  uuid(2573F890-CFEE-101A-9A9F-00AA00342820)
  ]
  module CHelloLib
  {
 
      // some code related header files
      importheader(<windows.h>);
      importheader(<ole2.h>);
      importheader(<except.hxx>);
      importheader("pshlo.h");
      importheader("shlo.hxx");
      importheader("mycls.hxx");
 
      // needed typelibs
      importlib("actimp.tlb");
      importlib("actexp.tlb");
      importlib("thlo.tlb");
 
      [
      uuid(2573F891-CFEE-101A-9A9F-00AA00342820),
      aggregatable
      ]
      coclass CHello
   {
   cotype THello;
   };
  };
 
 
  #include "ipfix.hxx"
 
  extern HANDLE hEvent;
 
  class CHello : public CHelloBase
  {
  public:
      IPFIX(CLSID_CHello);
 
      CHello(IUnknown *pUnk);
      ~CHello();
 
      HRESULT  __stdcall PrintSz(LPWSTR pwszString);
 
  private:
      static int cObjRef;
  };
 
 
  #include <windows.h>
  #include <ole2.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include "thlo.h"
  #include "pshlo.h"
  #include "shlo.hxx"
  #include "mycls.hxx"
 
  int CHello::cObjRef = 0;
 
  CHello::CHello(IUnknown *pUnk) : CHelloBase(pUnk)
  {
      cObjRef++;
      return;
  }
 
  HRESULT  __stdcall  CHello::PrintSz(LPWSTR pwszString)
  {
      printf("%ws
", pwszString);
      return(ResultFromScode(S_OK));
  }
 
 
  CHello::~CHello(void)
  {
 
  // when the object count goes to zero, stop the server
  cObjRef--;
  if( cObjRef == 0 )
      PulseEvent(hEvent);
 
  return;
  }
 
  #include <windows.h>
  #include <ole2.h>
  #include "pshlo.h"
  #include "shlo.hxx"
  #include "mycls.hxx"
 
  HANDLE hEvent;
 
   int _cdecl main(
  int argc,
  char * argv[]
  ) {
  ULONG ulRef;
  DWORD dwRegistration;
  CHelloCF *pCF = new CHelloCF();
 
  hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 
  // Initialize the OLE libraries
  CoInitializeEx(NULL, COINIT_MULTITHREADED);
 
  CoRegisterClassObject(CLSID_CHello, pCF, CLSCTX_LOCAL_SERVER,
      REGCLS_MULTIPLEUSE, &dwRegistration);
 
  // wait on an event to stop
  WaitForSingleObject(hEvent, INFINITE);
 
  // revoke and release the class object
  CoRevokeClassObject(dwRegistration);
  ulRef = pCF->Release();
 
  // Tell OLE we are going away.
  CoUninitialize();
 
  return(0); }
 
  extern CLSID CLSID_CHello;
  extern UUID LIBID_CHelloLib;
 
  CLSID CLSID_CHello = { /* 2573F891-CFEE-101A-9A9F-00AA00342820 */
      0x2573F891,
      0xCFEE,
      0x101A,
      { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }
  };
 
  UUID LIBID_CHelloLib = { /* 2573F890-CFEE-101A-9A9F-00AA00342820 */
      0x2573F890,
      0xCFEE,
      0x101A,
      { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }
  };
 
  #include <windows.h>
  #include <ole2.h>
  #include <stdlib.h>
  #include <string.h>
  #include <stdio.h>
  #include "pshlo.h"
  #include "shlo.hxx"
  #include "clsid.h"
 
  int _cdecl main(
  int argc,
  char * argv[]
  ) {
  HRESULT  hRslt;
  IHello        *pHello;
  ULONG  ulCnt;
  IMoniker * pmk;
  WCHAR  wcsT[_MAX_PATH];
  WCHAR  wcsPath[2 * _MAX_PATH];
 
  // get object path
  wcsPath[0] = '\0';
  wcsT[0] = '\0';
  if( argc > 1) {
      mbstowcs(wcsPath, argv[1], strlen(argv[1]) + 1);
      wcsupr(wcsPath);
      }
  else {
      fprintf(stderr, "Object path must be specified\n");
      return(1);
      }
 
  // get print string
  if(argc > 2)
      mbstowcs(wcsT, argv[2], strlen(argv[2]) + 1);
  else
      wcscpy(wcsT, L"Hello World");
 
  printf("Linking to object %ws\n", wcsPath);
  printf("Text String %ws\n", wcsT);
 
  // Initialize the OLE libraries
  hRslt = CoInitializeEx(NULL, COINIT_MULTITHREADED);
 
  if(SUCCEEDED(hRslt)) {
 
 
      hRslt = CreateFileMoniker(wcsPath, &pmk);
      if(SUCCEEDED(hRslt))
   hRslt = BindMoniker(pmk, 0, IID_IHello, (void **)&pHello);
 
      if(SUCCEEDED(hRslt)) {
 
   // print a string out
   pHello->PrintSz(wcsT);
 
   Sleep(2000);
   ulCnt = pHello->Release();
   }
      else
   printf("Failure to connect, status: %lx", hRslt);
 
      // Tell OLE we are going away.
      CoUninitialize();
      }
 
  return(0);
  }

Apprentice Hacker

  #!/usr/local/bin/perl
  $msg="Hello, world.\n";
  if ($#ARGV >= 0) {
    while(defined($arg=shift(@ARGV))) {
      $outfilename = $arg;
      open(FILE, ">" . $outfilename) || die "Can't write $arg: $!\n";
      print (FILE $msg);
      close(FILE) || die "Can't close $arg: $!\n";
    }
  } else {
    print ($msg);
  }
  1;

Experienced Hacker

  #include <stdio.h>
  #define S "Hello, World\n"
  main(){exit(printf(S) == strlen(S) ? 0 : 1);}

Seasoned Hacker

  % cc -o a.out ~/src/misc/hw/hw.c
  % a.out

Guru Hacker

  % echo "Hello, world."

New Manager

  10 PRINT "HELLO WORLD"
  20 END

Middle Manager

  mail -s "Hello, world." bob@b12
  Bob, could you please write me a program that prints "Hello, world."?
  I need it by tomorrow.
  ^D

Senior Manager

  % zmail jim
  I need a "Hello, world." program by this afternoon.

Chief Executive

  % letter
  letter: Command not found.
  % mail
  To: ^X ^F ^C
  % help mail
  help: Command not found.
  % damn!
  !: Event unrecognized
  % logout


*Taken from:
http://www.ariel.com.au/jokes/The_Evolution_of_a_Programmer.html

Wednesday, October 3, 2012

Dynamics CRM - small tip for formatted record name

Dynamics CRM 2011 - small tip for formatted record name

When there is a certain format to a record name it's best not to start it with the same initial all the time (unless it has a real purpose)

For example:
Let's say there is a custom entity called: new_certification and it's related to user,
If all new_certification records would start with lets say Certification, then it would be really annoying to use the lookup auto complete (It's when you are writing inside the lookup textbox, the CRM completes the name and sets the record for you).

General Role: Always write meaningful names to records and don't leave them empty!

Tuesday, October 2, 2012

How to change the customer / partner portal V2 to authenticate against the MSCRM only.


How to change the customer / partner portal V2 to authenticate against the MSCRM only.

There is a new portal and the changes are Little bit different than it was in the previous version
Customer portal V1 changes can be found here

Walkthrough:

In order to do it you'll need to download the portal and follow the installation steps, avoid all the stuff regarding the LiveID.
After you've imported the solution and the website data we are ready to go.
1)      Open the website in visual studio.
2)      Edit he web.config.
a.                 Remove
                a.1)
 <add name="Live" connectionString="Application Id=0000000000000000; Secret=00000000000000000000000000000000"/>

 a.2)
 <add key="FederationMetadataLocation" value="https://contoso.accesscontrol.windows.net/FederationMetadata/2007-06/FederationMetadata.xml"/>

                        a.3)
 <membership defaultProvider="CrmMembershipProvider">
                                        <providers>
                                                        <add name="CrmMembershipProvider" type="Microsoft.Xrm.Portal.Web.Security.LiveIdMembershipProvider, Microsoft.Xrm.Portal" liveIdConnectionStringName="Live"/>
                                        </providers>
                                        </membership>

                        a.4) 
<httpRuntime maxRequestLength="102400" requestValidationMode="2.0" requestValidationType="Microsoft.Xrm.Portal.IdentityModel.Web.FederationRequestValidator, Microsoft.Xrm.Portal"/>

                        a.5)
 <add name="LiveId" verb="*" path="LiveID.axd" preCondition="integratedMode" type="Microsoft.Xrm.Portal.IdentityModel.Web.Handlers.LiveIdAccountTransferHandler, Microsoft.Xrm.Portal"/>
                                        <add name="Federation" verb="*" path="Federation.axd" preCondition="integratedMode" type="Microsoft.Xrm.Portal.IdentityModel.Web.Handlers.FederationAuthenticationHandler, Microsoft.Xrm.Portal"/>


a.6) 
<microsoft.identityModel>
<service>
                                        <audienceUris>
                                                        <add value="http://contoso.cloudapp.net/"/>
                                        </audienceUris>
                                        <federatedAuthentication>
                                                        <wsFederation passiveRedirectEnabled="false" issuer="https://contoso.accesscontrol.windows.net/v2/wsfederation" realm="http://contoso.cloudapp.net/" requireHttps="false"/>
                                                        <cookieHandler requireSsl="false"/>
                                        </federatedAuthentication>
                                        <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
                                                        <trustedIssuers>
                                                                        <add thumbprint="0000000000000000000000000000000000000000" name="https://contoso.accesscontrol.windows.net/"/>
                                                        </trustedIssuers>
                                        </issuerNameRegistry>
                        </service>
        </microsoft.identityModel>
        <microsoft.xrm.portal.identityModel>
                        <registration enabled="true" registrationPath="~/confirm-invite" profilePath="~/profile" accountTransferPath="~/login" requiresInvitation="true"
                                        requiresChallengeAnswer="false" requiresConfirmation="false" invitationCodeDuration="01:00:00"/>
        </microsoft.xrm.portal.identityModel>

b.         Replace the Authentication tag to as follow
<authentication mode="Forms">
<forms loginUrl="/login" timeout="525600"     defaultUrl="/"  />
 </authentication>

3)      Edit \Pages\Login.aspx
a.         Remove                
a.1)
<%@ Register TagPrefix="adx" TagName="AzureAcs" Src="~/Controls/AzureAcs.ascx" %>

a.2)
<adx:AzureAcs runat="server" />

a.3)
<crm:Snippet runat="server" SnippetName="Login/ACS/AccountTransfer/Heading" DefaultText="Live ID Account Transfer" />

a.4)
<p>Already registered with a Windows Live ID account? Sign in to transfer the account to an AppFabric ACS account:</p>

b.         Replace
<crm:LiveIdLoginStatus ID="TransferLiveIdLink" runat="server" LoginImageUrl="https://www.passportimages.com/1033/signin.gif"
LogoutImageUrl="https://www.passportimages.com/1033/signout.gif" />
With
  <asp:Login ID="Login1" runat="server" OnAuthenticate="Login1_Authenticate"></asp:Login>

5)   Edit \Pages\Login.aspx.cs
a.                Add to using
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Crm.Sdk;
using Microsoft.Xrm.Client.Services;
using Microsoft.Xrm.Client;
using Xrm;
using System.Web.Security;

b.                Replace Page_Load
protected void Page_Load(object sender, EventArgs e)
        {
            if ((User != null) && User.Identity.IsAuthenticated)
            {
                var redirectUrl = !string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]) ? Request["ReturnUrl"]: !string.IsNullOrEmpty(Request.QueryString["URL"])? Request["URL"]: "/";
                Response.Redirect(redirectUrl);
            }
        }
c.                 Add
        private Contact _loginContact;

        protected Contact LoginContact
        {
            get
            {
                return _loginContact ?? (_loginContact = XrmContext.ContactSet.FirstOrDefault(c => c.Adx_username == Login1.UserName && c.Adx_LogonEnabled != null && c.Adx_LogonEnabled.Value));
            }
        }

        protected void Login1_Authenticate(object sender, System.Web.UI.WebControls.AuthenticateEventArgs e)
        {
            if (LoginContact == null)
            {
                e.Authenticated = false;
            }
            else
            {
                if (LoginContact.Adx_password == Login1.Password)
                {
                if (LoginContact.Adx_changepasswordatnextlogon != null && LoginContact.Adx_changepasswordatnextlogon.Value)
                    {
                        //var page = ServiceContext.GetPageBySiteMarkerName(Website, "ChangePassword");
                        //string redirectURL = ServiceContext.GetUrl(page) + "?UserName=" + Server.UrlEncode(Login1.UserName) + "&Password=" + Server.UrlEncode(Login1.Password);
                        //Response.Redirect(redirectURL);
                    }
                    else
                    {
                        LoginContact.Adx_LastSuccessfulLogon = DateTime.Now.Date;
                        XrmContext.UpdateObject(LoginContact);
                        XrmContext.SaveChanges();
                        e.Authenticated = true;
                        // Response.Redirect("/");
                        FormsAuthentication.RedirectFromLoginPage(Login1.UserName, true);
                    }
                }
                else
                {
                    e.Authenticated = false;
                }
            }
        }

Compile, Debug and Publish to the IIS.

Download changed portal: