CRM 2011: Change disabled button tooltip text

Guys, this is a nice one, just finished it! And I am quite happy with it.
When a button is disabled, your users will – and with reason – sometimes wonder why. The most logical place to display the reasons is in the tooltip, at least I think so.

So, how to do that?

1. In the function enabling/disabling the button, prepare a string with the reason(s)

2. Find the button id, using chrome or firefox web developer tools

3. More difficult, find the existing tooltip box id. Not so easy as it disappears as soon as the mouse leaves the button… Simple anyway: with Chrome dev tools open in a separate window, bring that window just under your disabled button, even covering it a little. So that moving the mouse on the button shows the tooltip, and that you can then switch to the dev window with the tooltip still open. In the elements tab, scroll down to the bottom, and watch a nice <span> element show, that looks like below. Copy the id and replace TOOLTIPID with it in the javascript code.

 

<span unselectable=”on” class=”ms-cui-tooltip” id=”new_entity|NoRelationship|Form|ipm.Form.new_entity.MyButton_ToolTip” role=”tooltip” aria-hidden=”false” style=”visibility: visible; position: absolute; top: 135px; left: 286px; min-width: 210px;”>
<div unselectable=”on” class=”ms-cui-tooltip-body”>
<div unselectable=”on” class=”ms-cui-tooltip-glow”>
<h1 unselectable=”on”>Temporary Suspension</h1>
<div unselectable=”on” class=”ms-cui-tooltip-description”>Temporary Suspension Description</div>
<div unselectable=”on” class=”ms-cui-tooltip-clear”></div>
<hr unselectable=”on”>
<div unselectable=”on” class=”ms-cui-tooltip-footer”>
<span unselectable=”on” class=” ms-cui-img-16by16 ms-cui-img-cont-float” style=”vertical-align: top;”></span>
<div unselectable=”on”>This button is currently disabled.</div>
</div>
<div unselectable=”on” class=”ms-cui-tooltip-description” style=”width: 90%;”>You may not have selected the item that works with this feature. If you do not have permissions to use this feature, contact your system administrator.</div>
</div>
</div>
</span>

 

4. Before returning true or false, if false, call this function, with the message. Replace BUTTONID with the appropriate value. It will be something long and with pipes (|) in it

function showDisabledReason(msg) {

// Get button
var link = window.parent.document.getElementById(‘BUTTONID’);

// Add custom tooltip
$(link, window.parent.document).hover(
function() { tryShowReason(msg); },
function() { }
);
return;

}

 

function tryShowReason(msg) {

var tooltip = manageToolTip(msg);
if (!tooltip) {
setTimeout(function () {
tryShowReason(msg);
}, 200);
}

}
function manageToolTip(msg) {
var tip = window.parent.document.getElementById(‘TOOLTIPID’);
if (tip) replaceToolTip(tip, msg);
return tip;
}
function replaceToolTip(tooltip, msg) {
$(tooltip).find(‘.ms-cui-tooltip-description:nth(1)’).html(msg);
}

 

And you’re done!!!

CRM 2011: Filter the customerid lookup field on quote form

And here we go for another highly unsupported customization.

This one has the following assumptions:

  • You know customerid is used only for showing contacts
  • There is another field on the form to show accounts, named new_AccountId
  • You want customerid to show only contacts from the selected account
  • You have jquery, otherwise the code will be slighly different and longer, but it’s of course possible

Here’s the code to run in the onLoad of the quote form:

 

$(‘#customerid’).attr(‘relationshipid’, ‘contact_customer_accounts’);
$(‘#customerid’).attr(‘dependantattributename’, ‘quote.new_AccountId’);
$(‘#customerid’).attr(‘dependantattributetype’, ‘1’);

 

And your lookup is filtered!

CRM 2011 Dialog: how to filter a lookup field

From what I’ve discovered while playing with dialogs, when using a lookup field, the filter criteria that you put on the field are simply ignored by the dialog. By default, the dialog will set the lookup default view to the concerned entity’s lookup view. That can be a problem sometimes. To solve this, here is another highly unsupported customization that I’ve come up with today.

 

1. Find the guid of your dialog (just open it and look for the id parameter in the url)
2. Create the view you want to use to filter the lookup, and get its guid (same method)
3. Open this file: “C:Program FilesMicrosoft Dynamics CRMCRMWebCSdialogrundialog.aspx”
4. Open the dialog, and using Chrome for example, find the lookup img’s id. It will look like this: “InteractionStep7”
5. At the end of the script tag, add the following function:

function setId(dialogGuid, viewGuid) {
if (window.location.href.indexOf(dialogGuid) > -1) {
var lookupImg = document.getElementById(‘InteractionStep7’);
if(!lookupImg) {
setTimeout(setId, 100);
} else {
lookupImg.setAttribute(‘defaultviewid’, viewGuid);
}
}
}
6. Then in the  same file, make the body tag like this, with the appropriate guids of course: <body onload=”setId(‘123124435’, ‘123124345’)”>
Explanations
Finding the guids are obivous.
Then editing the rundialog file, please note that this is highly unsupported of course.
Looking at the dialog page’s dom elements, you will see that the lookup image has an id. But that id is not set when onload is called. Probably some javascript executed there to get the dialog’s data.
This is why I use a timeout, that just waits for that id to be set. When the element is found, then I update the default view id, ie the view that will be use in the lookup window url.
And that’s it!

In the contact entity, modify the related orders default view

That one is extremely tricky. My customer had this sweet, short request: change the default view of related orders, in the contact entity.

RelatedOrders

By default, the defaule view is set to “Active”, and the customer wanted it to be “All”. How to do that? Well, here’s the answer below. It took me 5 hours to come up with this solution, but it works.
Highly unsupported if course…

Here’s the code below:

function setDefaultView() {
var grid = document.getElementById(“crmGrid_order_customer_contacts”);
    if (grid) {
        XMLHttpRequest.prototype.reallysend = XMLHttpRequest.prototype.send;
        XMLHttpRequest.prototype.send = function (body) {
            var that = this;
            if (body) {
                body = body.replace(‘<statecode>0</statecode>’, ‘<statecode>All</statecode>’);
                body = body.replace(‘<statecode>1</statecode>’, ‘<statecode>All</statecode>’);
                body = body.replace(‘<statecode>2</statecode>’, ‘<statecode>All</statecode>’);
                body = body.replace(‘<statecode>3</statecode>’, ‘<statecode>All</statecode>’);
                body = body.replace(‘<statecode>4</statecode>’, ‘<statecode>All</statecode>’);
            }
            this.onreadystatechange = function () {
                if (this.responseXML && XMLHttpRequest.prototype.reallysend) {
                    XMLHttpRequest.prototype.send = XMLHttpRequest.prototype.reallysend;
                    XMLHttpRequest.prototype.reallysend = null;
                }
            }
            return this.reallysend(body);
        }
        $(‘#crmGrid_order_customer_contacts_statecode’).find(‘option[value=”All”]’).prop(‘selected’, ‘selected’);
        grid.control.refresh();
}
This deserves a few explanations and configurations:
  1. I use jQuery to change the selected option in the picklist. However, and of course, this doesn’t change the displayed data. And, a call to refresh after changing this value, does not get the right data. It still retrieves only the active records. So to access jquery in this code, as we are in the context of an associated view, I use the ribbon trick (could also have deleted the js web resource directly and evaled it).
  2. I use a global variable, ouch. So I name it carefully.
  3. I replace the send function of the XMLHttpRequest object… That becomes sweet… 🙂 The point is to replace, in the message sent to the server, the statecode 0, by All, so that all records are retrieved, instead of the active ones.
  4. After the first pass, I reset the original send function, otherwise the replace will be applied to all subsequent queries, always retrieving all the records…

And here you go, a nice related view, with the default filter being set on ‘All’ !

That one is my pride. 🙂

Messing around with the CRM Database

Today I encountered an interesting bug in CRM: impossible to delete an invoice, coming along with the nice “Generic SQL Error”.
Digging a little, I quickly found that the SQL error was related to the foreign key constraint between InvoiceBase and InvoiceExtensionBase, namely.

The delete statement conflicted with the REFERENCE constraint “FK_InvoiceExtensionBase_InvoiceBase”. The conflict occurred in the database…, table…, column ‘InvoiceId’.

Counting records of both these tables  in the DB, I found different results… Hmmm

It just so happened that for performance reasons, my customer recently decided to transpose some plugin code directly in SQL code, and that included the creation of invoices. Quite crazy you might say, ok let’s not start this debate, as we are all fully aware of the risks (especially with CRM 2013 where the extension tables simply disappear…). To achieve that task, he run the SQL profiler and executed all the tasks he wanted to have in SQL. He then reorganized the logged SQL commands in to a few stored procedures. Except that he went probably a bit too far, as he inserted the invoiceid into the table InvoiceExtensionBase, as well.

Si what I had was records in the table InvoiceExtensionBase, starting from the day the plugin was replaced by the stored proc. And the consequence was that it was then impossible to delete an invoice. Interesting conclusion then: since the invoice entity had not been customized, the CRM platform simply did not insert anything into the extension table. So deleting all the records from InvoiceExtensionBase solved the problem.

CRM 2011: trigger a plugin from a stored procedure

No, it’s not possible, you’re dreaming. Or is it? Well, sort of. What’s possible is the following:

  1. I have created an entity called “new_pluginlauncher”. I have made a plugin firef upon the creation of a record of this entity, from which  I can do anything, depending on the parameters passed when creating the record. I can do things such as win or reopen a quote, create an order, close a case, well whatever really. As long as the correct guids and parameters are set upon record creation.
  2. I have created an SSIS package, based on Kingswaysoft,  that does that: create a record of that new_pluginlauncher entity
  3. I have deployed the SSIS package appropriately in the SQL Server instance
  4. I have created a stored procedure that calls that SSIS package

Bingo! From now on, I can access plugin code, on demand, from a SQL Server stored procedure.

And the best thing is: this is completely supported.

 

Here’s the code of the stored procedure. Pay attention to allowing xp_cmdshell.

ALTER Proc [dbo].[proc_CreatePluginLauncher]
@entitytype varchar(20),
@actiontype varchar(20),
@recordid varchar(50)
as
begin
declare @command varchar(8000),
@packagelocation varchar(1000),
@packagename varchar(200),
@param varchar(2000)
set @packagelocation = ‘SSISDBCRMCreateCRMPluginLauncher’
set @packagename = ‘CreatePluginLauncher.dtsx’
set @param = ‘select ‘ +
@entitytype + ‘ as EntityType, ‘ +
@actiontype + ‘ as ActionType, ‘ +
@recordid + ‘ as RecordGuid’
set @command =
‘dtexec /ISSERVER ‘ + @packagelocation + @packagename + ‘ ‘ +
‘/set package.variables[User::SQLCommand].Value;””‘ + @param + ‘”” ‘
—-now execute dynamic SQL by using EXEC.
DECLARE @returncode int
EXEC @returncode = xp_cmdshell @command
select @returncode
end

CRM 2011: Add javascript library to ribbon button

Say you want to use CrmRestKit or any other library from a ribbon button click, within an associated view. You can add the library to the parent entity form or to the associated entity, it won’t work.

As I found here, what you need to do is, in the command of the button click, using ribbon workbench for example (or directly in the xml), as follows.
The main thing here is to add the required libraries, and assign them isNaN as function name.

 

<Actions>

<JavaScriptFunction Library="$webresource:new_json2.js" FunctionName="isNaN" />
<JavaScriptFunction Library="$webresource:new_sdk.rest.js" FunctionName="isNaN" />
<JavaScriptFunction Library="$webresource:new_task_formscripts.js" FunctionName="Task_Update">
<CrmParameter Value="FirstSelectedItemId"/>
</JavaScriptFunction>
</Actions>

CK Editor and Rollup 15: problem

Apparently CRM 2011 UR15 has an impact on the way CRM fetches web resources from the server.
Since I installed it this morning, I get a JS error on my ckeditor enabled form, and a white area instead of my nice wysiwyg…
The console shows 404 errors, and CRM trying to get js and css files from the userdefined url, instead of webresources…

So what I did is :

  1. Delete all web resources related to ckeditor, as they are not necessary anymore
  2. Create a folder named by the organization in C:Program FilesMicrosoft Dynamics CRMCRMWeb
    1. If your org is “SalesForce”, then the folder is: C:Program FilesMicrosoft Dynamics CRMCRMWebSalesForce
  3. In there, create another folder called “userdefined”
  4. In there, copy all the ckeditor files (styles.css, config.js, all folders, everything)

Bingo, it works!

And the webresources are suddenly much less cluttered…

CR 2011: refresh grid – ur12

Since UR12 (update rollup 12), in CRM 2011, the crmGrid.refresh() function doesn’t exist anymore.

Here’s how to do it now:

document.getElementById(‘crmGrid’).control.refresh();