Posts filed under 'Programming'
CRM Calendar Control Bug – DataValue returns incorrect value
While doing some date verification against a CRM date control with JavaScript I came across a bug. When getting values from CRM UI controls we typically use the [Control].DataValue property. What i’ve found is when users use the actual Calendar control to select the date things work fine. However, if users type in a date into the field and you have code that gets the date via the .DataValue or .DataXml property it will NOT return the updated date.
In order to guarantee getting the latest value, I had to go against the raw HTML .value property while using some of CRM’s date format methods.
var dt = new Date();
dt = new Date(ParseDate([CRM_Date_Control_Id].getElementsByTagName("INPUT")[0].value))
Add comment November 16, 2009
Calling WCF service from a CRM Plug-In
I had a requirement to call a WCF service from with a CRM plug-in. This seemed like this should be straight forward, connecting to the service, building a service reference, setting up the config file and I should be good to go.
All was fine and good until working with the config file. When working with a WCF service it expects the configuration properties to be in-process. For instance, if you’re calling a WCF service from within an ASP.NET web site, then adding something like the snippet below to the web.config will allow you to call service.
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IContract" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="9000000" maxStringContentLength="9000000" maxArrayLength="9000000"
maxBytesPerRead="9000000" maxNameTableCharCount="9000000" />
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<!--<message clientCredentialType="UserName" algorithmSuite="Default" />-->
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://[SERVICE_URL]"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IContract"
contract="[CONTRACT]"
name="BasicHttpBinding_IContract" />
</client>
</system.serviceModel>
However, the plug-in is registered in the database so an in-process configuration file wasn’t being recognized. Thankfully the configuration properties are all available via the System.ServiceModel namespace. You can add the code snippet below to setup the configuration when calling the WCF service.
using System.ServiceModel;
…
BasicHttpBinding binding = new BasicHttpBinding();
binding.Name = "BasicHttpBinding_IContract";
binding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
binding.MessageEncoding = WSMessageEncoding.Text;
binding.TransferMode = TransferMode.Buffered;
binding.UseDefaultWebProxy = true;
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
binding.SendTimeout = new TimeSpan(0, 10, 0);
EndpointAddress endPointAddress = new EndpointAddress("http://[SERVICE_URL]");
ContractClient client = new ContractClient(binding, endPointAddress);
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
client.ChannelFactory.Credentials.Windows.ClientCredential = new System.Net.NetworkCredential([ACCOUNT], [PASSWORD], [DOMAIN]);
client.[METHOD]();
Add comment September 29, 2009
Permissions to create Price List
I had a plug-in on an opportunity that among other things assigned a price list based on the currency on the opportunity. Since price list also has a currency on it, it needed to match what’s on the opportunity.
In order for this to work dynamically, I had the plug-in code look for price lists that had a matching currency and if they didn’t exist create a new one. Since our system doesn’t use the product catalog or order system, performing these kinds of operations are safe.
The plug-in impersonates the user via the context.InitiatingUserId
ICrmService service = context.CreateCrmService(context.InitiatingUserId);
This was all fine and good, but after testing with less privileged security roles, I found that they didn’t have the prvCreateProduct privilege, so the plug-in would fail on creation of the price list (pricelevel entity). I had to override the impersonation to use the NETWORK SERVICE account by passing in false to the context.CreateCrmService(false) method in order to get the required privilege.
private Guid CreatePriceList(IPluginExecutionContext context, Guid currencyId)
{
// Need to override the identity of the calling user to SYSTEM, as not everyone has the prvCreateProduct privilege
// required to create the PriceLevel
ICrmService crmService = context.CreateCrmService(false);// Get the name of the currency and use it for the pricelist
// Unfortunately we have to make another query
ColumnSet cols = new ColumnSet();
cols.AddColumn(“currencyname”);transactioncurrency currency = (transactioncurrency)crmService.Retrieve(EntityName.transactioncurrency.ToString(), currencyId, cols);
pricelevel newPriceList = new pricelevel();
newPriceList.name = currency.currencyname + ” Price List”;
newPriceList.transactioncurrencyid = new Lookup(EntityName.transactioncurrency.ToString(), currency.transactioncurrencyid.Value);return crmService.Create(newPriceList);
}
I know I could’ve added the the user to a higher privileged OOTB role like Sales Manager, but shouldn’t this privilege be part of the security roles? If it is perhaps I wasn’t able to find it, does anyone know of a way to add it?
Add comment July 7, 2009
Writing to the Windows Event Log
Writing errors to the Windows event log can be tricky, especially with permissions on custom folders. Here’s a simple method that won’t require special permissions
using System.Diagnostics;
…
EventLog log = new EventLog();
log.Source = “Application”;
log.WriteEntry(“Your Message”, EventLogEntryType.Error);
If you’re capturing web service exceptions, be sure to capture the SoapException and write the Detail xml node.
Add comment July 6, 2009