Wednesday, June 10, 2015

Retrieve Global Optionset list of values with C#

This is a method that you can use to retrieve a Global Optionset list of values with C#, it works for CRM 2011, 2013 and 2015.

/// <summary>
/// Method to retrieve global optionset metadata values
/// </summary>
/// <param name="_serviceProxy">CRM Service instance</param>
/// <param name="_globalOptionSetName">Global Optionset name</param>
/// <returns>Returns the global optionset metadata values</returns>
private OptionMetadata[] GetGlobalOptionsetValues(OrganizationServiceProxy _serviceProxy, string _globalOptionSetName)
{
 OptionMetadata[] result = null;

 // Use the RetrieveOptionSetRequest message to retrieve  
 // a global option set by it's name.
 RetrieveOptionSetRequest retrieveOptionSetRequest = new RetrieveOptionSetRequest(){ Name = _globalOptionSetName };

 // Execute the request.
 RetrieveOptionSetResponse retrieveOptionSetResponse = (RetrieveOptionSetResponse)_serviceProxy.Execute(retrieveOptionSetRequest);            

 // Access the retrieved OptionSetMetadata.
 OptionSetMetadata retrievedOptionSetMetadata = (OptionSetMetadata)retrieveOptionSetResponse.OptionSetMetadata;

 // Get the current options list for the retrieved attribute.
 result = retrievedOptionSetMetadata.Options.ToArray();

 return result;
}      

Tuesday, June 9, 2015

UserHasTeam for CRM 2013 - Check if a user belongs to X team with Javascript

Method to check if the user belongs to X team, looking by name:

You can call it this way:

if(UserhasTeam("Sales Managers")){
           // Do something
}else{}


function UserHasTeam(teamName) {
    ///<summary>
    /// Checks to see if the current user is a member of a team with the passed in name.
    ///</summary>
    ///<param name="teamName" type="String">
    /// A String representing the name of the team to check if the user is a member of.
    ///</param>
    // build endpoint URL
    var serverUrl = Xrm.Page.context.getClientUrl();
    var oDataEndpointUrl = serverUrl + "/XRMServices/2011/OrganizationData.svc/";
                var result = false;
    // query to get the teams that match the name
    oDataEndpointUrl += "TeamSet?$select=Name,TeamId&$filter=Name eq '" + teamName + "'";
    //var service = GetRequestObject();
    var service = new XMLHttpRequest();
    service.open("GET", oDataEndpointUrl, false);
    service.setRequestHeader("Accept", "application/json");
    service.setRequestHeader("Content-Type", "application/json;charset=utf-8");
    service.onreadystatechange = function() {
        if (service.readyState == 4) {
            if (service.status == 200) {
                debugger;
                var requestResults = JSON.parse(service.responseText).d;
                if (requestResults != null && requestResults.results.length > 0) {
                    var teamCounter;
                    // iterate through all of the matching teams, checking to see if the current user has a membership
                    for (teamCounter = 0; teamCounter < requestResults.results.length; teamCounter++) {
                        var team = requestResults.results[teamCounter];
                        var teamId = team.TeamId;
                        // get current user teams
                        var currentUserTeams = getUserTeams(teamId);
                        // Check whether current user teams matches the target team
                        if (currentUserTeams != null) {
                            for (var i = 0; i < currentUserTeams.length; i++) {
                                var userTeam = currentUserTeams[i];
                                // check to see if the team guid matches the user team membership id
                                if (GuidsAreEqual(userTeam.TeamId, teamId)) {
                                    result = true;
                                }
                            }
                        } else {
                            result =  false;
                        }
                    }
                } else {
                    alert("Team with name '" + teamName + "' not found");
                    result =  false;
                }
            }
        }
    };
    service.send();
             
                return result;
}

function getUserTeams(teamToCheckId) {
    // gets the current users team membership
    var userId = Xrm.Page.context.getUserId().substr(1, 36);
    var serverUrl = Xrm.Page.context.getClientUrl();
                var result;
             
    var oDataEndpointUrl = serverUrl + "/XRMServices/2011/OrganizationData.svc/";
    oDataEndpointUrl += "TeamMembershipSet?$filter=SystemUserId eq guid' " + userId + " ' and TeamId eq guid' " + teamToCheckId + " '";
             
                var service = new XMLHttpRequest();
    service.open("GET", oDataEndpointUrl, false);
    service.setRequestHeader("Accept", "application/json");
    service.setRequestHeader("Content-Type", "application/json;charset=utf-8");
    service.onreadystatechange = function() {
        if (service.readyState == 4) {
            if (service.status == 200) {
                                                                // get user teams
                debugger;
                                                                var requestResults = JSON.parse(service.responseText).d;
                                                                if (requestResults != null && requestResults.results.length > 0) {
                                                                                result = requestResults.results;
                                                                }
                                                }
                                }
                }
                service.send();
                return result;
}

function GuidsAreEqual(guid1, guid2) {
    // compares two guids
    var isEqual = false;
    if (guid1 == null || guid2 == null) {
        isEqual = false;
    } else {
        isEqual = (guid1.replace(/[{}]/g, "").toLowerCase() == guid2.replace(/[{}]/g, "").toLowerCase());
    }
    return isEqual;
}

RetrieveMultiple - Retrieve more than 5000 records with C#

This method uses the RetrieveMultiple SDK Method and loops between all the Pages in order to avoid the 5000 records limitation.

/// <summary>
/// Method used to return more than 5000 records at the same time
/// </summary>
/// <param name="service">Organization service instance</param>
/// <param name="query">Query</param>
/// <returns>All entities that matches the query</returns>
private List < Entity > RetrieveMultiple(OrganizationServiceProxy service, QueryExpression query) {
 List < Entity > result = new List < Entity > ();
 int fetchCount = 5000;
 int pageNumber = 1;

 query.PageInfo = new PagingInfo();
 query.PageInfo.Count = fetchCount;
 query.PageInfo.PageNumber = pageNumber;
 query.PageInfo.PagingCookie = null;

 while (true) {
  EntityCollection collections = service.RetrieveMultiple(query);

  if (collections.Entities.Count > 0) {

   result.AddRange(collections.Entities);
  }

  if (collections.MoreRecords) {
   query.PageInfo.PageNumber++;
   query.PageInfo.PagingCookie = collections.PagingCookie;
  } else {
   break;
  }
 }

 return result;
}

CRM 2013 - Unable to run any report

Next time you are creating/editing security roles in CRM 2013 make sure you add the privilege to READ Currency entity.

 I was fighting with this issue, the users were unable to run any report under a new security role, after testing privileges one by one, I noticed that the security role needs to have the prvReadTransactionCurrency set to Organization level, this will allow the users to run any report (retrieving the data will depend on other privileges).


I will add pictures later...

Monday, December 10, 2012

CRM 2011, Authenticate With No Help



Since Office 365 authentication appeared, I had some troubles to authenticate from C# to CRM, (On premise, Online), but a few days ago, I found this helper that allows you to connect to any type of CRM environment, (On premise, Online with windows live id, or Online with office 365), enjoy :).
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Reflection;

// These namespaces are found in the Microsoft.Xrm.Sdk.dll assembly
// located in the SDK\bin folder of the SDK download.
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Crm.Sdk.Messages;

namespace Microsoft.Crm.Sdk.Samples
{
    /// <summary>
    /// Demonstrate how to do basic authentication using IServiceManagement and SecurityTokenResponse.
    /// </summary>
    class AuthenticateWithNoHelp
    {
        #region Class Level Members
        // To get discovery service address and organization unique name, 
        // Sign in to your CRM org and click Settings, Customization, Developer Resources.
        // On Developer Resource page, find the discovery service address under Service Endpoints and organization unique name under Your Organization Information.
        private String _discoveryServiceAddress = "https://disco.crm.dynamics.com/XRMServices/2011/Discovery.svc";
        private String _organizationUniqueName = "crmue";
        // Provide your user name and password.
        private String _userName = "sdk@crmue.onmicrosoft.com";
        private String _password = "ITG2install!";

        // Provide domain name for the On-Premises org.
        private String _domain = "mydomain";

        #endregion Class Level Members

        #region How To Sample Code
        /// <summary>
        /// 
        /// </summary>
        public void Run()
        {
            IServiceManagement<IDiscoveryService> serviceManagement =
                        ServiceConfigurationFactory.CreateManagement<IDiscoveryService>(
                        new Uri(_discoveryServiceAddress));
            AuthenticationProviderType endpointType = serviceManagement.AuthenticationType;

            // Set the credentials.
            AuthenticationCredentials authCredentials = GetCredentials(endpointType);


            String organizationUri = String.Empty;
            // Get the discovery service proxy.
            using (DiscoveryServiceProxy discoveryProxy =
                GetProxy<IDiscoveryService, DiscoveryServiceProxy>(serviceManagement, authCredentials))
            {
                // Obtain organization information from the Discovery service. 
                if (discoveryProxy != null)
                {
                    // Obtain information about the organizations that the system user belongs to.
                    OrganizationDetailCollection orgs = DiscoverOrganizations(discoveryProxy);
                    // Obtains the Web address (Uri) of the target organization.
                    organizationUri = FindOrganization(_organizationUniqueName,
                        orgs.ToArray()).Endpoints[EndpointType.OrganizationService];

                }
            }


            if (!String.IsNullOrWhiteSpace(organizationUri))
            {
                IServiceManagement<IOrganizationService> orgServiceManagement =
                    ServiceConfigurationFactory.CreateManagement<IOrganizationService>(
                    new Uri(organizationUri));

                // Set the credentials.
                AuthenticationCredentials credentials = GetCredentials(endpointType);

                // Get the organization service proxy.
                using (OrganizationServiceProxy organizationProxy =
                    GetProxy<IOrganizationService, OrganizationServiceProxy>(orgServiceManagement, credentials))
                {
                    // This statement is required to enable early-bound type support.
                    organizationProxy.EnableProxyTypes();

                    // Now make an SDK call with the organization service proxy.
                    // Display information about the logged on user.
                    Guid userid = ((WhoAmIResponse)organizationProxy.Execute(
                        new WhoAmIRequest())).UserId;
                    SystemUser systemUser = organizationProxy.Retrieve("systemuser", userid,
                        new ColumnSet(new string[] { "firstname", "lastname" })).ToEntity<SystemUser>();
                    Console.WriteLine("Logged on user is {0} {1}.",
                        systemUser.FirstName, systemUser.LastName);
                }
            }

        }

        /// <summary>
        /// Obtain the AuthenticationCredentials based on AuthenticationProviderType.
        /// </summary>
        /// <param name="endpointType">An AuthenticationProviderType of the CRM environment.</param>
        /// <returns>Get filled credentials.</returns>
        private AuthenticationCredentials GetCredentials(AuthenticationProviderType endpointType)
        {

            AuthenticationCredentials authCredentials = new AuthenticationCredentials();
            switch (endpointType)
            {
                case AuthenticationProviderType.ActiveDirectory:
                    authCredentials.ClientCredentials.Windows.ClientCredential =
                        new System.Net.NetworkCredential(_userName,
                            _password,
                            _domain);
                    break;
                case AuthenticationProviderType.LiveId:
                    authCredentials.ClientCredentials.UserName.UserName = _userName;
                    authCredentials.ClientCredentials.UserName.Password = _password;
                    authCredentials.SupportingCredentials = new AuthenticationCredentials();
                    authCredentials.SupportingCredentials.ClientCredentials =
                        Microsoft.Crm.Services.Utility.DeviceIdManager.LoadOrRegisterDevice();
                    break;
                default: // For Federated and OnlineFederated environments.                    
                    authCredentials.ClientCredentials.UserName.UserName = _userName;
                    authCredentials.ClientCredentials.UserName.Password = _password;
                    // For OnlineFederated single-sign on, you could just use current UserPrincipalName instead of passing user name and password.
                    // authCredentials.UserPrincipalName = UserPrincipal.Current.UserPrincipalName;  //Windows/Kerberos
                    break;
            }

            return authCredentials;
        }

        /// <summary>
        /// Discovers the organizations that the calling user belongs to.
        /// </summary>
        /// <param name="service">A Discovery service proxy instance.</param>
        /// <returns>Array containing detailed information on each organization that 
        /// the user belongs to.</returns>
        public OrganizationDetailCollection DiscoverOrganizations(
            IDiscoveryService service)
        {
            if (service == null) throw new ArgumentNullException("service");
            RetrieveOrganizationsRequest orgRequest = new RetrieveOrganizationsRequest();
            RetrieveOrganizationsResponse orgResponse =
                (RetrieveOrganizationsResponse)service.Execute(orgRequest);

            return orgResponse.Details;
        }

        /// <summary>
        /// Finds a specific organization detail in the array of organization details
        /// returned from the Discovery service.
        /// </summary>
        /// <param name="orgUniqueName">The unique name of the organization to find.</param>
        /// <param name="orgDetails">Array of organization detail object returned from the discovery service.</param>
        /// <returns>Organization details or null if the organization was not found.</returns>
        /// <seealso cref="DiscoveryOrganizations"/>
        public OrganizationDetail FindOrganization(string orgUniqueName,
            OrganizationDetail[] orgDetails)
        {
            if (String.IsNullOrWhiteSpace(orgUniqueName))
                throw new ArgumentNullException("orgUniqueName");
            if (orgDetails == null)
                throw new ArgumentNullException("orgDetails");
            OrganizationDetail orgDetail = null;

            foreach (OrganizationDetail detail in orgDetails)
            {
                if (String.Compare(detail.UniqueName, orgUniqueName,
                    StringComparison.InvariantCultureIgnoreCase) == 0)
                {
                    orgDetail = detail;
                    break;
                }
            }
            return orgDetail;
        }

        /// <summary>
        /// Generic method to obtain discovery/organization service proxy instance.
        /// </summary>
        /// <typeparam name="TService">
        /// Set IDiscoveryService or IOrganizationService type to request respective service proxy instance.
        /// </typeparam>
        /// <typeparam name="TProxy">
        /// Set the return type to either DiscoveryServiceProxy or OrganizationServiceProxy type based on TService type.
        /// </typeparam>
        /// <param name="serviceManagement">An instance of IServiceManagement</param>
        /// <param name="authCredentials">The user's Microsoft Dynamics CRM logon credentials.</param>
        /// <returns></returns>
        private TProxy GetProxy<TService, TProxy>(
            IServiceManagement<TService> serviceManagement,
            AuthenticationCredentials authCredentials)
            where TService : class
            where TProxy : ServiceProxy<TService>
        {
            Type classType = typeof(TProxy);

            if (serviceManagement.AuthenticationType !=
                AuthenticationProviderType.ActiveDirectory)
            {
                AuthenticationCredentials tokenCredentials =
                    serviceManagement.Authenticate(authCredentials);
                // Obtain discovery/organization service proxy for Federated, LiveId and OnlineFederated environments. 
                // Instantiate a new class of type using the 2 parameter constructor of type IServiceManagement and SecurityTokenResponse.
                return (TProxy)classType
                    .GetConstructor(new Type[] { typeof(IServiceManagement<TService>), typeof(SecurityTokenResponse) })
                    .Invoke(new object[] { serviceManagement, tokenCredentials.SecurityTokenResponse });
            }

            // Obtain discovery/organization service proxy for ActiveDirectory environment.
            // Instantiate a new class of type using the 2 parameter constructor of type IServiceManagement and ClientCredentials.
            return (TProxy)classType
                .GetConstructor(new Type[] { typeof(IServiceManagement<TService>), typeof(ClientCredentials) })
                .Invoke(new object[] { serviceManagement, authCredentials.ClientCredentials });
        }

        #endregion How To Sample Code

        #region Main method

        /// <summary>
        /// Standard Main() method used by most SDK samples.
        /// </summary>
        /// <param name="args"></param>
        static public void Main(string[] args)
        {
            try
            {
                AuthenticateWithNoHelp app = new AuthenticateWithNoHelp();
                app.Run();
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine("Timestamp: {0}", ex.Detail.Timestamp);
                Console.WriteLine("Code: {0}", ex.Detail.ErrorCode);
                Console.WriteLine("Message: {0}", ex.Detail.Message);
                Console.WriteLine("Trace: {0}", ex.Detail.TraceText);
                Console.WriteLine("Inner Fault: {0}",
                    null == ex.Detail.InnerFault ? "Has Inner Fault" : "No Inner Fault");
            }
            catch (System.TimeoutException ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine("Message: {0}", ex.Message);
                Console.WriteLine("Stack Trace: {0}", ex.StackTrace);
                Console.WriteLine("Inner Fault: {0}",
                    null == ex.InnerException.Message ? "Has Inner Fault" : "No Inner Fault");
            }
            catch (System.Exception ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine(ex.Message);

                // Display the details of the inner exception.
                if (ex.InnerException != null)
                {
                    Console.WriteLine(ex.InnerException.Message);

                    FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> fe = ex.InnerException
                        as FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>;
                    if (fe != null)
                    {
                        Console.WriteLine("Timestamp: {0}", fe.Detail.Timestamp);
                        Console.WriteLine("Code: {0}", fe.Detail.ErrorCode);
                        Console.WriteLine("Message: {0}", fe.Detail.Message);
                        Console.WriteLine("Trace: {0}", fe.Detail.TraceText);
                        Console.WriteLine("Inner Fault: {0}",
                            null == fe.Detail.InnerFault ? "Has Inner Fault" : "No Inner Fault");
                    }
                }
            }
            // Additional exceptions to catch: SecurityTokenValidationException, ExpiredSecurityTokenException,
            // SecurityAccessDeniedException, MessageSecurityException, and SecurityNegotiationException.

            finally
            {
                Console.WriteLine("Press <Enter> to exit.");
                Console.ReadLine();
            }
        }
        #endregion Main method
    }
}

You can find the original post here.

Tuesday, November 6, 2012

Xrm.Utility

Well, I was not aware of this new object created by Microsoft, since Microsoft Dynamics CRM 2011 Update Rollup 8. (http://msdn.microsoft.com/en-us/library/jj602956.aspx)

It has 2 function (both open a new browser window) called:

openEntityForm 
  Xrm.Utility.openEntityForm(name,id,parameters) 

Examples:
Open a new account record
Xrm.Utility.openEntityForm("account");
Open an existing account record
Xrm.Utility.openEntityForm("account","A85C0252-DF8B-E111-997C-00155D8A8410");

Open a new account record with a specific form and setting default values
var parameters = {};
parameters["formid"] = "b053a39a-041a-4356-acef-ddf00182762b";
parameters["name"] = "Test";
parameters["telephone1"] = "(425) 555-1234";
Xrm.Utility.openEntityForm("account", null, parameters);
Open a new contact record, move it to the top left corner of the screen, and set the size of the window 
NoteYou cannot use window object methods such as moveTo or resizeTo in scripts that will run in Microsoft Dynamics CRM for Microsoft Office Outlook.
var newWindow = Xrm.Utility.openEntityForm("contact");
newWindow.moveTo(0,0);
newWindow.resizeTo(800,600);
openWebResource

    Xrm.Utility.openWebResource(webResourceName,webResourceData,width, height)
Examples:
Open an HTML web resource named “new_webResource.htm”:Xrm.Utility.openWebResource("new_webResource.htm");
Open an HTML web resource including a single item of data for the data parameter”Xrm.Utility.openWebResource("new_webResource.htm","dataItemValue");
Open an HTML web resource passing multiple values through the data parameter
var customParameters = encodeURIComponent("first=First Value&second=Second Value&third=Third Value"); Xrm.Utility.openWebResource("new_webResource.htm",customParameters); 
NoteThese values have to be extracted from the value of the data parameter in the HTML web resource. For more information, see Sample: Pass Multiple Values to a Web Resource Through the Data Parameter.
Open an HTML web resource with the parameters expected by HTML web resources:
Xrm.Utility.openWebResource("new_webResource.htm?typename=account&userlcid=1033");




Wednesday, October 31, 2012

CRM 2011 Recurring workflows - Running only one instance at the same time

Maybe some of you already read the post of how to implement a recurring workflow in Dynamics CRM 2011, I have read it from http://crmbusiness.wordpress.com/2011/05/24/crm-2011-how-to-schedule-recurring-workflows-in-crm-2011/ , the article is copied below.

When I implemented this approach with a client, I had the next problem, The user was clicking on a ribbon button and that button was calling a JScript code that performed a call to a workflow using something similar to this: (http://andreaswijayablog.blogspot.com/2011/07/crm-2011-custom-button-on-custom-entity.html)

function callworkfow(id) {
    try {
        var guid;

        if (id == null) {
            guid = Xrm.Page.data.entity.getId();
        }
        else
            guid = id;

        // assign workflow guid
        var WorkflowId = "89BA3166-948E-4B12-8A55-4BE01DEDAB0B";

        var xml = "" +
    "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
    "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
    Xrm.Page.context.getAuthenticationHeader() +
    "<soap:Body>" +
    "<execute xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" +
    "<request xsi:type=\"ExecuteWorkflowRequest\">" +
    "<entityid>" + guid + "</EntityId>" +
    "<workflowid>" + WorkflowId + "</WorkflowId>" + //WorkflowId = guid of the workflow
    "</Request>" +
    "</Execute>" +
    "</soap:Body>" +
    "</soap:Envelope>";

        var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
        xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
        xmlHttpRequest.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Execute");
        xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
        xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
        xmlHttpRequest.send(xml);

        var resultXml = xmlHttpRequest.responseXML;

        if (id == null)
            alert("Success");
        return (resultXml.xml);
    }
    catch (e) {
        alert("Failed to Execute");
    }
}


But, the problem here was that I needed to have only one instance of the workflow running at the same time, and the way it was implemented was creating a new instance of the workflow everytime the button was clicked, and as far as I know the CRM webservice does not have a way to retrieve Async Operations in case I need to be sure that any instance of the workflow was running before calling it, and neither the Plugins can be attached to Async Operations, so.. (If you already followed the tutorial from http://crmbusiness.wordpress.com/2011/05/24/crm-2011-how-to-schedule-recurring-workflows-in-crm-2011/ ), what I did to solve this problem is the next:


Change the workflow to run only as Child and on i.e. Appointments CREATION

---------------  USER ---------------------------------------------------------------------------------------------------------------------
- User clicks on the ribbon button, this will trigger the jscript code
-------------------------------------------------------------------------------------------------------------------------------------------------

--------------- JSCRIPT Code that will be called from the ribbon button------------------------------------------------
- A jscript code searches for an appointment with subject "Only one workflow instance"
   - If the appointment is found, then do nothing.. (Because this means that the workflow is running already)
   - But if the appointment was not found, then  CREATE an appointment with subject "Only one workflow instance", this will trigger the workflow.
--------------------------------------------------------------------------------------------------------------------------------------------------

--------------  WORKFLOW process -------------------------------------------------------------------------------------------------
- Workflow starts
- Do the work that needs to do..
- Waits X time
- Calls itself as again (This starts a new iteration)..
- Workflow ends..
--------------------------------------------------------------------------------------------------------------------------------------------------

Also you can manage it using a plugin that triggers on the pre-create of i.e. Appointment.

I hope this helps!