Showing posts with label unsupported. Show all posts
Showing posts with label unsupported. Show all posts


Remove attributes from form which cannot be removed

There are some attributes on CRM default forms which you cannot remove. This post will show you how you can do this anyway. While doing so, please keep in mind that Microsoft must have good reasons to fix attributes on the form. Removing this might have implications, as always have a good backup ready.

The steps you need to follow to remove are:
- Export customizations for the specific entity
- Modify the form in the customizations xml
- Import the modified customizations

Since most of you are well known with CRM I will skip the Export and Import of customizations. If you need assistance with this, then don't try this change anyway :) To modify the customizations, open the file in notepad or another XML editor. In this file look for this path:

ImportExportXml - Entities - Entity - FormXml - forms - entity - form - tabs

Within tabs look for the tab with the correct name and similar for the sections. Get a bit familiar with the section xml once you have found the correct section. You'll find that there are a couple of lines that you need to remove. This is the example for the subjectid on the case entity:


<cell id="{a9859c32-0cdc-41b5-8e7e-3eb173cab4a8}">
<labels>
<label description="Onderwerp" languagecode="1043" />
<label description="Subject" languagecode="1033" />
</labels>
<control id="subjectid" classid="{270BD3DB-D9AF-4782-9025-509E298DEC0A}" datafieldname="subjectid" />
</cell>


Attributes which are locked on the form includes the following:
case - subjectid
case - contractid
case - contractdetailid

Note: Some of the attributes are used in hidden javascript codes. For instance the case contract contractid is used in the script for the customer onchange. You can also look at removing those scripts from the customizations file.



Get rid of "Do you want to close this window?"

You might get a question when opening CRM in Internet Explorer. The question is:


The webpage you are viewing is trying to close the window.
Do you want to close this window?
yes no

This message does appear in CRM 4.0 only when you are using Internet Explorer 7.0 and you have enabled the application mode setting. Nevertheless, it is an anoying message which you can get away!

To get rid of this message open the default.aspx file which resides in the root of the CRM website. In this file there are these three lines of code:

var oMe = window.self;
oMe.opener = window.self;
oMe.close();

Modify the second line of this snippet and end up with these three lines:

var oMe = window.self;
oMe.open('','_self','');
oMe.close();

You now will not have the message anymore. Keep in mind that any update or migration might remove this change, but you should be able to reapply the change easily again.



Determine the logged in user

Some time ago I wrote a post around how to show and hide fields based on the logged in user. Apparently this doesn't work for Dynamics CRM 4.0 anymore for two reasons. This post will help you to get it working again.

The first one is that you are not allowed to use the <%= approach anymore. You do now have to use the function:

Page.Response.Write(string strText);

The second reason is that the variable "Microsoft.Crm.Security.User.Current.UserAuth.UserId" does not exist anymore. Microsoft has changed the internal structure of how they work with the current user. You now should use this "Microsoft.Crm.Security.User.Current.SystemUserId.ToString()". These two changes lead to the following code

<script language="javascript">
var loggedInUser = '<%Page.Response.Write(Microsoft.Crm.Security.User.Current.SystemUserId.ToString())%>';
</script>

If you do have a single mistake in the aspx page you are changing, then you'll get the following error:

The VirtualPathProvider returned a VirtualFile object with VirtualPath set to '/MicrosoftCRM/sfa/accts/edit.aspx' instead of the expected '//MicrosoftCRM/sfa/accts/edit.aspx'

This has nothing to do with the VirtualPathProvider, it's just that you have a mistake in your aspx page.

Good luck!



Stopping and continuing a save event

For some customizations you do need to stop the form onsave event, perform some business logic and continue the save event. An example would be that in specific conditions are met when a record is saved, then a popup will need to be shown. After filling in data in the popup and pressing a continue button on the popup, then the save operation would need to continue. The SDK helps in this situation. Look at the following page for more information. MSDN SDK: OnSave Event

On that page you will see that you can stop the onsave event by setting the 'event.returnValue = false'. Don't forget to follow that line with a 'return false'. This will cause the save procedure to stop right at that point. Otherwise statements after that will still be executed.

To call the save event from your javascript code, you can use the javascript functions crmForm.Save(); and crmForm.SaveAndClose(). By looking at the 'event.Mode' you can determine which event was executed before. If the code is 1, then it is a crmForm.Save(); or it is 2 for a crmForm.SaveAndClose(). There's only one small issue. There can be other save events as well. There's the 'save and new', 'save as completed', and also the 'send' for emails. Below is a list of save events with the corresponding javascript functions to call. Once again, this is not documented in the SDK, so this might change with a hotfix or new version.


Save
Code: 1
Function: crmForm.Save();


SaveAndClose
Code: 2
Function: crmForm.SaveAndClose();


Send
Code: 7
Function: send();


SaveAsCompleted
Code: 58
Function: SaveAsCompleted();


SaveAndNew
Code: 59
Function: crmForm.SubmitCrmForm(59, true, true, false);

Good luck!



Change requirement level at runtime

I've made quite some posts around how to dynamically modify the form by using JavaScript. One thing I haven't posted around yet, but what I do use quite often, is to modify the requirement level of attributes on the form at runtime. Based on a selection in the system, another field could become required. Also if the selection changes again, then the field should not be required anymore. Here is the code for making a field optional, recommended or required:


// Set field to not required
crmForm.SetFieldReqLevel("fieldname", 0);

// Set field to business required
crmForm.SetFieldReqLevel("fieldname", 1);

// Set field to business recommended
crmForm.SetFieldReqLevel("fieldname", 2);


Update: new code placed based on the comment by Peter. Before the update I had this piece of code:


// set the field required (i.e. show error message when field is not filled in)
crmForm.all.fieldname.req = 2;

// modify the label to be red
crmForm.all.fieldname_c.className = 'req';



Add New Button on Lookup

Hi Guys,

I’ve been looking into how to enable the ‘new’ button on custom entities. Here is what I have found. O yeah, For the direct solution scroll to the end of the mail.

- You can set test some things when using this url:
http://localhost:5555/_controls/lookup/lookupsingle.aspx?class=null&objecttypes=10001&browse=0&DefaultType=0&ShowNewButton=1&ShowPropButton=1
Ofcourse modify the servername and the objecttypes to match your situation

- The properties and new button can be set by using the querystring parameters as given above.

- There is one additional piece of hard coding in the class Microsoft.Crm.Web.Controls.Lookup.LookupPageBase. This particular piece of code is this:


if (this._showNewButton)
{
for (int j = 0; j < this._objectTypes.Length; j++)
{
switch (int.Parse(this._objectTypes[j], CultureInfo.InvariantCulture))
{
case 1:
this._canCreateAccount = base.CurrentUser.GetPrivilege(Privileges.CreateAccount);
break;

case 2:
this._canCreateContact = base.CurrentUser.GetPrivilege(Privileges.CreateContact);
break;

case 3:
this._canCreateOpportunity = base.CurrentUser.GetPrivilege(Privileges.CreateOpportunity);
break;

case 4:
this._canCreateLead = base.CurrentUser.GetPrivilege(Privileges.CreateLead);
break;

case 0x10cc:
this._canCreateList = base.CurrentUser.GetPrivilege(Privileges.CreateList);
break;
}
}
this._showNewButton = ((this._canCreateAccount || this._canCreateContact) || (this._canCreateLead || this._canCreateOpportunity)) || this._canCreateList;
}

What it does, is looking to the specified list of objecttypecodes and looks if any of these match the id of account, contact, lead, opportunity or (marketing)list. If it does not match one of those, then the new button is hidden.

- You can run the javascript code "createNew();”. This is the same function as that will be executed by the button. Running this javascript will open the quickcreate window which will work.

- You can also modify the file ‘lookupsingle.aspx’. In this file there is the function window.onload() function. Modify this function to add these lines to the end:

//enable new button
btnNew.style.display = 'inline';

This will cause the new button to be visible.

That’s about it. Just add the line above and you’ll be ready to go. The new button will now be available for all entities. I haven’t tested this through so that will need to be done in your situation. Also note that this is unsupported and will not be migrated to a potential upgrade to titan or might be lost after an install of a hotfix.

Hope this helps,

Ronald



Modifying the history (associated) view filter

One of my friends, Michael Höhne, has blogged about how to change the default view of the associated view. This is a very common requested feature, also by my clients. Usually I inform them that it is not possible to change it, but that will change as of today. Take a look at the post at this address be thankful that Michael is willing to share all the codes just for the community :)

http://www.stunnware.com/crm2/topic.aspx?id=JS11



Filter data in a CRM lookup field

Curt Spanburgh posted this message on the Sandbox: Custom Lookup Dialog for Microsoft Dynamics CRM 3.

In this post he describes that it is possible to filter the data in a lookup box. He also gives an example which uses data on the form to filter the lookup data. For testing purposes I have used the code below. It should be placed in the form onload of the account entity. It does filter the lookup parent account and will only show accounts which do have a parent account already. Not necessarily useful in a business case, but it does give you a clear example.


crmForm.all.parentaccountid.lookupbrowse = 1;
crmForm.all.parentaccountid.additionalparams = "fetchXml=<fetch mapping='logical'><entity name='account'><all-attributes/><order attribute='name' descending='false'/><filter type='and'><condition attribute='parentaccountid' operator='not-null'/></filter></entity></fetch>";
crmForm.all.parentaccountid.additionalparams += "&selObjects=1&findValue=0";


As you can see, some extra values are sent to the additionalparams attribute. These are selObjects and findValue. The selObjects should be set to the enity id of the lookup its entity.

To help you create fetchXml queries look here: Using Advanced Find for FetchXml

Great tip Curt!

Update: This does not work in CRM 4.0. Instead, look at the add-on as developed by Stunnware: http://www.stunnware.com/Products/FLD4/Default.htm



Set a default entity on a lookup window

In case you have more entities to chose from on a lookup window, then you might want to be able to set the default for this lookup. For instance, the regarding field on an activity entity like letter, email and fax does support multiple entities. If you want this to default set to contact, then use this javascript on the form onload:


//set default
crmForm.all.regardingobjectid.defaulttype = "2";


Even better, what if you dont want your user to be able to choose any entity and force them to use the contact entity? Then use this script:


//set only contact
crmForm.all.regardingobjectid.lookuptypes = "2";

//set icon
crmForm.all.regardingobjectid.lookuptypeIcons = "/_imgs/ico_16_2.gif";


Dont forget to set the icon. The icon will be displayed in front of the selected name after a selection in the lookup box has been made.

For a custom entity, then mind that the icon does not work that easily. Use this script for custom entities:


//set only contact
crmForm.all.regardingobjectid.lookuptypes = "10003";

//set icon
crmForm.all.regardingobjectid.lookuptypeIcons = "/_imgs/icon.aspx?objectTypeCode=10003&iconType=GridIcon&inProduction=1&cache=1";


Good luck!



Clear BizTalk Messagebox

Since the release of the CRM - BizTalk connector, you're all doing integrations right? Did it happen to you that you start a BizTalk application one evening and in the morning your messagebox is filled with more then 100.000 instances? If that is the case, then you can delete these by using the BizTalk Administration console. Unfortunately this will take hours and hours..

Instead, running this code on the BizTalk databases solves the issue for you:


DELETE FROM BizTalkMsgBoxDb.Instances
GO
DELETE FROM BizTalkMsgBoxDb.InstancesSuspended
GO


Keep in mind that you should only do this on development, test and acceptation, NEVER on production.



Error: Deletion Service failed to clean up some tables

Some people have been experiencing the following error message in the event log:

Event Type: Error
Event Source: MSCRMDeletionService
Event Category: None
Event ID: 16387
Date: Date
Time: Time
AM User: N/A
Computer: Computer_Name
Description:
Error: Deletion Service failed to clean up some tables.

Microsoft has a solution for this message. See http://support.microsoft.com/kb/921391/EN-US/ for their solution.

Personally I don't feel comfortable with the solution they give. They are removing the enforced relationships for the tables which cause the issue. This will leave you with orphaned records in your database and probably the same issue when you upgrade to the upcoming titan release. Here are my steps to correctly fix this issue (partly copied from the MS solution) :

To identify the table where the Deletion Service is failing, follow these steps:
1. On the Microsoft Dynamics CRM server, click Start, click Run, type cmd, and then click OK.
2. At the command prompt, locate the system drive, locate the program files, locate Microsoft Dynamics CRM, locate the server, locate the bin, type crmdeletionservice.exe –runonce, and then press ENTER. You then receive a message that resembles the following message:
Can't clean up the following tables: Campaign

Note The table that is returned in the message is the table for which the Deletion Service failed.

The following example demonstrates how to resolve this problem for the Campaign table.

To resolve the problem for the Campaign table, follow these steps:
1. Run a statement in Microsoft SQL Query Analyzer. To do this, follow these steps:
a. Click Start, point to All Programs, point to Microsoft SQL Server, and then click Query Analyzer.
b. Run the following query against the Organization Name_MSCRM database.
delete from Campaign where DeletionStateCode = 2
This query returns a message that resembles the following message:

DELETE statement conflicted with COLUMN REFERENCE constraint 'campaign_leads'.

2. Run two queries agains the Organization Name_MSCRM database.
delete from [table name in message] where [column name in message] in (select campaignid from Campaign where DeletionStateCode = 2)

delete from Campaign where DeletionStateCode = 2

By doing this, you do delete the associated records as well. Keep in mind that once deleted, you cannot get the data back. Therefore always make a backup first!



Modifying the fullname

The fullname of a contact is always "firstname lastname". Right? Well, did you know that you can modify this? Go to Settings, System Settings. There you can modify this setting to some other options!

Great :) So lets modify the fullname to "lastname, firstname". When saving this setting, CRM warns you that this only works for the new entities and not for the existing entities. If this really is a problem, then you can go into SQL and update the fullname manually. Here's the code to update the contacttable:

UPDATE contactbase SET fullname = ISNULL(lastname, '') + ', ' + ISNULL(firstname, '')

Of course you can update the contactbase with other values for the fullname as well.

Keep in mind that you NEVER update the Contact view, always the contactbase. Updating the contact view will cause problems.

Anyway, you should always make a backup of your database before making any modification to the database.



Add descriptions to relationship view

When working with relationships, you will find out that it would be useful to add the fields "Description 1" and "Description 2" to the associated view. This means that when you're in the account detail page and you view the associated relationships, that you will not only see "Party 1", "Role 1", "Role 2" and "Party 2". The description fields are useful to store additional information, but it would be really useful if you can show this data without the need of a double click on the record.

So Ronald. How do we do this?

Usually I'd say: "go to the settings, customization, relationships, forms and views and then modify the associated view. Now there's only one small problem. There is no forms and views for the relationships entities. There are 2 entities for relationships: Customer Relationship and Opportunity Relationship. In this article I'm using only the Customer Relationship.

Well, if the link is not there, then why not create a link to that specific page? This is the solution to this problem. The link to any view modification page is:
http://localhost:5555/tools/vieweditor/viewManager.aspx?id={guid}. We only need the guid of the view which is used for the associated relationship view. We can find this id by following the next steps:

- find out the guid of any account (open an account and press CTRL+N)
- go to this url (replace the guid with your account guid):
http://localhost:5555/sfa/accts/areas.aspx?oId={60548D20-B7F5-DA11-AAE6-0003FF2689B7}&oType=1&security=4294500000&tabSet=areaRelationships
- view the source of the page
- search for "viewid"

The value of this attribute is the view guid which you should use in the url http://localhost:5555/tools/vieweditor/viewManager.aspx?id={guid}. Ofcourse you'll need to modify the port number if you have used another port.

Now you can modify the view as desired, for instance add the fields "description 1" and "description 2" to the view. After saving the modifications, you will need to publish the entity "Customer Relationship". And you're done!

Update September 4th 2007:
The article describes how to do this for the customerrelationship. If you need to do this for the opportunity relationship, then follow this url:
http://localhost:5555/sfa/opps/areas.aspx?oId={60548D20-B7F5-DA11-AAE6-0003FF2689B7}&oType=3&security=852407&tabSet=areaRelationship



Finally there: Show and hide fields based on the users role!

Hi all,

I've been posting around showing and hiding fields more often. I won't be talking about that now, instead I'll be discussing the ability to perform JavaScript coding based on the users role.

This solution is very neat and very clean, though unsupported!!! Now... Let's get started and dig into this.

MS CRM has a lot of Javascript codes delivered with the product. One of these codes is located here: "/_controls/RemoteCommands/RemoteCommand.js". This file is being included into every page. One of the functions in this file is RemoteCommand(sObject, sCommand, sUrlBase). You can use this function to connect to CRM webservices.

Via Javascript you can get the userid of the currently logged in user via this RemoteCommand. This is done using this code:


var command = new RemoteCommand("SystemUser", "WhoAmI", "/MSCRMServices/");
var oResult = command.Execute();


Another webservice which is interesting is the UserManager webservice. By using this function, you can get all roles of the system. The roles to which the user is assigned are marked with "checked='true'". Here's the code to get this list


var command = new RemoteCommand("UserManager", "GetUserRoles");
command.SetParameter("userIds", "<guid>" + userId + "</guid>");
var oResult = command.Execute();


Now if we add this functionality to functions and add some try catches, then you'll end up with this code:


function getUserId()
{
try
{
var command = new RemoteCommand("SystemUser", "WhoAmI", "/MSCRMServices/");
var oResult = command.Execute();

if (oResult.Success)
{
return oResult.ReturnValue.UserId;
}
}
catch(e)
{
alert("Error while retrieving userid.");
}
return null;
}

function getUserRoles(userId)
{
try
{
var command = new RemoteCommand("UserManager", "GetUserRoles");
command.SetParameter("userIds", "<guid>" + userId + "</guid>");

var oResult = command.Execute();

if (oResult.Success)
{
return oResult.ReturnValue;
}
}
catch(e)
{
alert("Error while retrieving roles.");
}
return null;
}


Now we only have to add functions which checks if the user has a specific role. The final code is:


function getUserId()
{
try
{
var command = new RemoteCommand("SystemUser", "WhoAmI", "/MSCRMServices/");
var oResult = command.Execute();

if (oResult.Success)
{
return oResult.ReturnValue.UserId;
}
}
catch(e)
{
alert("Error while retrieving userid.");
}
return null;
}

function getUserRoles(userId)
{
try
{
var command = new RemoteCommand("UserManager", "GetUserRoles");
command.SetParameter("userIds", "<guid>" + userId + "</guid>");

var oResult = command.Execute();

if (oResult.Success)
{
return oResult.ReturnValue;
}
}
catch(e)
{
alert("Error while retrieving roles.");
}
return null;
}

function userHasRole(userId, roleName)
{
result = getUserRoles(userId);
if (result != null)
{
var oXml = new ActiveXObject("Microsoft.XMLDOM");
oXml.resolveExternals = false;
oXml.async = false;
oXml.loadXML(result);

roleNode = oXml.selectSingleNode("/roles/role[name='" + roleName + "']");
if (roleNode != null)
{
if (roleNode.selectSingleNode("roleid[@checked='true']") != null)
return true;
}
}

return false;
}

function currentUserHasRole(roleName)
{
userId = getUserId();
return userHasRole(userId, roleName);
}


Now you can check for roles by using the code listed down here:

if(currentUserHasRole('Salesperson')){
alert('true');
}else{
alert('false');
}


You can copy and paste the functions as well as the last code in the form onload event. This is not really user friendly / readable though. If you copy and paste the functions to the global.js script instead, then you can use the last code snippet in the form onload.

Now. To get back to the subject "show and hide fields based on the users role"... If you modify the last code snippet to contain the showing and hiding fields as discussed in this article, instead of alert('true') and alert('false'), then you've got a very good customized system.

Thanks go out to Steven Brom (Qurius Advanced Solutions - NL) for supplying the codes!

Update 1: Thanks to Josh Painter who pointed me to the fact that I forgot to replace the < sign with <> in the codes.

Update 2: I mentioned that this solution was supported, but since we're using webservices from MS CRM other then the regular webservice, this is unsupported anyway... But still it's cool ;)

Update 3: This code doens't work on the outlook client. I'm still thinking about a solution to that.

Update 4: The code above is created for CRM 3.0. For CRM 4.0 look at the following blogs:
http://www.crowehorwath.com/cs/blogs/crm/archive/2008/05/08/hide-show-fields-in-crm-4-0-based-on-security-role.aspx
http://jianwang.blogspot.com/2008/01/crm-40-check-current-users-security.html



Hide and show fields based on the logged in user

There have been a lot of requests on how to hide and show fields since my earlier postings regarding hiding and showing fields and rows. John Straumann has written an article about this before. A working solution, but not too nice though.

Here's another solution which is unsupported (ofcourse ;) ), but it gives you a lot more freedom.

So how to tackle this problem. We already know how to hide and show fields. Now wouldn't it be great if we know what user is logged in? We would then be able to say (ofcourse modify the loggedInUser id with the guid of the user you need):


if (loggedInUser == '{123456789-1234-5678-9012-123456789012}'{
crmForm.all.name_c.style.display = 'none';
crmForm.all.name_d.style.display = 'none';
} else{
crmForm.all.name_c.style.display = 'block';
crmForm.all.name_d.style.display = 'block';
}

Now... how do we get that loggedInUser?
This is where it gets quite tricky. You can open the aspx page where you want to use this code. So why not add a little bit of asp coding in there? Lets say you want to use the code on the contact page. Then go to the server and open the "/SFA/conts/edit.aspx" page. Add this piece of code just before another script starts:

<script language="javascript">
var loggedInUser = '<%=Microsoft.Crm.Security.User.Current.UserAuth.UserId%>';
</script>


And now we have the loggedInUser! Save the file, store the javascript in the page onload and publish the contact. You will now have a page special for one user.
You can extend the codes as much as you like. You con for instance change the
Microsoft.Crm.Security.User.Current.UserAuth.UserId
with
Microsoft.Crm.Security.User.Current.IsSysAdmin
.
This all is undocumented and unsupported. Therefore you should only try these kind of modifications if you feel comfortable with this.

Good luck!



Getting inside MS CRM

If you think that solutions which are on this blog are unsupported, then take a look at the post which my new German colleague Arne Janning has posted:

Getting inside Microsoft CRM - Part I

He'll get you virtually inside crm and from there you will be able to do anything that you have ever wanted to do with MS CRM.

I'll have to warn you: if you don't feel comfortable with what you see, then just don't try this. You'll likely be messing up your (clients?) system :-)



Hide a form field

Now that each field offers an OnChange event, we want to use it. At least I do. Based on the selection or values in another field, I'm disabling fields all over the screen. But why disabling and not hide them?! It's not documented and therefore not supported, but it sometimes is needed. The code to hide a field is:


crmForm.all.name_c.style.visibility = 'hidden';
crmForm.all.name_d.style.visibility = 'hidden';

Ofcourse modify the name_c and name_d to the correct fieldname_c and fieldname_d.

Happy hiding!

Update: Thanks to Robert Amos for an even better way to hide the fields.



Override save method

!unsupported alert!

Unsupported, but this works :)

I do sometimes get the question how it is possible to override the save method. You could dig into the javascript files or htc files, but following this solution is a lot better.

You can modify the aspx file, which you want to change the behaviour of the save button. Insert this script into the file and you will get an alert before saving the file:


<script language="javascript">
//make the form aware of our new save method
function window.onload(){
crmForm.onsave = overriddenSave;
crmForm._bUseCustomSaveEvent = true;
}

//implementation of the new save method
function overriddenSave(){
//do your thing here
alert('Your modifications will be saved now');

//save the form.
crmForm.SubmitCrmForm(1, true, false, false, false);
}
</script>


Hope this helps!