Bug: Notes creation in custom entity

There currently seems to be an issue in Dynamics 365 Online, where you can see notes in area in the custom entity, but cannot create new notes. I could reproduce this issue in this version -> Version 1612 (8.2.0.773) (DB 8.2.0.764)

This is how the notes area looks in a custom entity. There is no header area to create new notes.

missing-header-in-notes

Compare this with the contact entity.

notes-area-contact-entity

As you can see, there is a textarea to create new notes, which seems to be missing in the custom entity. I looked into this using DevTools, and it appears to be display issue, as the DOM elements for creating the notes are still there. I quickly wrote this bookmarklet to temporarily show the create new notes text area in the form.

Sourcecode

(function () {
	let contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		let activityWall = contentPanels[0].contentDocument.querySelector('#notesWall div.header');
		if(activityWall && activityWall.style.display === 'none'){
			activityWall.style.display = ''
		}
	} else {
		alert('Entity form not detected');
	}
})();

Bookmarklet

javascript:(function(){let contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){let activityWall=contentPanels[0].contentDocument.querySelector('#notesWall div.header');if(activityWall&&activityWall.style.display==='none'){activityWall.style.display=''}}else{alert('Entity form not detected');}})();void 0;

This is how the custom form will look, after you run the bookmarklet.

after-bookmarklet-header-restore-in-notes

I am not sure what sets the “display” property to none. When the DOM element is initially created, it is created without the “display” property set and then some event handler appears to be modifying this. I set a DOM breakpoint on “Attributes modifications”, but the breakpoint is not retained when I refresh the page, and hence I am unable to catch the “display: none” being set on “#notesWall div.header”. I spent some time to investigate this from the pretty-printed source, but could not figure it out.

Bookmarklet: Copy and Paste Lookup

Lookups (including partylist and customer) cannot be copy pasted. I have encountered couple of scenarios where I needed this capability. I have developed these bookmarklets to solve this issue.

Copy Lookup

Select and the drag the code below to the bookmark bar.

javascript:(function(){let contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){let Xrm=contentPanels[0].contentWindow.Xrm;let currentControl=Xrm.Page.ui.getCurrentControl();if(currentControl&¤tControl.getControlType()==='lookup'){let currentLookup=currentControl.getAttribute().getValue();if(currentLookup){let serialisedLookupValue=JSON.stringify(currentLookup.map(x=>{let c={};({id:c.id,name:c.name,type:c.type,typename:c.typename,entityType:c.entityType}=x);return c;}));sessionStorage.setItem('ryr_serialisedLookup',serialisedLookupValue);alert('Lookup copied. Ready to paste');}}else{alert('No field has been selected or the currently selected field is not a lookup');}}else{alert('Entity form not detected');}})();void 0;

Source:

(function () {
	let contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		let Xrm = contentPanels[0].contentWindow.Xrm;
		let currentControl = Xrm.Page.ui.getCurrentControl();
		if (currentControl && currentControl.getControlType() === 'lookup') {
			let currentLookup = currentControl.getAttribute().getValue();
			if (currentLookup) {
				let serialisedLookupValue = JSON.stringify(
						currentLookup.map(x =  > {
								let c = {};
								({
									id : c.id,
									name : c.name,
									type : c.type,
									typename : c.typename,
									entityType : c.entityType
								}
										 = x);
								return c;
							}));
				sessionStorage.setItem('ryr_serialisedLookup', serialisedLookupValue);
				alert('Lookup copied. Ready to paste');
			}
		} else {
			alert('No field has been selected or the currently selected field is not a lookup');
		}
	} else {
		alert('Entity form not detected');
	}
})();

Paste Lookup

Select and the drag the code below to the bookmark bar.

javascript:(function(){let contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){let Xrm=contentPanels[0].contentWindow.Xrm;let currentControl=Xrm.Page.ui.getCurrentControl();if(currentControl&¤tControl.getControlType()==='lookup'){let currentLookup=currentControl.getAttribute();let copiedLookupValue=sessionStorage.getItem('ryr_serialisedLookup');if(copiedLookupValue){currentLookup.setValue(JSON.parse(copiedLookupValue));} else{alert('Please select a lookup to copy first before pasting');}}else{alert('No field has been selected or the currently selected field is not a lookup');}}else{alert('Entity form not detected');}})();void 0;

Source:

(function () {
	let contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		let Xrm = contentPanels[0].contentWindow.Xrm;
		let currentControl = Xrm.Page.ui.getCurrentControl();
		if (currentControl && currentControl.getControlType() === 'lookup') {
			let currentLookup = currentControl.getAttribute();
			let copiedLookupValue = sessionStorage.getItem('ryr_serialisedLookup');
			if(copiedLookupValue){
				currentLookup.setValue(JSON.parse(copiedLookupValue));
			}
			else{
				alert('Please select a lookup to copy first before pasting');
			}
		} else {
			alert('No field has been selected or the currently selected field is not a lookup');
		}
	} else {
		alert('Entity form not detected');
	}
})();

Instructions

  1. Select the lookup field to be copied. The lookup will usually be highlighted with a blue background when it is selectedlookup-selected
  2. Run the Copy Lookup bookmarklet. You will get a alert message confirming that the lookup has been copied
  3. Select the lookup field that is the target of the paste. The lookup has to be of the same type i.e. if the copied lookup was a contact lookup, the target lookup also has to be a contact lookup
  4. Run the Paste Lookup bookmarklet
  5. Save the record

I have tested these bookmarklets only in Chrome 53 and Firefox 50.

Bug: EntityReference, Create and latebinding

For big projects and large code sizes, it is recommended to use strongly typed classes when you are dealing with CRUD operations related to the CRM entities. You can use Early Bound Generator or “crmsvcutil” to do this. The advantages are compile type errors for mismatches types and intellisense. But if you are quickly testing out some code, latebinding is the easiest path, as it doesn’t involve the overhead of generating the classes. During this process, I discovered a bug.

Here is the code and I will explain the bug after that.

            CrmServiceClient crmSvc = new CrmServiceClient(ConfigurationManager.ConnectionStrings["CRMConnectionString"].ConnectionString);
            if (crmSvc.IsReady)
            {
                #region create contact with account entityreference

                var account = new Entity("account");
                account["name"] = "Test Account";
                account.Id = crmSvc.OrganizationServiceProxy.Create(account);
                Console.WriteLine($"Created account with id {account.Id}");

                Console.WriteLine("Creating contact with account as EntityReference");
                Console.WriteLine("----------------------------------------------------");

                var contact = new Entity("contact");
                contact["parentcustomerid"] = account.ToEntityReference();
                contact.Id = crmSvc.OrganizationServiceProxy.Create(contact);
                Console.WriteLine($"Created contact with id {contact.Id}");

                contact = crmSvc.OrganizationServiceProxy.Retrieve("contact", contact.Id, new ColumnSet("parentcustomerid"));
                Console.WriteLine($"Contact: Account Lookup {contact.GetAttributeValue<EntityReference>("parentcustomerid")?.Id}");

                #endregion

                #region update contact with account Guid

                Console.WriteLine("Updating contact with account as Guid");
                Console.WriteLine("----------------------------------------------------");
                try
                {
                    contact["parentcustomerid"] = account.Id;
                    crmSvc.OrganizationServiceProxy.Update(contact);
                    var updateContact = crmSvc.OrganizationServiceProxy.Retrieve("contact", contact.Id, new ColumnSet("parentcustomerid"));
                    Console.WriteLine($"Updated Contact: Account Lookup {updateContact.GetAttributeValue<EntityReference>("parentcustomerid")?.Id}");
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Update Contact failed with Exception: {e.Message}");
                }

                crmSvc.OrganizationServiceProxy.Delete("contact", contact.Id);
                Console.WriteLine($"Deleted contact with id {contact.Id}");

                #endregion

                #region create contact with account Guid

                Console.WriteLine("\nCreating contact with account as Guid");
                Console.WriteLine("----------------------------------------------------");
                contact = new Entity("contact");
                contact["parentcustomerid"] = account.Id;
                //This should not succeed
                contact.Id = crmSvc.OrganizationServiceProxy.Create(contact);
                Console.WriteLine($"Created contact with id {contact.Id}");

                contact = crmSvc.OrganizationServiceProxy.Retrieve("contact", contact.Id, new ColumnSet("parentcustomerid"));
                Console.WriteLine($"Contact: Account Lookup {contact.GetAttributeValue<EntityReference>("parentcustomerid")?.Id}");

                crmSvc.OrganizationServiceProxy.Delete("contact", contact.Id);
                Console.WriteLine($"Deleted contact with id {contact.Id}");

                crmSvc.OrganizationServiceProxy.Delete("account", account.Id);
                Console.WriteLine($"\nDeleted account with id {account.Id}");

                #endregion

            }

Here is the result.

Guid Entityreference result

Even if you are using latebinding, CRM webservice respects type and doesn’t allow you to set an attribute to a type, that doesn’t match its definition, except in one case: You can set the EntityReference attribute to a Guid, and the code with execute without any exception. The good thing though is the fields is just set to null, and the Guid is ignored, but this a bug as you cannot do it with other types e.g. you can’t set an Optionset field to an int or a Currency field to a decimal.

I have logged this on Connect for verification. I experienced this behaviour in CRMOnline and CRM2015.

Connect issue -> https://connect.microsoft.com/site687/feedback/details/2740732/sdk-permits-setting-entityreference-attribute-to-guid-late-binding

Further reading:

MSDN: Create, retrieve, update, and delete (late bound)

 

Bookmarklet: Display Optionset values

Unless you have generated strongly typed classes, looking up Optionset values for the selected value or any other value, is one of the most annoying things to do as a developer. You can use the bookmarklet below to display Optionset values, along with the Optionset text for all the Optionsets on a form. You can drag the minified source to your bookmark bar, for creating the bookmarklet.

This is how a sample contact form looks after executing the bookmarklet.

Altered Optionset

Unminified

(function () {
	var contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		var Xrm = contentPanels[0].contentWindow.Xrm;
		var frameDocument = contentPanels[0].contentWindow.document;
		Xrm.Page.ui.controls.forEach(function (c) {
			if (c.getControlType() !== 'optionset')
				return;
			var attribute = c.getAttribute();
			var selectedOptionValue = attribute.getValue();
			var options = attribute.getOptions();
			var isClearOptions = options.some(function (o) {
					return o.text.indexOf(' (') === -1;
				});
			if (isClearOptions) {
				c.clearOptions();
			}
			options.forEach(function (o) {
				if (o.text && o.text.indexOf(' (') === -1) {
					o.text = o.text + ' (' + o.value + ')';
				}
				c.addOption(o);
			});
			if (selectedOptionValue && isClearOptions) {
				attribute.setValue(selectedOptionValue);
			}
		});
	} else {
		alert('Entity form not detected');
	}
})();

Minified

javascript:(function(){var contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){var Xrm=contentPanels[0].contentWindow.Xrm;var frameDocument=contentPanels[0].contentWindow.document;Xrm.Page.ui.controls.forEach(function(c){if(c.getControlType()!=='optionset')
return;var attribute=c.getAttribute();var selectedOptionValue=attribute.getValue();var options=attribute.getOptions();var isClearOptions=options.some(function(o){return o.text.indexOf(' (')===-1;});if(isClearOptions){c.clearOptions();}
options.forEach(function(o){if(o.text&&o.text.indexOf(' (')===-1){o.text=o.text+' ('+o.value+')';}
c.addOption(o);});if(selectedOptionValue&&isClearOptions){attribute.setValue(selectedOptionValue);}});}else{alert('Entity form not detected');}})();void 0;

Bookmarklet: Clone Current Record

Sometimes, you just want to clone a current record, and slightly change the details. e.g. some sort of config entity record. You can use the bookmarklet below, that does cloning only at the parent entity level. Once the new form is open you can change the details and save it. Drag the minified code to your bookmarklet bar, and execute it when you have the CRM record open. The bookmarklet uses query parameters to populate the fields in the new form.

Unminified

(function() {
    var contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
        return d.style.visibility !== 'hidden'
      });

    if (!contentPanels || contentPanels.length == 0 || !contentPanels[0].contentWindow.Xrm) {
		alert('CRM Form not detected');
		return;
    }
	var Xrm = contentPanels[0].contentWindow.Xrm;
	var extraq = '';
	var clientUrl = Xrm.Page.context.getClientUrl();
	var entityName = Xrm.Page.data.entity.getEntityName();

	Xrm.Page.data.entity.attributes.forEach(function(c){
		var attributeType = c.getAttributeType();
		var attributeName = c.getName();
		var attributeValue = c.getValue();
		if(!attributeValue ||
		attributeName === 'createdon' ||
		attributeName === 'modifiedon' ||
		attributeName === 'createdby' ||
		attributeName === 'modifiedby') return;

		if(attributeType === 'lookup'){
			extraq += (attributeName+'name='+attributeValue[0].name+'&');
			extraq += (attributeName+'type='+attributeValue[0].entityType+'&');
			attributeValue = attributeValue[0].id;
		}
		if(attributeType === 'datetime'){
			attributeValue = attributeValue.toDateString();
		}
		extraq += (attributeName+'='+attributeValue+'&');
	});

	var newWindowUrl = clientUrl+'/main.aspx?etn='+entityName+'&pagetype=entityrecord'+'&extraqs=?'+encodeURIComponent(extraq)+'etn='+entityName;

	window.open(newWindowUrl, '_blank', "location=no,menubar=no,status=no,toolbar=no", false);
})();

Minified

javascript:(function(){var contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return d.style.visibility!=='hidden'});if(!contentPanels||contentPanels.length==0||!contentPanels[0].contentWindow.Xrm){alert('CRM Form not detected');return;}
var Xrm=contentPanels[0].contentWindow.Xrm;var extraq='';var clientUrl=Xrm.Page.context.getClientUrl();var entityName=Xrm.Page.data.entity.getEntityName();Xrm.Page.data.entity.attributes.forEach(function(c){var attributeType=c.getAttributeType();var attributeName=c.getName();var attributeValue=c.getValue();if(!attributeValue||attributeName==='createdon'||attributeName==='modifiedon'||attributeName==='createdby'||attributeName==='modifiedby')return;if(attributeType==='lookup'){extraq+=(attributeName+'name='+attributeValue[0].name+'&');extraq+=(attributeName+'type='+attributeValue[0].entityType+'&');attributeValue=attributeValue[0].id;}
if(attributeType==='datetime'){attributeValue=attributeValue.toDateString();}
extraq+=(attributeName+'='+attributeValue+'&');});var newWindowUrl=clientUrl+'/main.aspx?etn='+entityName+'&pagetype=entityrecord'+'&extraqs=?'+encodeURIComponent(extraq)+'etn='+entityName;window.open(newWindowUrl,'_blank',"location=no,menubar=no,status=no,toolbar=no",false);})(); void 0;

I tested this only in CRMOnline on version 8.1.0.359.

End note:

This bookmarklet does not copy across the time component, if you have a datetime field with both date and time on the parent form.

Reference:

https://msdn.microsoft.com/en-us/library/gg334375.aspx#BKMK_ExampleXrmUtilityOpentEntityForm

Bookmarklet: Customise Form & Refresh Subgrids

If you just want to quickly customise the form layout use this bookmarklet below. Don’t add any new fields from this window, as it will create the field with the Default Publisher’s prefix, which by default is “new_”.

Unminified

var entityName = prompt("Entity?", "");
var url = Xrm.Page.context.getClientUrl() + '/main.aspx?etn=' + entityName + '&extraqs=formtype%3dmain&pagetype=formeditor';
window.open(url, '_blank');

Minified

javascript:var%20entityName=prompt("Entity?","");var%20url=Xrm.Page.context.getClientUrl()+'/main.aspx?etn='+entityName+'&extraqs=formtype%3dmain&pagetype=formeditor';window.open(url,'_blank');void%200;

If you just want to quickly refresh all the subgrids in the form, without refreshing the whole page, use this bookmarklet.

Unminified

(function () {
	var contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		var Xrm = contentPanels[0].contentWindow.Xrm;
		Xrm.Page.ui.controls.forEach(function (c) {
			if (c.getControlType() === 'subgrid') {
				c.refresh();
			}
		});
	} else {
		alert('Entity form not detected');
	}
})();

Minified

javascript:(function(){var%20contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return%20d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){var%20Xrm=contentPanels[0].contentWindow.Xrm;Xrm.Page.ui.controls.forEach(function(c){if(c.getControlType()==='subgrid'){c.refresh();}});}else{alert('Entity%20form%20not%20detected');}})();void%200;

Advanced Find and “In” condition

Advanced Find, in its current form, doesn’t have the capability to do a true “In” condition for text, datetime and numbers. For example this is how you would really do an “In” condition for a text field.

In condition

However the above condition would not return any results, as Advanced Find doesn’t allow separating valid values with a semi-colon. Query like the one above, has to be actually be done like the screenshot below.

In condition Actual

This is actually clunky and involves too many clicks. The query capability in Advanced Find also differs to what the workflow condition offers. For e.g. you can do a true “In” condition in workflow editor. The workflow condition builder uses a similar UI to Advanced Find, but it also displays a “In” condition for text attribute, which is not displayed in the Advanced Find.

Workflow In Condition

Workflow Step

Bookmarklet Solution

In order to do a “in” condition, where the target values are separated by semi-colons, you can use the bookmarklet below.

Unminified code

var contentPanel = $('#crmContentPanel > iframe');
if (contentPanel && contentPanel.length > 0) {
	var targetFrame = contentPanel[0].contentWindow;
	var isFetchModified = false;
	targetFrame.ExecuteQuery();
	var xml = targetFrame.$get("FetchXml").value;
	var xmlDoc = $.parseXML(xml),
	$xml = $(xmlDoc);
	$xml.find('condition[value]').each(function (i, d) {
		var inCondition = '';
		var multipleValues = $(d).attr('value');
		if (multipleValues.indexOf(';') === -1)
			return;
		multipleValues.split(';').forEach(function (c) {
			inCondition += ('<value>' + c + '</value>');
		});
		$(d).attr('operator', 'in');
		$(d).removeAttr('value');
		$(d).html(inCondition);
		isFetchModified = true;
	});
	if (isFetchModified) {
		targetFrame.$get("FetchXml", $get("resultRender")).value = '<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true">' + xmlDoc.documentElement.innerHTML + '</fetch>';
	}
	targetFrame.changeArea(targetFrame.ResultsPage);
	targetFrame.$get('resultRender').submit();
} else {
	alert('Cannot locate Advanced Find Frame');
}
void 0;

Minified code for bookmark bar

javascript:var contentPanel=$('#crmContentPanel > iframe');if(contentPanel&&contentPanel.length>0){var targetFrame=contentPanel[0].contentWindow;var isFetchModified=false;targetFrame.ExecuteQuery();var xml=targetFrame.$get("FetchXml").value;var xmlDoc=$.parseXML(xml),$xml=$(xmlDoc);$xml.find('condition[value]').each(function(i,d){var inCondition='';var multipleValues=$(d).attr('value');if(multipleValues.indexOf(';')===-1)
return;multipleValues.split(';').forEach(function(c){inCondition+=('<value>'+c+'</value>');});$(d).attr('operator','in');$(d).removeAttr('value');$(d).html(inCondition);isFetchModified=true;});if(isFetchModified){targetFrame.$get("FetchXml",$get("resultRender")).value='<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true">'+xmlDoc.documentElement.innerHTML+'</fetch>';}
targetFrame.changeArea(targetFrame.ResultsPage);targetFrame.$get('resultRender').submit();}else{alert('Cannot locate Advanced Find Frame');}
void 0;

You basically continue to use the “equal” condition and then separate the valid values with semi-colon. Run the bookmarklet after that and the “equal” condition will be converted into a “in”. This currently works only for text fields. Datetime and number fields have additional format validations, hence it is not possible to use “;” to separate out the values.

I have tested this bookmarklet in CRM Online and CRM2015 with Firefox and Chrome.

Known Issues

A javascript error pops up before the results are displayed in Firefox. This can be ignored as the correct results are shown.

Bookmarklet: Turn off autosave & lookup on newwindow

The first bookmarklet turns off autosave for the current record, and refreshes the form without saving. Drag the below to your favorites bar.

javascript:(function(){var%20contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return%20d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){var%20Xrm=contentPanels[0].contentWindow.Xrm;Xrm.Page.data.refresh(false).then(function(){Xrm.Page.data.entity.addOnSave(function(econtext){var%20eventArgs=econtext.getEventArgs();if(eventArgs.getSaveMode()===70||eventArgs.getSaveMode()===2){eventArgs.preventDefault();}});alert('Form%20refreshed%20without%20save.%20Autosave%20turned%20off.');},function(errorCode,message){alert(message);});}else{alert('Entity%20form%20not%20detected');}})();void%200;

Below the unminified source.

(function () {
	var contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		var Xrm = contentPanels[0].contentWindow.Xrm;
		Xrm.Page.data.refresh(false).then(function () {
			Xrm.Page.data.entity.addOnSave(function (econtext) {
				var eventArgs = econtext.getEventArgs();
				if (eventArgs.getSaveMode() === 70 || eventArgs.getSaveMode() === 2) {
					eventArgs.preventDefault();
				}
			});
			alert('Form refreshed without save. Autosave turned off.');
		}, function (errorCode, message) {
			alert(message);
		});
	} else {
		alert('Entity form not detected');
	}
})();

The second bookmarklet open the selected lookup in a new window. Starting from CRM2015 every link you click inside a record form, opens in the same window. This can be annoying sometimes. To use this bookmarklet, you’ll just have to select the lookup on the record and execute the bookmarklet. This will open the lookup record in a new window. Below is the bookmarklet.

javascript:(function(){var%20contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return%20d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){var%20Xrm=contentPanels[0].contentWindow.Xrm;var%20currentControl=Xrm.Page.ui.getCurrentControl();if(currentControl.getControlType()==='lookup'){var%20currentLookup=currentControl.getAttribute().getValue();if(currentLookup){var%20entityName=currentLookup[0].type,entityId=currentLookup[0].id;var%20url=Xrm.Page.context.getClientUrl()+'/main.aspx?etc='+entityName+'&id='+entityId+'&newWindow=true&pagetype=entityrecord';window.open(url,'_blank');}}else{alert('The%20currently%20selected%20control%20is%20not%20a%20lookup');}}else{alert('Entity%20form%20not%20detected');}})();void%200;

This is the unminified source.

(function () {
	var contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		var Xrm = contentPanels[0].contentWindow.Xrm;
		var currentControl = Xrm.Page.ui.getCurrentControl();
		if (currentControl.getControlType() === 'lookup') {
			var currentLookup = currentControl.getAttribute().getValue();
			if (currentLookup) {
				var entityName = currentLookup[0].type,
				entityId = currentLookup[0].id;
				var url = Xrm.Page.context.getClientUrl() + '/main.aspx?etc=' + entityName + '&id=' + entityId + '&newWindow=true&pagetype=entityrecord';
				window.open(url, '_blank');
			}
		} else {
			alert('The currently selected control is not a lookup');
		}
	} else {
		alert('Entity form not detected');
	}
})();

References:

https://msdn.microsoft.com/en-us/library/gg509060.aspx

Bookmarklet: Theme Colour Picker

Only of the features lacking in the current CRM Theme form is colour picker. I logged issue 1197446 in Connect regarding this. But, it seems this is not really a priority at the moment. You can use Guido’s CRM Theme Generator to get around this and it is really excellent, as it also gives you instant feedback on how the theme will look. But I still like to see a colour picker in the theme form. So, I developed a bookmarklet to do this.

Copy paste this code into your favorites/bookmarks bar and execute this when you have opened the theme form.

javascript: var contentPanel=$('#crmContentPanel > iframe');if(contentPanel&&contentPanel.length>0){var targetFrame=contentPanel[0].contentDocument;var Xrm=contentPanel[0].contentWindow.Xrm;Array.from(targetFrame.querySelectorAll('.ms-crm-ColorValueDirection')).forEach(function(d,i){var c=targetFrame.createElement('input');c.setAttribute('id',d.getAttribute('id')+'_colorpicker');c.setAttribute('type','color');c.setAttribute('style','display: block; width: 50px; height: 16px; right: -54px; top: 1px; position: absolute;');c.value=targetFrame.querySelectorAll('#'+d.getAttribute('id')+' span')[0].textContent;d.appendChild(c);});targetFrame.getElementById('tdAreas').addEventListener('change',function(e){var targetIdParts=e.target.id.split('_');if(targetIdParts[1]!=='colorpicker')return;var mainDiv=targetIdParts[0];targetFrame.querySelectorAll('#'+mainDiv+' span')[0].textContent=e.target.value;targetFrame.getElementById(mainDiv+'_colord').style.background=e.target.value;targetFrame.getElementById(mainDiv+'_i').value=e.target.value;Xrm.Page.getAttribute(mainDiv).setValue(e.target.value);},false);}
else
{alert('Cannot locate theme IFrame');} void 0;

Here in the unminified source.

var contentPanel = $('#crmContentPanel > iframe');
if (contentPanel && contentPanel.length > 0) {
	var targetFrame = contentPanel[0].contentDocument;
	var Xrm = contentPanel[0].contentWindow.Xrm;
	Array.from(targetFrame.querySelectorAll('.ms-crm-ColorValueDirection')).forEach(function(d,i){
		var c = targetFrame.createElement('input');
		c.setAttribute('id',d.getAttribute('id')+'_colorpicker');
		c.setAttribute('type','color');
		c.setAttribute('style','display: block; width: 50px; height: 16px; right: -54px; top: 1px; position: absolute;');
		c.value = targetFrame.querySelectorAll('#'+d.getAttribute('id')+' span')[0].textContent;
		d.appendChild(c);
	});

	targetFrame.getElementById('tdAreas').addEventListener('change', function(e) {
		var targetIdParts = e.target.id.split('_');
		if(targetIdParts[1] !== 'colorpicker') return;
		var mainDiv = targetIdParts[0];
		targetFrame.querySelectorAll('#'+mainDiv+' span')[0].textContent = e.target.value;
		targetFrame.getElementById(mainDiv+'_colord').style.background = e.target.value;
		targetFrame.getElementById(mainDiv+'_i').value = e.target.value;
		Xrm.Page.getAttribute(mainDiv).setValue(e.target.value);
	},false);
}
else
{
	alert('Cannot locate theme IFrame');
}

Here is how it looks after you have executed the bookmarklet.

Colour Picker

I have tested this only in Firefox 43 and Chrome 47.

EDIT (27/01/16): Fixed hardcoded navbarbackgroundcolor in getAttribute. Apologies.

Bookmarklet: Open view in new window

Drag the below bookmarklet to the favorites bar in your browser, to quickly open the current view being displayed, in a new window. You can then probably bookmark the view Url, to assist with your daily housekeeping tasks. The current way the get the view url is Email a Link -> Of Current view from the command bar and then copy the url from the resulting Outlook window.

ViewCommandbar

This bookmarket will save you that 3 seconds to do that.

javascript:if(frames&&frames[0].$P_CRM&&frames[0].$P_CRM('#crmGrid')){var grid=frames[0].$P_CRM('#crmGrid')[0].control;if(grid){window.open(frames[0].Xrm.Page.context.getClientUrl()+'/main.aspx?etc='+grid.GetParameter('otc')+'&pagetype=entitylist&viewid='+grid.GetParameter('viewid')+'&viewtype='+grid.GetParameter('viewtype'),'_blank');}else{alert('Unable to find grid');}}else{alert('Unable to find grid');} void 0;

The source for this is as below

if (frames && frames[0].$P_CRM && frames[0].$P_CRM('#crmGrid')) {
	var grid = frames[0].$P_CRM('#crmGrid')[0].control;
	if (grid) {
		window.open(frames[0].Xrm.Page.context.getClientUrl() + '/main.aspx?etc=' + grid.GetParameter('otc') + '&pagetype=entitylist&viewid=' + grid.GetParameter('viewid') + '&viewtype=' + grid.GetParameter('viewtype'), '_blank');
	} else {
		alert('Unable to find grid');
	}
} else {
	alert('Unable to find grid');
}