Wednesday, September 29, 2010

Marketing List History

Greetings guys,

Last week I worked on a very strange requirement to implement some kind of a marketing list auditing mechanism. The customer wants to be able to track which user added particular contact in a given marketing list.
Since the list <–> contact relationship is many-to-many and therefore the relations are stored in a third table (called ListMemberBase in this case), there is no event available to trigger a workflow or do something out of the box. Technically nor the list is updated neither the contact. Since I didn’t have much time to do this I decided to use database trigger to achieve the goal. Yes it is unsupported but it works very fast and nice so I decided to share it.
I first created a custom field of type ntext called Marketing List History (new_marketinglisthistory) on the Contact Entity. Added this to the contact form.
Then created the following trigger:

USE [XXX_MSCRM]
GO
/****** Object: Trigger [dbo].[trg_ListHistory] Script Date: 09/28/2010 17:09:44 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[trg_ListHistory]
ON [dbo].[ListMemberBase]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
if (select EntityType from inserted) = 2
begin
if (select New_MarketingListHistory from Contact, inserted where Contact.contactid=inserted.entityid) is null
begin
UPDATE Contact
SET New_MarketingListHistory = (select l.createdbyname+' --> '+listname from listmember l inner join list m on l.listid=m.listid where l.ListMemberId=inserted.ListMemberId)
FROM inserted
WHERE Contact.contactid = inserted.entityid
end
else
begin
UPDATE Contact
SET New_MarketingListHistory = (select l.createdbyname+' --> '+listname + char(13) from listmember l inner join list m on l.listid=m.listid where l.ListMemberId=inserted.ListMemberId) + (select New_MarketingListHistory from Contact where Contact.contactid=inserted.entityid)
FROM inserted
WHERE Contact.contactid = inserted.entityid
end
end
END

Done! Here is the result:


The last addition in a given marketing list goes on top of the history.
You can also put this column in the marketing list view – all members to look like this:


Regards!
Rinshwind

Thursday, September 23, 2010

MS CRM 3.0: Set email default font

 Hello again,

 I've been working recently on a way to set the default font within the CRM 3.0 email message edit field. The default font is Tahoma 10, however, a different font was needed (in this code, Arial). From the 3 specified font families, Internet Explorer will use the first one available on the system. As those 3 fonts are rather mundane, it's unlikely they will not be available. Nonetheless, should you decide to use a special font, make sure it's available on each system the code will run on.

 My method uses the "subject" field in order to determine if the form is used to view an email or to edit a new email / a reply email / a forwarded email. It was enough to achieve my goal on this task. The thing about IFrames is that you're not able to determine exactly when they've finished loading. I have attempted to use the onLoad event of the IFrame, however that lead to nothing viable.

 Thus, it came down to the same old workaround of using a timer to make a check against the readyState of the IFrame object.

 The amount of time set on the timers is really up to you - it was enough for my objectives, you might need a quicker / slower time between checks. If the IFrame is not yet loaded, the method sets another timer to make another call attempt later. That, off course, only if the email is in edit mode (the "subject" field is editable).

 Please note that this is not tested on CRM 4.0. I am not aware if the IFrame structure is different than that used in 3.0, therefore I cannot claim it might work on 4.0 as well. ;-)



 Cheers!



Here is the code (in my case, it was placed within the onLoad code of the Email entity):

---------------------
function forceFontFormat(parentNode, fontFamily, fontSizeInPoints, fontSizeInIndex)
{
    if(parentNode)
    {
        try
        {
         var tagName = '';
         if(parentNode.tagName)
         tagName = parentNode.tagName.toLowerCase();
         switch (tagName)
         {
         case 'div':
         case 'p':
         case 'span':
            parentNode.style.fontFamily = fontFamily;
                 parentNode.style.fontSize = fontSizeInPoints + 'pt';
         break;
         case 'font':
         parentNode.setAttribute('face', fontFamily);
         parentNode.setAttribute('size', fontSizeInIndex);
         break;
         }
        }
        catch (err) { }
        if((parentNode.childNodes) && (parentNode.childNodes.length > 0))
        {
            for(var i = 0; i < parentNode.childNodes.length; i++)
            {
                forceFontFormat(parentNode.childNodes[i], fontFamily, fontSizeInPoints, fontSizeInIndex);
            }
        }
    }
}


function forceDefaultCrm30EmailFontFamily()
{
    try
    {
        var isEditable = false;
        try
        {
            var emailSubject = document.getElementById("subject");
            isEditable = emailSubject.isContentEditable;
        }
        catch (secondErr) {}
        if ( isEditable == true)
        {
            var iframe = document.getElementById("descriptionIFrame");
            var forceFont = false;
            try
            {
                if(iframe.contentWindow.document.readyState == "complete")
                    forceFont = true;
            }
            catch (thirdErr)
            {
                forceFont = false;
            }
            if(forceFont == true)
            {
                forceFontFormat(iframe.contentWindow.document.body, 'Arial', '10', '2');
            }
            else
            {
                setTimeout(forceDefaultCrm30EmailFontFamily, 1000);
            }
        }
    }
    catch (firstErr)
    {
        /*alert("CRM Development\n\nThis warning does not affect your work, please ignore it.\nThank you!\n\n\nError message (see below):\n\n" + err.description);*/
    }
}


// Method call
setTimeout(forceDefaultCrm30EmailFontFamily, 1000);

Wednesday, September 15, 2010

Another way of loading certain CRM Views into an IFRAME on the main form

Since every CRM View has its own GUID and can be launched using the Advanced Find out-of-the-box functionality, you can follow several easy steps to load what view you want (with whatever additional filters you want) in a certain IFRAME available on the crmForm.
Here's the scenario: you wanna display your active contacts (My Active Contacts view) in a separate TAB on your account entity form.
- find out the id and objecttypecode of your view;
- create a new TAB and IFRAME on the form of the account entity;
- dynamically set the IFRAME URL as following:
http://[crm_server_name]/[ORGANIZATION_NAME]/AdvancedFind/AdvFind.aspx?EntityCode=[CRM_VIEW_ENTITY_CODE]&QueryId=[CRM_VIEW_ID]&ViewType=1039&AutoRun=True (for our scenario, it would be something like http://CRMSERVER/MyOrganization/AdvancedFind/AdvFind.aspx?EntityCode=2&QueryId={FC3A5B78-1AA4-10C4-1234-12C45B78F01A}&AutoRun=True )
Warning: you must type "AutoRun=True" using this casing. lower/upper case won't trigger the Advanced Find to auto run (and that because the parsing of that parameter is as it is).

There you go - your custom filtered view available anywhere you want on any form :)