Automatically Set Regarding on New Activities

Summary

Missing functionality in the MS CRM 2011 OOB functionality:

  • Activities created from menu File – New Activity on entity forms do not get the Regarding field populated.
  • When creating new activities from the associated view on the entity form, the Regarding field is mapped properly.

FileNewActivityIn this post, I will demonstrate a javascript example of a generic way to populate the Regarding field on activities, where the OOB CRM functionality fails to do this.TaskWORegarding

Objective

Whenever possible, the activity form shall to try to find which record that should be set as regarding. While doing this, also provide an opportunity to specify which entities that shall be allowed as regarding for each type of activity entity.

Method

  1. As the activity form is not opened with any parameters indicating “where it came from”, I investigate window.parent.opener to find information of its origin.
  2. Metadata requests are used to find additional information of the source, to be able to map between ObjectTypeCode and LogicalName, as well as to dynamically find which attribute on the source that is the primary attribute (i.e. the “name” attribute”).
  3. I perform a REST request to find the name of the source record, instead of e.g. trying to walk through the source form’s DOM to find information about it.

Code samples

Function to verify and populate the Regarding field:

Cinteros.Xrm.Activity = {
      _verifyRegarding: function (regardingEntities) {
        try {
            // First check if we have a valid mapped regardingobject
            var regardingObjectId = Xrm.Page.getAttribute("regardingobjectid").getValue();
            if (regardingObjectId && regardingObjectId[0].entityType) {
                // We have a regarding object through mapping, verify it is from an allowed entity
                if (!regardingEntities || !regardingEntities.length) {
                    return true;
                }
                for (var i = 0; i < regardingEntities.length; i++) {
                    if (regardingObjectId[0].entityType === regardingEntities[i]) {
                        return true;
                    }
                }
                return false;
            }
            // No regarding object was set - now examine opener to see where we came from
            if (window && window.parent && window.parent.opener && window.parent.opener.location && window.parent.opener.location.href) {
                var href = window.parent.opener.location.href;
                // Extract parent etc from its href
                var parentEtc = Cinteros.Xrm.SDK.getParameter(href, "etc") || Cinteros.Xrm.SDK.getParameter(href, "oType");
                // Get metadata for parent entitytype
                var regardingEntityMeta;
                var entityMetadataCollection;
                if (!regardingEntities || !regardingEntities.length) {
                    // No allowed entities specified, load metadata for all entities
                    entityMetadataCollection = Cinteros.Xrm.SDK.RetrieveAllEntities();
                }
                else {
                    // Load metadata only for allowed entities
                    entityMetadataCollection = [];
                    for (var i = 0; i < regardingEntities.length; i++) {
                        entityMetadataCollection.push(Cinteros.Xrm.SDK.RetrieveEntity(regardingEntities[i]));
                    }
                }
                // Get the metadata for correct parent entity, based on etc/otc
                for (var i = 0; i < entityMetadataCollection.length; i++) {
                    if (entityMetadataCollection[i].ObjectTypeCode == parentEtc) {
                        regardingEntityMeta = entityMetadataCollection[i];
                        break;
                    }
                }
                if (regardingEntityMeta && regardingEntityMeta.ObjectTypeCode == parentEtc) {
                    // Extract parent id from its href
                    var parentId = Cinteros.Xrm.SDK.getParameter(href, "id") || Cinteros.Xrm.SDK.getParameter(href, "oId");
                    if (parentId) {
                        parentId = unescape(parentId);
                        var attributeMeta = Cinteros.Xrm.SDK.RetrieveAttribute(regardingEntityMeta.LogicalName, regardingEntityMeta.PrimaryNameAttribute);
                        // Retrieve the regarding entity, to be able to find its primary name
                        var regardingObject = Cinteros.Xrm.REST.Retrieve(regardingEntityMeta.SchemaName, parentId, "?$select=" + attributeMeta.SchemaName);
                        if (regardingObject) {
                            // Found regarding record, create lookup object
                            var regardingLkp = [{ "id": parentId, "entityType": regardingEntityMeta.LogicalName, "name": regardingObject[attributeMeta.SchemaName]}];
                            Xrm.Page.getAttribute("regardingobjectid").setValue(regardingLkp);
                            return true;
                        }
                    }
                }
            }
            return false;
        }
        catch (e) {
            window.alert("Error in verifyRegarding:nn" + e.description);
        }
    }
}

Note: The javascript-functions in namespace Cinteros.Xrm are part of our internally developed tools, but the names should be quite self-explanatory.
REST functionality can be replaced by similar functionality in the CrmRestKit, see http://crmrestkit.codeplex.com/, or by other custom made code.
MetaData functionality can be replaced by functionality in the MS CRM SDK, see Sample: Retrieve Entity Metadata Using JScript.
Feel free to contact me if you have any questions.

Function to register for the formLoad event on each activity entity that shall have this functionality:

Cinteros.Xrm.Activity = {
    formLoad: function () {
        try {
            // Only do this when creating new activities
            if (Xrm.Page.ui.getFormType() === 1) {
                // For this example - four entities are allowed to be set as regarding
                var allowedEntities = ["contact", "lead", "opportunity", "jr_my_custom_entity"];
                if (this._verifyRegarding(allowedEntities) === false) {
                    window.alert("Activity must be created from a valid regarding record.n(" + allowedEntities.toString() + ")");
                    Xrm.Page.ui.close();
                }
            }
        }
        catch (e) {
            window.alert("Error during formLoad:nn" + e.description);
        }
    }
}

It is possible to call the _verifyRegarding function without any parameter, thus allowing any entity as regarding object. This will however read all entity metadata from the database, which typically takes a few seconds. So specifying the allowed set of regarding entity types is recommended.
Exclude the if-clause when calling the _verifyRegarding function to ignore it’s return value. Then the function will simply populate the regarding field when possible, without any verification that the field must be populated, or that it must be populated by a specific entity type.
Note that this solution uses window references and url parameters to interpret the caller. This is probably not supported according to MS CRM SDK, but it is not unsupported either, as it does not alter the DOM or use undocumented javascript methods, and it includes quite good error handling.

Save & Publish Button for Forms and WebResources

While working with customizations and UI development in CRM 2011 – have you ever been looking for that “Save and Publish” button? Have you ever not been looking for it? We all wonder why it is not there and there is a case on Microsoft Connect requesting it.
But relax, it is quite easy to get it in there right now.

Annoying facts:

SaveAndPublish32It seems to me that someone just forgot to implement this function. There is a button icon /CRMWEB/_imgs/ribbon/SaveAndPublish32.png (and corresponding 16 pix icon). The javascript function being called when clicking the existing Publish button in form customization is called SaveAndPublish(). It just does not do what the name indicates.

Solution:

To get a Save & Publish button:

  • Create a javascript webresource that first calls the Save function and then the Publish function
  • Create the button in the RibbonDiffXml section of customizations.xml, using existing icon files and calling the function created in step 1
  • Import the ribbon customizations SnP
    Javascript:
    Cinteros.Xrm.Customization = {
        SaveAndPublishForm: function () {
            try {
                SaveForm(false);
            }
            catch (e) { }
            try {
                SaveAndPublish();
            }
            catch (e) { }
        },
        SaveAndPublishWebResource: function () {
            try {
                SaveForm(false);
            }
            catch (e) { }
            try {
                PublishWebResource();
            }
            catch (e) { }
        }
    }
    RibbonDiffXml:
      <RibbonDiffXml>
        <CustomActions>
          <CustomAction Id="Cint.Mscrm.FormEditorTab.SaveButtons.Controls" Location="Mscrm.FormEditorTab.SaveButtons.Controls._children" Sequence="1">
            <CommandUIDefinition>
              <Button Command="Cint.Mscrm.FormEditorTab.SaveButtons.Controls.Button.SaveAndPublish" CommandType="General" Id="Cint.Mscrm.FormEditorTab.SaveButtons.Controls.Button.SaveAndPublish.Id" Image16by16="/_imgs/ribbon/SaveAndPublish16.png" Image32by32="/_imgs/ribbon/SaveAndPublish32.png" TemplateAlias="o1" LabelText="$LocLabels:Cint.Mscrm.FormEditorTab.SaveAndPublish.LabelText" ToolTipTitle="$LocLabels:Cint.Mscrm.FormEditorTab.SaveAndPublish.ToolTip" ToolTipDescription="$LocLabels:Cint.Mscrm.FormEditorTab.SaveAndPublish.ToolTipDescription" />
            </CommandUIDefinition>
          </CustomAction>
          <CustomAction Id="Cint.Mscrm.WebResourceEditTab.Save.Controls" Location="Mscrm.WebResourceEditTab.Save.Controls._children" Sequence="1">
            <CommandUIDefinition>
              <Button Command="Cint.Mscrm.WebResourceEditTab.Save.Controls.Button.SaveAndPublish" CommandType="General" Id="Cint.Mscrm.WebResourceEditTab.Save.Controls.Button.SaveAndPublish.Id" Image16by16="/_imgs/ribbon/SaveAndPublish16.png" Image32by32="/_imgs/ribbon/SaveAndPublish32.png" TemplateAlias="o1" LabelText="$LocLabels:Cint.Mscrm.FormEditorTab.SaveAndPublish.LabelText" ToolTipTitle="$LocLabels:Cint.Mscrm.FormEditorTab.SaveAndPublish.ToolTip" ToolTipDescription="$LocLabels:Cint.Mscrm.FormEditorTab.SaveAndPublish.ToolTipDescription" />
            </CommandUIDefinition>
          </CustomAction>
        </CustomActions>
        <Templates>
          <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
        </Templates>
        <CommandDefinitions>
          <CommandDefinition Id="Cint.Mscrm.FormEditorTab.SaveButtons.Controls.Button.SaveAndPublish">
            <EnableRules>
              <EnableRule Id="Mscrm.Enable.IsCustomizableManagedPropertyRule" />
            </EnableRules>
            <DisplayRules />
            <Actions>
              <JavaScriptFunction FunctionName="Cinteros.Xrm.Customization.SaveAndPublish" Library="$webresource:cint_/scripts/utils_support.js" />
            </Actions>
          </CommandDefinition>
          <CommandDefinition Id="Cint.Mscrm.WebResourceEditTab.Save.Controls.Button.SaveAndPublish">
            <EnableRules>
              <EnableRule Id="Mscrm.Enable.IsWebResourceCustomizableRule" />
            </EnableRules>
            <DisplayRules />
            <Actions>
              <JavaScriptFunction FunctionName="Cinteros.Xrm.Customization.SaveAndPublishWebResource" Library="$webresource:cint_/scripts/utils_support.js" />
            </Actions>
          </CommandDefinition>
        </CommandDefinitions>
        <RuleDefinitions>
          <TabDisplayRules />
          <DisplayRules />
          <EnableRules />
        </RuleDefinitions>
        <LocLabels>
          <LocLabel Id="Cint.Mscrm.FormEditorTab.SaveAndPublish.LabelText">
            <Titles>
              <Title languagecode="1033" description="Save and Publish" />
              <Title languagecode="1053" description="Spara och Publicera" />
            </Titles>
          </LocLabel>
          <LocLabel Id="Cint.Mscrm.FormEditorTab.SaveAndPublish.ToolTip">
            <Titles>
              <Title languagecode="1033" description="Save and Publish" />
              <Title languagecode="1053" description="Spara och Publicera" />
            </Titles>
          </LocLabel>
          <LocLabel Id="Cint.Mscrm.FormEditorTab.SaveAndPublish.ToolTipDescription">
            <Titles>
              <Title languagecode="1033" description="Saves and publishes the form" />
              <Title languagecode="1053" description="Sparar och publicerar formuläret" />
            </Titles>
          </LocLabel>
        </LocLabels>
      </RibbonDiffXml>
    

    Shortcut:

    If you don’t want to do this manually, you can download a solution with just this content HERE.