Sunday, September 25, 2011

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


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


There is a new version for the portals, to see how to convert Customer Portal V2 go to http://dynamicslollipops.blogspot.co.il/2012/10/how-to-change-customer-partner-portal.html


This blog is based on few other blogs:
Active Directory Authentication:
http://www.shanmcarthur.net/crm/developers-corner/customer-portal-modifications-for-demo-vpc-without-internet-connectivity
SqlMembershipProvider: http://intergr8it.net/?p=216
Both are great and doing the work.
Yet I had to create another approach.

As you probably know the portals comes with authentication against LiveID, Even that it's best practice and all, sometimes the portal isn't expost to the internet and simpler authentication is needed.

Basically the authentication options are:

1)      LiveID – Each user has to have account in Microsoft LiveId website in order to use the portal.

2)      Active Directory – Each user has to be active directory user (almost same as every MSCRM user)

3)      SqlMemberhipProvider – Each user will be created in separate sql table.

4)      Form authentication – It's up to you (In code) who to authenticate and who not. This is the one we'll use to authenticate user based only on the data in the MSCRM.

In this Solution register page is droped and the crm administrator is registers the users stright in the crm. Once the user is configured in the crm his good to go.

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 the
<add name="Live" connectionString="Application Id=0000000000000000; Secret=00000000000000000000000000000000"/>

From the connectionstring tag

b.      Remove the
<membership defaultProvider="CrmMembershipProvider">
<providers><add name="CrmMembershipProvider" type="Microsoft.Xrm.Portal.Web.Security.LiveIdMembershipProvider, Microsoft.Xrm.Portal" liveIdConnectionStringName="Live"/></providers></membership>

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

 </authentication>

3)      Edit \MasterPages\Default.master.

a.       Replace

<crm:LiveIdLoginStatus ID="LiveLoginStatus" runat="server" LoginNavigateUrl='<%$ CrmSiteMap: SiteMarker=Login, Eval=Url %>'/>

With

<asp:LoginStatus ID="LoginStatus1" runat="server" />

4)      Edit \Pages\Login.aspx

a.        Replace               
<crm:LiveIdLoginStatus ID="LiveIdLink" runat="server" LoginImageUrl="http://www.passportimages.com/1033/signin.gif"

LogoutImageUrl="http://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:
  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));

            }

        }

b.        Replace onload:
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:
  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.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.

Friday, September 16, 2011

MSCRM 2011 Ribbon dynamically display the save button.


Microsoft Dynamics CRM 2011 Ribbon is great once you get to know how to work with it. 
At the beginning it looked too complicated, there are too many tags to write and it is difficult to understand how to place the button in the right place. 
The good news are after understanding the why it works, it all makes sense.
They are very flexible and you can do all most everything in a supported why.

This blog shows how to remove and show Ribbon save buttons based on some logic.

In MSCRM 4 it can be done using unsupported code
Something like: 

var toolBarButtons = document.all.mnuBar1.rows[0].cells[0].childNodes[0].childNodes;
and then iterate over all the buttons till the save button found and removed / showed when needed.


In MSCRM 2011 there is supported and much more elegant solution:

Generally speaking you should know these steps before starting (Copied from: http://msdn.microsoft.com/en-us/library/gg334532.aspx):
1.     Prerequisites: This section describes the specific requirements and resources you need.
2.     Export: Create and export a solution containing just the solution components you need. For more information about creating and exporting a solution see the application Help and Export the Ribbon.
3.     Identify the IDs for existing items: Examine default ribbon definitions. The walkthrough provides the XML you need, but it is important that you understand the relationships to existing data in the default ribbon definitions.
4.     Create the script Web resource: Walkthroughs that add controls to the ribbon require a JScript Web resource to perform an action. For more information about creating JScript Web resources, see the application Help and JScript Libraries for Microsoft Dynamics CRM.
5.     Edit the customizations.xml file: This section leads you through a series of cumulative steps editing nodes within the <RibbonDiffXml> (RibbonDiffXml)element. The final step shows you the entire modified RibbonDiffXml element in the customizations.xml file.
6.     Import and publish the solution: Compress the modified solution files and import those files to update your solution. For more information about compressing and importing a solution, see the application Help and Import the Ribbon.
7.     Test and verify that the requirements are met: In this step you confirm that the requirements specified in the prerequisites section are met.


We'll twist it a little bit to meet our requirement.

1) Replace the CommandDefinition that descripes the save button with new one. We'll copy it from incidentribbon.xml and place it in the exported customization.xml.

      <CommandDefinition Id="Mscrm.SavePrimary">
        <EnableRules>
          <EnableRule Id="Mscrm.AvailableOnForm" />
          <EnableRule Id="Mscrm.CanSavePrimary" />
          <EnableRule Id="Mscrm.ReadPrimaryPermission" />
          <EnableRule Id="Mscrm.Form.incident.MainTab.CustomRule.EnableRule" />
        </EnableRules>
        <DisplayRules>
          <DisplayRule Id="Mscrm.CanSavePrimaryEntityType" />
        </DisplayRules>
        <Actions>
          <JavaScriptFunction FunctionName="Mscrm.RibbonActions.saveForm" Library="/_static/_common/scripts/RibbonActions.js">
            <CrmParameter Value="PrimaryControl" />
          </JavaScriptFunction>
        </Actions>
      </CommandDefinition>

2) add new Enable Rule which indicates should it be shown or not
<EnableRule Id="Mscrm.Form.incident.MainTab.CustomRule.EnableRule">
     <CustomRule FuncationName="EnableButtons" Library="$Webresource:new_incident_library" />
</EnableRule>

3) Now in your code you'll be able to decide would the save button be shown or not by defining the logic in the function EnableButtons. Whenever you want to change the display make sure the function returns true / false as expected and refresh the ribbon by using: 
Xrm.Page.ui.refreshRibbon()

The thing to remember here is that you can change the current setting of almost everything in the ribbon by redefining them.