Recently when a user was trying to use set regarding for a tracked email they were receiving the following error.
'You do not have enough privileges to acces the Micrsoft Dynamics CRM object or perform the requested opeartion.'
When running the CRM trace we found this error 'SecLib::AccessCheckEx failed. Returned hr = -2147187962, ObjectID: b043cc71-a644-e611-80d7-005056a076e4, OwnerId: 9d589563-f988-dd11-8b66-0013211ccf00, OwnerIdType: 8 and CallingUser: e2619700-963c-e111-9119-005056932219. ObjectTypeCode: 4200, objectBusinessUnitId: c7ec9cc6-9e67-db11-97e6-0013211cbfff, AccessRights: WriteAccess'
It turns out CRM 2016 sets the owner automatically based off who sent the email rather than who created the tracked record. We had a disabled user in MSCRM in a different business unit that when ever another user tried to track an email from them it would set the disabled user as the owner! Reassigning the email fixed the issue and I found a few more that were problematic using advanced find.
Tuesday, 12 July 2016
CRM 2016 Can't track an email - missing prvReadConvertRule privilege
Recently we found an intermittant new issue with tracking email messages for some users. When tracing with outlook we found the error 'missing prvReadConvertRule privilege'. This didn't seem to directly translate to any role found configurable in CRM.
Using a bit of SQL I managed to find the roles that had that privilege set and then started to compare with the role I was hoping to use and found out that I needed to set organisational read for Record Creation and Update Rule under the Service Management tab.
If you are interested here is the query I used to find the roles with the correct privileges set.
SELECT r.Name FROM Role r WHERE EXISTS(SELECT 1 FROM RolePrivileges rp JOIN Privilege p ON p.PrivilegeId = rp.PrivilegeId WHERE r.RoleId = rp.RoleId AND p.Name = 'prvReadConvertRule')
Using a bit of SQL I managed to find the roles that had that privilege set and then started to compare with the role I was hoping to use and found out that I needed to set organisational read for Record Creation and Update Rule under the Service Management tab.
If you are interested here is the query I used to find the roles with the correct privileges set.
SELECT r.Name FROM Role r WHERE EXISTS(SELECT 1 FROM RolePrivileges rp JOIN Privilege p ON p.PrivilegeId = rp.PrivilegeId WHERE r.RoleId = rp.RoleId AND p.Name = 'prvReadConvertRule')
Tuesday, 10 May 2016
3 Bugs in CRM 2016
In our CRM 2016 upgrade we have uncovered 3 bugs.
1) Unable to go offline, error relating to generating BCP file.
In our database which is an older install of CRM we have a timestamp around the 8 000 000 000 mark.
When creating the BCP header file it tries to convert that timestamp to a 32 bit integer which creates an overflow exception. It seems older versions of CRM ignored this error and continued anyway.
Microsoft has now released a hotfix for this which now handles 64bit timestamps.
2) IFrames stripping out parameters relating to reporting services parameters when set via JS or form designer with Pass record type code etc ticked.
The only way around this bug so far that we have found is to upload the report into CRM and using JS set a relative URL!
3) Cannot find X method when deactivating a record
It seems when you deactivate a record it can try to call methods from a previously viewed form in the onload, the work around was to include the library from the other form in a form you are likely to be using in sequence, for example contracts and contract lines.
1) Unable to go offline, error relating to generating BCP file.
In our database which is an older install of CRM we have a timestamp around the 8 000 000 000 mark.
When creating the BCP header file it tries to convert that timestamp to a 32 bit integer which creates an overflow exception. It seems older versions of CRM ignored this error and continued anyway.
Microsoft has now released a hotfix for this which now handles 64bit timestamps.
2) IFrames stripping out parameters relating to reporting services parameters when set via JS or form designer with Pass record type code etc ticked.
The only way around this bug so far that we have found is to upload the report into CRM and using JS set a relative URL!
3) Cannot find X method when deactivating a record
It seems when you deactivate a record it can try to call methods from a previously viewed form in the onload, the work around was to include the library from the other form in a form you are likely to be using in sequence, for example contracts and contract lines.
Friday, 22 April 2016
CRM 2016 Update Profile Picture
Information on this seems to be fairly limited at the moment, but this is cool feature.
1. Double click your image at the top right of the CRM 2016 screen.
2. Select Choose File
3. Pick your picture and click open
1. Double click your image at the top right of the CRM 2016 screen.
2. Select Choose File
3. Pick your picture and click open
4. Click OK
Your profile picture should now be updated!
Tuesday, 5 April 2016
Unable to add user to CRM 2016
Recently I encountered an issue where I couldn't add a user as CRM determined that the user ID already existed in the organisation, however I could see no evidence of this.
After a quick search I found this article
https://blogs.msdn.microsoft.com/ukdynsupport/2009/07/16/authentication-when-you-are-adding-a-new-user-to-crm-or-a-user-to-a-new-organization-in-crm/
So the key was the user record was in the MSCRM_CONFIG database still, as we had done a restore and org import on an earlier backup.
To identify this was actually the cause I had to find the SID for the user, without access to the AD tools, I found an article on using powershell, the following commands got me the SID
After a quick search I found this article
https://blogs.msdn.microsoft.com/ukdynsupport/2009/07/16/authentication-when-you-are-adding-a-new-user-to-crm-or-a-user-to-a-new-organization-in-crm/
So the key was the user record was in the MSCRM_CONFIG database still, as we had done a restore and org import on an earlier backup.
To identify this was actually the cause I had to find the SID for the user, without access to the AD tools, I found an article on using powershell, the following commands got me the SID
PS> $objUser = New-Object System.Security.Principal.NTAccount("{domain name}")With the returned SID I could identify the record
PS> $strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
PS> $strSID.Value
S-1-5-21-1004336348-1715567821-725345543-40705
SELECT * FROM SystemUserAuthentication WHERE AuthInfo LIKE '%S-1-5-21-1004336348-1715567821-725345543-40705'I decided to update the record SID to one that will never exist rather than delete, as there could be a spider web of relationships that get really stuffed up.
UPDATE SystemUserAuthentication
SET AuthInfo = 'W:S-1-5-21-1004336348-1715567822-725345543-40705'
WHERE Id = 'CF775160-6BE6-E511-80CF-005056BD0631'After this update I was able to create the user record.
Wednesday, 2 March 2016
Unable to distribute Campaign Activities, Error retrieving next number (currentbulkoperationnumber) ..
In a CRM system we were having trouble distributing campaign activities, we would always get the following error 'Error retrieving next number (currentbulkoperationnumber) for organization {e14513f2-5cdb-4194-9ca1-52d04cd0b077): return value is empty'. No matter what entity was in the marketing list the distribute would fail after choosing the owner. What was equally perplexing new organisatios wouldn't have this issue.
We started getting this error in CRM 2011, my hope was upgrading to CRM 2016 would resolve this error, unfortunately not.
After doing a SQL trace on the SQL server we managed to find 1 SQL query which looked suspect.
The CurrentBulkOperationNumber field in our OrganizationBase table was currently set to NULL, updating this to 1000 resolved the issue.
This does make me wonder what other data issues are lurking in our database, that we just haven't noticed yet.
We started getting this error in CRM 2011, my hope was upgrading to CRM 2016 would resolve this error, unfortunately not.
After doing a SQL trace on the SQL server we managed to find 1 SQL query which looked suspect.
exec sp_executesql N'declare @currentval intupdate OrganizationBase set @currentval = CurrentBulkOperationNumber, CurrentBulkOperationNumber = CurrentBulkOperationNumber + 1 where OrganizationId = @orgidselect @currentval',N'@orgid uniqueidentifier',@orgid='E14513F2-5CDB-4194-9CA1-52D04CD0B077'
The CurrentBulkOperationNumber field in our OrganizationBase table was currently set to NULL, updating this to 1000 resolved the issue.
UPDATE OrganizationBase SET CurrentBulkOperationNumber = 1000
This does make me wonder what other data issues are lurking in our database, that we just haven't noticed yet.
Wednesday, 2 December 2015
CRM 2011 Plugin Assembly not updating with new code
With CRM 2011, recently had a really odd situation, deploying the CRM 2011 plugin assembly wasn't overwriting the old code in the CRM database. Even after I tried de-registering the assembly and redeploying the old code was still used.
How I fixed this was incrementing the assembly version, it then updated the plugin assembly properly in the database.
How I fixed this was incrementing the assembly version, it then updated the plugin assembly properly in the database.
CRM 2011 - Ghost Dependencies
Long time since the last post sorry, but figured I would post about something odd I found in our CRM 2011 environment here.
When we were trying to delete an entity we were getting a message that we couldn't delete the entity due to dependencies showing a blank list!
After a lot of searching we found the error to be related to some orphaned records in the ReportEntityBase table that were set as a managed solution component.
I first attempted to remove this the supported way to no avail so had to come up with a 'hack' to get them out of the database. I have no idea how you would do this with Cloud CRM.
First step was to backup the data about to be removed, just in case we had some downstream impact.
--Back the Dependencies, nodes and offending records
Next delete out the dependency records pointing to the problem records
Now delete the joining node records
When we were trying to delete an entity we were getting a message that we couldn't delete the entity due to dependencies showing a blank list!
After a lot of searching we found the error to be related to some orphaned records in the ReportEntityBase table that were set as a managed solution component.
I first attempted to remove this the supported way to no avail so had to come up with a 'hack' to get them out of the database. I have no idea how you would do this with Cloud CRM.
First step was to backup the data about to be removed, just in case we had some downstream impact.
--Back the Dependencies, nodes and offending records
SELECT db.* INTO DependencyBase_backup FROM DependencyBase dbJOIN DependencyNodeBase dnb ON dnb.DependencyNodeId = db.DependentComponentNodeIdJOIN ReportEntity rpe ON rpe.ReportEntityId = dnb.ObjectIdWHERE ComponentType = 32 and rpe.ObjectTypeCode = 10001SELECT dnb.* INTO DependencyNodeBase_backup FROM DependencyNodeBase dnbJOIN ReportEntity rpe ON dnb.ObjectId = rpe.ReportEntityIdWHERE ComponentType = 32 and rpe.ObjectTypeCode = 10001SELECT * INTO ReportEntityBase_backup FROM ReportEntityBase WHERE ObjectTypeCode = 10001
Next delete out the dependency records pointing to the problem records
DELETE db FROM DependencyBase db
JOIN DependencyNodeBase dnb ON dnb.DependencyNodeId = db.DependentComponentNodeId
JOIN ReportEntity rpe ON rpe.ReportEntityId = dnb.ObjectId
WHERE ComponentType = 32 and rpe.ObjectTypeCode = 10001
Now delete the joining node records
DELETE dnb FROM DependencyNodeBase dnb
JOIN ReportEntity rpe ON dnb.ObjectId = rpe.ReportEntityId
WHERE ComponentType = 32 and rpe.ObjectTypeCode = 10001Delete the Orphaned records
DELETE FROM ReportEntityBase WHERE ObjectTypeCode = 10001
Friday, 7 June 2013
CRM 2011 HTTPS Endpoint with SSIS and ADFS
After quite a bit of work, I finally managed to get a connection working with the CRM 2011 Https endpoint, using no config file, below is the code I used to achieve this.
private static IOrganizationService GetHttpsCRMService(string serverUrl, string adfsUrl, string username, string password){Uri organizationUriIFD = new Uri(serverUrl + "/XRMServices/2011/Organization.svc");EndpointAddress endpointAddress = new EndpointAddress(organizationUriIFD);CustomBinding customBinding = new CustomBinding();customBinding.Name = "CustomBinding";var tsBE = new TransportSecurityBindingElement();tsBE.AllowInsecureTransport = false;tsBE.IncludeTimestamp = true;EndpointAddress usernameMixed = new EndpointAddress(adfsUrl + "/adfs/services/trust/13/usernamemixed");WS2007HttpBinding wsbinding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);wsbinding.MaxReceivedMessageSize = 65536;wsbinding.MaxBufferPoolSize = 524288;wsbinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;wsbinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;wsbinding.Security.Message.EstablishSecurityContext = false;IssuedSecurityTokenParameters stP = new IssuedSecurityTokenParameters(null, usernameMixed, wsbinding);stP.IssuerMetadataAddress = new EndpointAddress(new Uri(adfsUrl + "/adfs/services/trust/mex"));var additionalRquestParameters =@"<trust:SecondaryParameters xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>" +@" <trust:KeyType xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</trust:KeyType>" +@" <trust:KeySize xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>256</trust:KeySize>" +@" <trust:Claims Dialect='http://schemas.xmlsoap.org/ws/2005/05/identity' xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>" +@" <wsid:ClaimType Uri='http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn' xmlns:wsid='http://schemas.xmlsoap.org/ws/2005/05/identity' />" +@" </trust:Claims>" +@" <trust:KeyWrapAlgorithm xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p</trust:KeyWrapAlgorithm>" +@" <trust:EncryptWith xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptWith>" +@" <trust:SignWith xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://www.w3.org/2000/09/xmldsig#hmac-sha1</trust:SignWith>" +@" <trust:CanonicalizationAlgorithm xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://www.w3.org/2001/10/xml-exc-c14n#</trust:CanonicalizationAlgorithm>" +@" <trust:EncryptionAlgorithm xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptionAlgorithm>" +@"</trust:SecondaryParameters>";System.Xml.XmlDocument additionalParamDoc = new System.Xml.XmlDocument();additionalParamDoc.LoadXml(additionalRquestParameters);stP.AdditionalRequestParameters.Add(additionalParamDoc.DocumentElement);stP.RequireDerivedKeys = false;stP.KeySize = 256;stP.KeyType = System.IdentityModel.Tokens.SecurityKeyType.SymmetricKey;tsBE.LocalClientSettings.DetectReplays = false;tsBE.LocalServiceSettings.DetectReplays = false;tsBE.EndpointSupportingTokenParameters.Endorsing.Add(stP);customBinding.Elements.Add(tsBE);var textEncoding = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();var httpsTransport = new System.ServiceModel.Channels.HttpsTransportBindingElement();customBinding.Elements.Add(textEncoding);customBinding.Elements.Add(httpsTransport);customBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);customBinding.SendTimeout = new TimeSpan(0, 1, 0);customBinding.OpenTimeout = new TimeSpan(0, 1, 0);customBinding.CloseTimeout = new TimeSpan(0, 1, 0);var contract = new System.ServiceModel.Description.ContractDescription("IOraganizationService", "http://schemas.microsoft.com/xrm/2011/Contracts/Services");contract.ContractType = typeof(IOrganizationService);OrganizationServiceClient osClient = new OrganizationServiceClient(customBinding, endpointAddress);osClient.ClientCredentials.UserName.UserName = username;osClient.ClientCredentials.UserName.Password = password;return (IOrganizationService)osClient;}
Tuesday, 5 March 2013
CRM 2011 Date Only Issue Plugin
As explained quite well in Kelvin's blog, CRM 2011 handles Date Only fields in a strange way. So I have developed a plugin that will get round this.
The code for this is below and it should be deployed pre validation for the entities you wish to use it with, create or update.
/// <summary>
/// Fix for getting round the timezone issues with date only fields
/// </summary>
public class TimeZoneFix : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
//Get an instance of the helper class.
PluginHelper helper = new PluginHelper(serviceProvider);
helper.tracer.Trace("Loaded Helper");
if (helper.targetEntity.Attributes.Any(a => a.Value != null && a.Value.GetType() == typeof(DateTime)))
{
IOrganizationService service = helper.service;
helper.tracer.Trace("Building Request");
RetrieveEntityRequest request = new RetrieveEntityRequest();
request.LogicalName = helper.context.PrimaryEntityName;
request.EntityFilters = EntityFilters.Attributes;
RetrieveEntityResponse response = (RetrieveEntityResponse)service.Execute(request);
if (response != null && response.EntityMetadata != null)
{
List<KeyValuePair<string, object>> attribs = helper.targetEntity.Attributes.Where(a => a.Value != null && a.Value.GetType() == typeof(DateTime)).ToList();
foreach (var att in attribs)
{
DateTime date = (DateTime)helper.targetEntity[att.Key];
DateTime localDate = date.ToLocalTime();
TimeSpan dateDiff = localDate - date;
//Get the attribute metadata
AttributeMetadata meta = response.EntityMetadata.Attributes.FirstOrDefault(am => am.LogicalName.ToLower().Equals(att.Key));
//Check in the attribute metadata if the attribute is a date only.
if (meta != null && meta.AttributeType != null &&
meta.AttributeType == AttributeTypeCode.DateTime &&
((DateTimeAttributeMetadata)meta).Format == DateTimeFormat.DateOnly)
{
helper.targetEntity[att.Key] = localDate.AddHours(dateDiff.Hours + 10);
}
}
}
}
}
}
The code for this is below and it should be deployed pre validation for the entities you wish to use it with, create or update.
/// <summary>
/// Fix for getting round the timezone issues with date only fields
/// </summary>
public class TimeZoneFix : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
//Get an instance of the helper class.
PluginHelper helper = new PluginHelper(serviceProvider);
helper.tracer.Trace("Loaded Helper");
if (helper.targetEntity.Attributes.Any(a => a.Value != null && a.Value.GetType() == typeof(DateTime)))
{
IOrganizationService service = helper.service;
helper.tracer.Trace("Building Request");
RetrieveEntityRequest request = new RetrieveEntityRequest();
request.LogicalName = helper.context.PrimaryEntityName;
request.EntityFilters = EntityFilters.Attributes;
RetrieveEntityResponse response = (RetrieveEntityResponse)service.Execute(request);
if (response != null && response.EntityMetadata != null)
{
List<KeyValuePair<string, object>> attribs = helper.targetEntity.Attributes.Where(a => a.Value != null && a.Value.GetType() == typeof(DateTime)).ToList();
foreach (var att in attribs)
{
DateTime date = (DateTime)helper.targetEntity[att.Key];
DateTime localDate = date.ToLocalTime();
TimeSpan dateDiff = localDate - date;
//Get the attribute metadata
AttributeMetadata meta = response.EntityMetadata.Attributes.FirstOrDefault(am => am.LogicalName.ToLower().Equals(att.Key));
//Check in the attribute metadata if the attribute is a date only.
if (meta != null && meta.AttributeType != null &&
meta.AttributeType == AttributeTypeCode.DateTime &&
((DateTimeAttributeMetadata)meta).Format == DateTimeFormat.DateOnly)
{
helper.targetEntity[att.Key] = localDate.AddHours(dateDiff.Hours + 10);
}
}
}
}
}
}
Friday, 21 December 2012
AX Dynamics Connector
I recently hit this random error with the Microsoft AX Dynamics connector when try to configure the CRM org.
The following exception occurred while attempting to retrieve all companies: Could not load type 'Microsoft.Xrm.Sdk.Client.IServiceManagement`1' from assembly 'Microsoft.Xrm.Sdk, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
All I did to fix it was move the Microsoft.Xrm.Sdk.dll from the C:\Program Files (x86)\Microsoft Dynamics\Microsoft Dynamics Adapter\Adapters\Microsoft.Dynamics.Integration.Adapters.Crm2011 folder and copy it into the root folder for the dynamics adapter install.
It seems the installer may have not moved that required file and the configuration wizard is from within that folder.
The following exception occurred while attempting to retrieve all companies: Could not load type 'Microsoft.Xrm.Sdk.Client.IServiceManagement`1' from assembly 'Microsoft.Xrm.Sdk, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
All I did to fix it was move the Microsoft.Xrm.Sdk.dll from the C:\Program Files (x86)\Microsoft Dynamics\Microsoft Dynamics Adapter\Adapters\Microsoft.Dynamics.Integration.Adapters.Crm2011 folder and copy it into the root folder for the dynamics adapter install.
It seems the installer may have not moved that required file and the configuration wizard is from within that folder.
Monday, 10 September 2012
Tech Ed 2012
It has been a long time since my last post, anyhow currently I'm at Tech Ed and I figured I would blog about a cool session I attended regarding CRM 2011 and tips and tricks.
Gayan Perera from Magnetism ran the session and mentioned a really cool generation tool to replace the crmsvcutil tool from the sdk. It has now been released on code plex and allows generation of code using templating, this is a lot more flexible than the current tool and easier to work with. Check it out here: http://crm2011codegen.codeplex.com/
The other cool information was turning managed solutions into unmanaged solutions using SQL, it makes the org unusable but if done on an import of a copied database the solution can be exported and imported where required.
Kudos to Gayan and Thomas getting this SQL running:
Gayan Perera from Magnetism ran the session and mentioned a really cool generation tool to replace the crmsvcutil tool from the sdk. It has now been released on code plex and allows generation of code using templating, this is a lot more flexible than the current tool and easier to work with. Check it out here: http://crm2011codegen.codeplex.com/
The other cool information was turning managed solutions into unmanaged solutions using SQL, it makes the org unusable but if done on an import of a copied database the solution can be exported and imported where required.
Kudos to Gayan and Thomas getting this SQL running:
declare @solutionId uniqueidentifier, @systemSolutionId uniqueidentifier -- specify the uniquename of the managed solution you'd like to unmanage
select @solutionId = solutionid from SolutionBase where UniqueName='Your Solution Name'
select @systemSolutionId = solutionid from SolutionBase where UniqueName='Active'
declare @sql nvarchar(max)
select @sql ='update PublisherBase set IsReadonly=0 where PublisherId in (select PublisherId from SolutionBase where SolutionId=N'''+cast(@solutionId as nvarchar(100))+''')'
print (@sql)
exec (@sql)
declare @isManagedTables table (id int identity, name nvarchar(100))
declare @count int, @currentTable nvarchar(100), @currentM bit, @currentS bit
-- go through all the tables that have the ismanaged/solutionid flag, find the related records for the current solution and move them to the crm active solution.
insert into @isManagedTables (name)
select name from sysobjects where id in
(select id from syscolumns where name in ('IsManaged'))
and type='U'
order by name
select @count = count(*) from @isManagedTables
while (@count > 0)
begin
select @currentTable =name from @isManagedTables where id=@count
select @sql ='update ' + @currentTable + ' set IsManaged=0 where SolutionId=N''' + cast(@solutionId as nvarchar(100)) + ''''
print (@sql)
exec (@sql)
select @count = @count -1, @currentTable = NULL
end
declare @isSolutionIdTables table (id int identity, name nvarchar(100))
insert into @isSolutionIdTables (name)
select name from sysobjects where id in
(select id from syscolumns where name in ('SolutionId'))
and type='U' and name not in ('SolutionComponentBase') -- ignore this table because it doesn't make a difference. it does cause dependency errors on the exported solution but we can manually edit the xml for that.
order by name
select @count = count(*) from @isSolutionIdTables
while (@count > 0)
begin
select @currentTable =name from @isSolutionIdTables where id=@count
select @sql ='update ' + @currentTable + ' set SolutionId=N''' + cast(@systemSolutionId as nvarchar(100)) + ''' where SolutionId=N''' + cast(@solutionId as nvarchar(100)) + ''''
print (@sql)
exec (@sql)
select @count = @count -1, @currentTable = NULL
end
-- Remove dependencies BaseSolutionId
delete from DependencyBase where DependentComponentNodeId in (
select DependencyNodeId from DependencyNodeBase where BaseSolutionId=@solutionId
)
alter table DependencyBase NOCHECK CONSTRAINT dependencynode_ancestor_dependency
delete from DependencyNodeBase where BaseSolutionId=@solutionId
alter table DependencyBase CHECK CONSTRAINT dependencynode_ancestor_dependency
-- Remove dependencies TopSolutionId
delete from DependencyBase where DependentComponentNodeId in (
select DependencyNodeId from DependencyNodeBase where TopSolutionId=@solutionId
)
alter table DependencyBase NOCHECK CONSTRAINT dependencynode_ancestor_dependency
delete from DependencyNodeBase where TopSolutionId=@solutionId
alter table DependencyBase CHECK CONSTRAINT dependencynode_ancestor_dependency
Saturday, 30 April 2011
Crm 2011 Rollup 1 Subgrids
If you install rollup 1 for CRM 2011 and suddenly notice that subgrids are no longer working for Windows XP machines, ensure that you have 'Enable native XMLHTTP support' turned on in the internet explorer settings.
The specific issue we noticed was just a Grey outline of where the subgrid should be and a js error when closing the form.
The specific issue we noticed was just a Grey outline of where the subgrid should be and a js error when closing the form.
Thursday, 21 April 2011
CRM 2011 advanced mapping now on Codeplex
I have now adding the latest version of the CRM Advanced find mapping to codeplex, check it out http://crmmapping.codeplex.com/
Wednesday, 20 April 2011
CRM 2011, Add mapping to advanced find.
I strongly believe that out of the box features should be used for development where appropriate. My first post will show how to use the features of advanced find to display search results on a map.
If you are wanting the completed version of this just to install I will be putting this on codeplex for free download. Or you can download the managed and unmanaged solutions from below.
http://crmmapping.codeplex.com
First thing that is needed is to add a button to the advanced find ribbon. The following XML copied into an exported customisations.xml file will provide a new Map button in the Show group in the MSCRM.AdvancedFind tab.
The CustomAction element contains the location reference to the advanced find tab show group, as noted by Mscrm.AdvancedFind.Groups.Show.Controls._children.
The Button element references the custom command defined in the CommandDefintions element and specifies the TemplateAlias, without the correct alias defined the button will not display.
The JavaScriptFunction references the method DisplayMap and the library in the webresources called boggle_AdvancedFindSearch.js.
The resulting button looks like this screenshot below:
The next thing that is needed is a way to process the results into a generic array for adding to a map. The following code has been added to a webresource named boggle_AdvancedFindSearch.js.
The method DisplayMap loads the fetchXml from the advanced find form and removes the attribute elements and adds the all-attributes element to ensure address fields are retrieved. The fetchxml is used for a retrievemultiple request by passing the fetchxml to the FetchResultsXml method. The results are then processed using the GetArrayFromFetchResults method to get the results in an easier to use form to be passed onto the map. The map window is then opened using the window.open method, the bing maps window will look at the var resultsArray to get the results retrieved from the advanced find.
The next part we need is an html webresource for displaying the bing maps with the results, here is the code that loads the required elements for the map.
The part that displays the result are in the javascript webresource named boggle_bingMapsJScript.js. This library adds pins to the map based on the addresses in the entities and centres on the last pin. The code for the library is below.
Once all this is implemented a page like the screenshot below will have the results displayed on it.
If you are wanting the completed version of this just to install I will be putting this on codeplex for free download. Or you can download the managed and unmanaged solutions from below.
http://crmmapping.codeplex.com
First thing that is needed is to add a button to the advanced find ribbon. The following XML copied into an exported customisations.xml file will provide a new Map button in the Show group in the MSCRM.AdvancedFind tab.
<RibbonDiffXml>
<CustomActions>
<CustomAction Id="AdvancedFind.OpenMap.CustomAction" Location="Mscrm.AdvancedFind.Groups.Show.Controls._children" Sequence="75">
<CommandUIDefinition>
<Button Id="AdvancedFind.OpenMap.Button.Map" Command="AdvancedFind.OpenMap.Button.Map.Command" LabelText="Map" ToolTipTitle="Map" ToolTipDescription="Open results in map" Image32by32="$webresource:boggle_EarthIcon32" TemplateAlias="o1" Image16by16="$webresource:boggle_EarthIcon16" />
</CommandUIDefinition>
</CustomAction>
</CustomActions>
<Templates>
<RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
</Templates>
<CommandDefinitions>
<CommandDefinition Id="AdvancedFind.OpenMap.Button.Map.Command">
<EnableRules />
<DisplayRules />
<Actions>
<JavaScriptFunction FunctionName="DisplayMap" Library="$webresource:boggle_AdvancedFindSearch.js" />
</Actions>
</CommandDefinition>
</CommandDefinitions>
<RuleDefinitions>
<TabDisplayRules />
<DisplayRules />
<EnableRules />
</RuleDefinitions>
<LocLabels />
</RibbonDiffXml>The CustomAction element contains the location reference to the advanced find tab show group, as noted by Mscrm.AdvancedFind.Groups.Show.Controls._children.
The Button element references the custom command defined in the CommandDefintions element and specifies the TemplateAlias, without the correct alias defined the button will not display.
The JavaScriptFunction references the method DisplayMap and the library in the webresources called boggle_AdvancedFindSearch.js.
The resulting button looks like this screenshot below:
The next thing that is needed is a way to process the results into a generic array for adding to a map. The following code has been added to a webresource named boggle_AdvancedFindSearch.js.
var resultsArray;
function DisplayMap() {
//Get the fetchXml from the advanced find window.
var fetchXml = advFind.FetchXml;
//Create a new XML doc.
var fetchXmlDoc = new ActiveXObject("Microsoft.XMLDOM");
fetchXmlDoc.async = "false";
fetchXmlDoc.loadXML(fetchXml);
//Clear out the existing attribute nodes, we want all attributes
var attribs = fetchXmlDoc.getElementsByTagName("attribute");
for (var i = 0; i < attribs.length; i++)
attribs[i].parentNode.removeChild(attribs[i]);
//Add the all-attributes element if it isn't there
var existingAllAttribs = fetchXmlDoc.getElementsByTagName("all-attributes");
if (existingAllAttribs == null || existingAllAttribs.length == 0) {
var entityElem = fetchXmlDoc.getElementsByTagName("entity")[0];
var allAttribElem = fetchXmlDoc.createElement("all-attributes");
entityElem.insertBefore(allAttribElem, entityElem.childNodes[0]);
}
//Get the results from the crm service
var results = FetchResultsXml(fetchXmlDoc.xml);
//Process the results from the fetch into an array
resultsArray = GetArrayFromFetchResults(results);
//Open the map webresource
var mapWindow = window.open("/WebResources/boggle_bingMapsControl", "mapWindow", "toolbar=0,menubar=0,directories=0");
}
function GetArrayFromFetchResults(fetchResults) {
//Instantiate a new array
var entityResults = new Array();
//Get all the entity elements
var entityElems = fetchResults.getElementsByTagName("a:Entity");
for (var i = 0; i < entityElems.length; i++) {
//Create a new array object to load the results into
entityResults[i] = new Object();
//Get the field values for the object
entityResults[i].Name = GetFieldValue(entityElems[i], "name");
entityResults[i].Street = GetFieldValue(entityElems[i], "address1_line1");
entityResults[i].City = GetFieldValue(entityElems[i], "address1_city");
entityResults[i].Country = GetFieldValue(entityElems[i], "address1_country");
}
//Return the array
return entityResults;
}
function GetFieldValue(entity, fieldName) {
var vals = entity.getElementsByTagName("a:KeyValuePairOfstringanyType");
//Find the field that matches the name
for (var j = 0; j < vals.length; j++) {
if (vals[j].getElementsByTagName("b:key")[0].firstChild.nodeValue == fieldName) {
if (vals[j].getElementsByTagName("b:value").length > 0)
return vals[j].getElementsByTagName("b:value")[0].firstChild.nodeValue;
else
return "";
}
}
}
function EncodeXml(xml) {
return xml.replace(/\&/g, '&' + 'amp;').replace(/</g, '&' + 'lt;')
.replace(/>/g, '&' + 'gt;').replace(/\'/g, '&' + 'apos;').replace(/\"/g, '&' + 'quot;');
}
function FetchResultsXml(fetchXml) {
//Create the fetch Xml request
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
"<soapenv:Body>" +
"<RetrieveMultiple xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<query i:type=\"a:FetchExpression\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\">" +
"<a:Query>" + EncodeXml(fetchXml) + "</a:Query>" +
"</query>" +
"</RetrieveMultiple>" +
"</soapenv:Body>" +
"</soapenv:Envelope>";
var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
xmlHttpRequest.Open("POST", "/XRMServices/2011/Organization.svc/web", false);
xmlHttpRequest.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
//Send the request to CRM
xmlHttpRequest.send(xml);
//Load the results into the xml var
var resultXml = xmlHttpRequest.responseXML;
return resultXml;
}
The method DisplayMap loads the fetchXml from the advanced find form and removes the attribute elements and adds the all-attributes element to ensure address fields are retrieved. The fetchxml is used for a retrievemultiple request by passing the fetchxml to the FetchResultsXml method. The results are then processed using the GetArrayFromFetchResults method to get the results in an easier to use form to be passed onto the map. The map window is then opened using the window.open method, the bing maps window will look at the var resultsArray to get the results retrieved from the advanced find.
The next part we need is an html webresource for displaying the bing maps with the results, here is the code that loads the required elements for the map.
<HTML xmlns:v = "urn:schemas-microsoft-com:vml"><HEAD><TITLE></TITLE>
<META content="text/html; charset=utf-8" http-equiv=Content-Type>
<SCRIPT type=text/javascript src="https://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2&s=1"></SCRIPT>
<SCRIPT src="ClientGlobalContext.js.aspx"></SCRIPT>
<SCRIPT type=text/javascript src="boggle_bingMapsJScript.js"></SCRIPT>
<SCRIPT type=text/javascript src="boggle_AdvancedFindSearch.js"></SCRIPT>
<SCRIPT type=text/javascript src="boggle_bingMapsJScript.js"></SCRIPT>
</HEAD>
<BODY contentEditable=true onload=GetMap();>
<DIV style="POSITION: relative; WIDTH: 100%; HEIGHT: 100%" id=myMap></DIV></BODY></HTML>The part that displays the result are in the javascript webresource named boggle_bingMapsJScript.js. This library adds pins to the map based on the addresses in the entities and centres on the last pin. The code for the library is below.
//Instantiate the variables
var map = null;
var resultsArray = null;//If there is a parent window with the resultsArray variable set the var results array
if (window.opener != null && window.opener.resultsArray != null)
resultsArray = window.opener.resultsArray;
function GetMap() { //Load the map
map = new VEMap('myMap');
map.LoadMap(); //Load the results if there are any
if (resultsArray != null && resultsArray != undefined) {
for (var i = 0; i < resultsArray.length; i++) {
resultsArray[i].id = i;
FindLocation(resultsArray[i]);
}
}
}
function FindLocation(entity) {
if (entity != null && entity != undefined) { var address = entity.Street + ", " + entity.City + ", " + entity.Country;
//Find the address in bing maps
map.Find(null, address, null, null, 0, 1, false, false, true, false, function (shapeLayer, findresults, places, moreresults, error) {
if (error != null && error != "") {
alert(error);
return;
} //Create the pin and setup its values
var pin = new VEShape(VEShapeType.Pushpin, places[0].LatLong);
pin.SetTitle(entity.Name);
pin.SetDescription(address);
map.AddShape(pin);
map.SetCenterAndZoom(places[0].LatLong, 10);
});
}
}Once all this is implemented a page like the screenshot below will have the results displayed on it.
Subscribe to:
Posts (Atom)

