Posted on 26. October 2012

No-Code Workflow Shortcut Ribbon Button

Users are always asking about making solutions 'less-clicky' - a common request is to provide a button to run a frequently used workflow rather than having to use the look up dialog.

The following solution shows you how to do this without writing a single line of code!

http://ribbonworkbench.uservoice.com/knowledgebase/articles/132235-create-a-workflow-short-cut-ribbon-button-no-code

Homepage button

Of course it is using the Ribbon Workbench for CRM2011. If you've not already, download it for free now!

Posted on 26. October 2012

Small is beautiful – Useful Ribbon Buttons

I am forever having to copy record link urls and extract the record GUID from the query string and remove the escape characters. Last week was the straw that broke the camel's back so I had to create a Ribbon Button to do it for me.

It is my pleasure to present to you the 'Useful Buttons' solution - small and simple - but most useful! It is a managed solution so that you can install when you need - and then remove without trace afterwards!

UsefulButtons_1_0_0_1_managed.zip (11.28 kb)

Features:

  1. Get Form Record ID – Allows you to copy to the clipboard the GUID of the current form Record
  2. Get Selected Record IDs - Allows you to copy to the clipboard all of the currently selected record GUIDs in a home or sub-grid.
  3. Refresh Record – Reloads the current form – Like pressing F5!
  4. Refresh Ribbon – Refreshes the current Ribbon – useful when developing but you haven't wired up any on-change events yet.

 

Of course, this was really easy to build using the Ribbon Workbench for CRM2011! Download it for free if you've not already!

 

Posted on 12. October 2012

Why is my Ribbon Button Disabled? (or CRM2011 caching under the covers)

 

Background

Today I had a most frustrating problem where my Ribbon button was visible but disabled no matter what I did to the Command.

This is usually caused by the Command missing or being invalid - but on further inspection of the RibbonLayout.js.aspx being loaded, the Command simply wasn't included in the initialisation objects.

IISRESET just doesn't cut it!

After a performing an IISRESET, the button was then enabled and the command was then included in the RibbonLayout.js.

IISRESET clears the server cache so I guessed the problem was down to caching - but I didn't want to be doing this every time I published. So I investigated further. I soon discovered that the issue was that the reference to to the RibbonLayout.js included two query string parameter rver and mver along the lines of :

/_controls/ribbon/RibbonLayout.js.aspx?
hierarchy=9886ead0-4fcc-4747-9a18-08e7a9a6de71
&id=EntityTemplateTab.lead.NoRelationship.Form.Mscrm.Form.lead.MainTab
&ise=1
&lcid=1033
&mver=830776802
&oc=0
&rver=-1307274159
&ver=-986909320

When I published my customisations, the mver (MetaData version) was incrementing, but the rver (Ribbon Version) was not. 

So for some reason the publish was not incrementing the ribbon version, and so my new command was not being loaded into the browser. The CRM 2011 cache is stored in the standard ASP.NET HTTP Cache - and it was this cache that holds the Ribbon Version. The publish is meant to invalidate this cache so that the new version can be used. Without this cache, every single request would have to hit the database to load customisations xml - so it is a very important part of the framework - but if it does not refresh automatically when changes are made - then the user interface is effectively stuck at a previous version.

Note: I also discovered that the cache was on a sliding 60 minutes refresh - so If I advanced the time by 1 hour, I would also see my new ribbon customisations and the rver number incremented. The rver is not actually an incremental number, but a hash of all of the 'last published' dates of the ribbon customisations. The main thing is that it is a unique key that points to the correct cache item.

So...Why was the cache not being invalidated?

Upon investigating how the CRM2011 cache is managed - there is a Notifications table in the MSCRM_CONFIG database that includes entries that instruct CRM to clear cache items. The Async Server picks these up and notifies the HTTP Worker processes to drop each cache by Key.

You can see what's going on for your server when you publish by using the following SQL:

CREATE TABLE  #eventNames (eventId int, eventName nvarchar(2000))
INSERT INTO #eventNames (eventId, eventName) VALUES (0,'netSecurityUser')
INSERT INTO #eventNames (eventId, eventName) VALUES (1,'netSecurityBusiness')
INSERT INTO #eventNames (eventId, eventName) VALUES (2,'netSecurityOrganization')
INSERT INTO #eventNames (eventId, eventName) VALUES (3,'netMetadata')
INSERT INTO #eventNames (eventId, eventName) VALUES (4,'netMetadataEntity')
INSERT INTO #eventNames (eventId, eventName) VALUES (5,'netSettingsOrganization')
INSERT INTO #eventNames (eventId, eventName) VALUES (6,'netSettingsUser')
INSERT INTO #eventNames (eventId, eventName) VALUES (7,'netSchedulingEngineInvalidateContainerCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (8,'netSchedulingEngineInvalidateResourceExpansionCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (9,'netSchedulingEngineInvalidateScheduleCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (12,'netSqmSessionReset')
INSERT INTO #eventNames (eventId, eventName) VALUES (13,'netSiteMap')
INSERT INTO #eventNames (eventId, eventName) VALUES (14,'netHardExpiry')
INSERT INTO #eventNames (eventId, eventName) VALUES (15,'netInfo')
INSERT INTO #eventNames (eventId, eventName) VALUES (16,'netMessageProcessorInvalidatePipelineCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (17,'netSavedQuery')
INSERT INTO #eventNames (eventId, eventName) VALUES (18,'netPluginTypeInvalidateCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (19,'netPluginAssemblyInvalidateCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (20,'netStepDescriptionInvalidateCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (21,'netStepImageDescriptionInvalidateCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (22,'netConfigCreate')
INSERT INTO #eventNames (eventId, eventName) VALUES (23,'netConfigUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (24,'netConfigDelete')
INSERT INTO #eventNames (eventId, eventName) VALUES (25,'netConfigAll')
INSERT INTO #eventNames (eventId, eventName) VALUES (26,'netScaleGroupSetState')
INSERT INTO #eventNames (eventId, eventName) VALUES (27,'netServerSetState')
INSERT INTO #eventNames (eventId, eventName) VALUES (28,'netEncryptionConfigurationChange')
INSERT INTO #eventNames (eventId, eventName) VALUES (29,'netOrganizationCreate')
INSERT INTO #eventNames (eventId, eventName) VALUES (30,'netOrganizationSetState')
INSERT INTO #eventNames (eventId, eventName) VALUES (31,'netOrganizationUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (32,'netOrganizationDelete')
INSERT INTO #eventNames (eventId, eventName) VALUES (33,'netSubscriptionEntity')
INSERT INTO #eventNames (eventId, eventName) VALUES (34,'netOutlookFilter')
INSERT INTO #eventNames (eventId, eventName) VALUES (35,'netCacheItemRemove')
INSERT INTO #eventNames (eventId, eventName) VALUES (36,'netStepSecureConfigInvalidateCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (37,'netTransactionCurrency')
INSERT INTO #eventNames (eventId, eventName) VALUES (38,'netLocator')
INSERT INTO #eventNames (eventId, eventName) VALUES (39,'netSchedulingEngineMustRunPublishResourceGroupExpansion')
INSERT INTO #eventNames (eventId, eventName) VALUES (40,'netOrganizationMove')
INSERT INTO #eventNames (eventId, eventName) VALUES (41,'netUserInvitationUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (42,'netSolution')
INSERT INTO #eventNames (eventId, eventName) VALUES (43,'netServerCreate')
INSERT INTO #eventNames (eventId, eventName) VALUES (44,'netServerMove')
INSERT INTO #eventNames (eventId, eventName) VALUES (45,'netServerDelete')
INSERT INTO #eventNames (eventId, eventName) VALUES (46,'netSecurityTeam')
INSERT INTO #eventNames (eventId, eventName) VALUES (47,'netRibbonCustomizationUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (48,'netDependencyNode')
INSERT INTO #eventNames (eventId, eventName) VALUES (50,'netDnsUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (51,'netSystemForm')
INSERT INTO #eventNames (eventId, eventName) VALUES (52,'netSrsExtensionsInstalled')
INSERT INTO #eventNames (eventId, eventName) VALUES (53,'netFederationProviderCreate')
INSERT INTO #eventNames (eventId, eventName) VALUES (54,'netFederationProviderUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (55,'netFederationProviderDelete')
INSERT INTO #eventNames (eventId, eventName) VALUES (56,'netCertificateCreate')
INSERT INTO #eventNames (eventId, eventName) VALUES (57,'netCertificateUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (58,'netCertificateDelete')
INSERT INTO #eventNames (eventId, eventName) VALUES (59,'netWebResource')
INSERT INTO #eventNames (eventId, eventName) VALUES (60,'netSolutionImportFailure')
INSERT INTO #eventNames (eventId, eventName) VALUES (62,'netFlushRecordCountSnapshotCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (63,'netFlushPrincipalObjectAccessReadSnapshotCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (64,'netMaxEventCount')


SELECT n.CreatedOn, l.eventName, n.EventData ,n.EventId, n.OrganizationId
FROM MSCRM_CONFIG.dbo.Notification n LEFT OUTER JOIN #eventNames l ON n.EventId = l.eventId
ORDER BY CreatedOn desc
  

The reason that my notifications were not getting through was that I had done some testing previously where the server time had been advanced a month. This meant that there were notifications sitting in this table queue that were for a month time. The Async server seems to use the latest date when it first starts to define a time window to query for new notifications. Obviously, in my case, the clear cache notifications (or netRibbonCustomizationUpdate events) were not being processed. I performed an unsupported DELETE from the MSCRM_CONFIG.dbo.Notifications table and all was well again. (phew!)

I hope this has provided you with a bit of an insight into how CRM2011 caching works and how you can work out what's going on if you find things not being reflected when you make changes to customisations.


 

Posted on 2. October 2012

Ribbon Workbench demo at eXtremeCRM!

Yesterday, Jim Daly demoed the Ribbon Workbench in his session on 'Dynamics CRM Extensibility:  Tools, Resources, and What’s New' at eXtremeCRM in Las Vagas. Coincidently, last week the Ribbon Workbench moved out of beta testing.

I'm really pleased with how the Ribbon Workbench has turned out - and a BIG THANK YOU to all those who have helped.

The coming months will see new features being introduced into version 2 - be sure to watch out for an up coming post with the road-map for the Ribbon Workbench.

 

 

Posted on 2. October 2012

Enable/Disable a ribbon button dynamically based on a form value

I've added another 'How To' article to the Ribbon Workbench knowledge base:

Enable/Disable a ribbon button dynamically based on a form value

Any suggestions for more? Let me know!