Skip navigation

Category Archives: Publisher Actions

One of the most hyped, and in my opinion least documented features of the Summer 13 release of Salesforce is custom Chatter Publisher Actions. Various Salesforce bloggers have written about how easy it is to create and use regular Chatter Publisher Actions, which, basically, just let you quickly create records related to a primary record from your Chatter Feed (e.g. from an Account’s Chatter feed, creating a Contact or Opportunity, without having to go down to the corresponding related lists. For one excellent discussion of regular Publisher Actions, see Daniel Hoechst’s post. The release notes also give examples of regular Publisher Actions, so I won’t go into any more depth on them here.

However, neither the release notes, nor any other bloggers, nor Salesforce themselves in their recent release webinars, have yet shown any working examples of custom Publisher Actions. What are they? Here’s a brief overview:

  • They come in two flavors: Global and Object-Specific.
    • Global custom Actions: can be added to any Chatter Publisher Layout, anywhere.
    • Object-specific custom Actions: can only be added to one specific object’s Publisher Layouts.
  • You create them using Visualforce Pages.
    • For Global custom actions, your Visualforce page must either have no controller, or a custom controller that is NOT an extension.
    • For Object-specific custom Actions, your Visualforce page must use the standard controller of the object you’d like to use the Action with.
  • Chatter implements custom Actions using iFrames.
    • When creating a new custom Action, you must specify a height for your Action — this is setting the height of the iFrame in which your page will be included.
    • This iFrame is always run from within the Visualforce Page’s domain. So, if it’s a local VF Page, the domain will be something like “c.na14.visual.force.com”, whereas if the page is included in a managed package, the domain will be something like “skuid.cs15.visual.force.com”.

So custom Publisher Actions are, by virtue of having to be created through Visualforce Pages, and being embedded in iFrames, already more complicated to develop. This begs the question: what sort of “Action” would be compelling enough to warrant going through this hassle? What’s a good use case?

A use case: create a Contact and linked Opportunity Contact Role all at once, from an Opportunity

Many of the examples I thought of all fall into one category: creating records of multiple objects all at once, in particular, creating a junction object and creating the joined record if it doesn’t exist yet. For example, from an Opportunity record, you often want to create a new Opportunity Contact Role, but can’t because the related Contact doesn’t exist yet. Or, in a hypothetical iTunes-on-Force.com app, from an Album’s page, create a “Genre Association” junction object as well as a new Genre if it doesn’t exist yet.

I finally settled on the “Create a Contact and Opportunity Contact Role all at once” example.

Making it happen

The first step in creating a Custom Publisher Action is to create the Action’s associated Visualforce Page. For this example, my Custom Action is specific to the Opportunity object, so our Page must use the Opportunity Standard Controller. As for the body of the page, since I’m using Skuid, I just need to specify the name of a Skuid Page I want to include, which we’ll build later.

VisualforcePage_ForFeedAction

Now that we have a Visualforce Page associated to the Opportunity standard controller, we can create our Action!

The first step is to go to the newly-reorganized Opportunity “Buttons, Links, and Actions” section. In Summer 13, Salesforce has consolidated Custom Buttons, Custom Links, Standard Actions and Overrides (e.g. the place where you can override what happens for the “View”, “Edit”, “New”, and “Tab” action for each object), and the new Chatter Publisher Actions, into this one list for each Object.

Click on “New Action”, and then enter the following:

ButtonsLinksAndActions NewAction

Notice two things:

  1. The “Height” attribute corresponds directly to the height of the iFrame which will embed your Visualforce page
  2. The “Label” is the actual label that users will see for your new Publisher Action, e.g. they’ll see “Link”, “File”, “Post”, “Poll”, and “Add Contact”.

Now that we’ve created the action, we need to actually add it to one of our Opportunity page layout’s Publisher Actions area. By default, all objects will inherit from the default “Global” Publisher Action area, so we’ll need to explicitly “break” this inheritance to allow the set of Publisher Actions that are displayed on our Opportunity layout(s) to be different than the Publisher Actions for all other objects. TO do this, click “override the global layout” in the “Publisher Actions” area of one of your Opportunity page layouts.

OverrideTheGlobalLayout

Next, drag in the “Add Contact” Publisher Action we just created, and position it as you’d like relative to the other Publisher Actions:

DragInNewAction

Once we save the Page Layout, we’re ready to roll! This Publisher Action should show up in our Opportunity page’s Chatter feed. Because we positioned it last in the list, and there’s more than 4 Publisher Actions, our Action shows up under the “More” area:

SelectingThePublisherAction

Now, we’ll show how we built this with Skuid later, but here’s what our custom Publisher Action actually looks like:

PublisherAction_InAction

We’re presented with two columns of data. In the first column, we enter some bare bones info for a new Contact record we want to create that’s associated with this Opportunity’s Account. In the second column, we define the Opportunity Contact Role. Once we click Save, what Skuid will do is the following:

  1. Create a new Contact record using the fields we filled out, with the AccountId of the Opportunity in context (“Acme”)
  2. Create a new Opportunity Contact Role record, using the fields we entered, and associated to the Opportunity in context (“Acme”) and our newly created Contact (“Bilbo Baggins”)
  3. Create a new post in this Opportunity’s Chatter Feed describing what just happened.
  4. Refreshing the page — we’ll describe why this is necessary in a second.

In the Skuid page I built in place of the standard Opportunity detail page, I display Opportunity Contact Roles in a “Key People” tab. As you can see, a new “Bilbo Baggins” contact record was created, and an Opportunity Contact Role record linking him to the “Acme – 500 Widgets” Opportunity was created as well.

ContactRole_CreatedByFeedAction

Finally, a new FeedItem was created that records the addition of the new Contact and his Role:

FeedItem_CreatedByAction

The Skuid page that made the real magic happen

Getting the actual Publisher Action configured is pretty quick once you’ve got a Visualforce Page on hand to use. But it’s this Visualforce Page, of course, which has to do all the hard work to make the custom Publisher Action useful.

Using Skuid as the content of the Publisher Action made the process very fast and fairly painless. In a nutshell, here’s what I did:

(1) Create a new Skuid Page using the “New Page” template on the Contact object.

The “New Page” template, here, scaffolds a basic Skuid Page for use in creating brand new Contact records. It sets up a Model on the Contact page with “Create Default Row if None Exists” pre-checked, and adds a Field Editor component with the FirstName and LastName fields already thrown in:

NewSkuidPage

ContactModelProperties

(2) Add in 3 additional Models: on the Opportunity, OpportunityContactRole, and FeedItem objects:

AddlModels

The Opportunity Model grabs the Opportunity record in context from the page, using the “id” URL Parameter. We then grab the AccountId lookup field from the Opportunity, so that we can use this to automatically prepopulate the AccountId of our newly-created Contact:

OppConditions OppFields

For the Opportunity Contact Role model, we pull in the Role and IsPrimary fields so that we can display them in the page, and we add 2 “Model Merge” Conditions to associate the new record to the context Opportunity and the newly-created Contact:

OppCRConditions

Finally, for the FeedItem model, we add a single condition to automatically associate the new FeedItem to the Opportunity in context, and we add in the Body field, which we will edit in our Save action:

FeedItemConditions FeedItemFields

(3) Place 2 Field Editors, one each for the Contact and ContactRole Models, in separate Panels:

PanelSetLayout

(4) Embed this Panelset within the first step of a new Wizard Component

-We could have used a PageTitle component instead, but we do this because it allows for easy transition into multi-step wizards. We could easily have put the Opportunity Contact Role fields within a separate step of the wizard, and had a “Next Step”/”Previous Step” sequential navigation here.

Wizard

(5) Create a custom Inline (Snippet) to use for our “Save All” Wizard Step action

Skuid’s standard wizard action types almost suffice here for achieving everything we want to do, but we want to automatically populate the Body field of our new FeedItem, which takes a little code, and,  because we’re using this Page as a Chatter Publisher Action, which is run in an iFrame, we run in to a Gotcha involving using the URL Redirect action, as we need to reload the location of our frame’s parent, not the frame itself (window).

First, we customize our Action to execute a Snippet called Save All:

SaveAllStepAction

Finally, we create the Inline (Snippet) itself:

InlineSnippet

//
// Get our Models
var contactModel = skuid.model.getModel('ContactData'),
    contactRoleModel = skuid.model.getModel('ContactRole'),
    feedItemModel = skuid.model.getModel('FeedItem');

// Get the first rows in each of our Models
var contact = contactModel.getFirstRow(),
    contactRole = contactRoleModel.getFirstRow(),
    feedItem = feedItemModel.getFirstRow();

var bodyText = 'Added new ' + contactRole.Role
    + ' to this Opportunity: '
    + contact.FirstName + ' ' + contact.LastName
    + ' (' + contact.Title + ').';

// Update the Body of our new Feed Item
feedItemModel.updateRow(feedItem,'Body',bodyText);

// Save our Models,
// and when we're done,
// refresh the iframe's parent

skuid.model.save(
    [contactModel,contactRoleModel,feedItemModel]
,{ callback: function() {
    parent.location.reload();
}});

In the Snippet, we do the following:

  1. Get references to 3 of our Models
  2. Get references to their first rows
  3. Populate the “Body” field of the FeedItem model’s first row with the Role field from our OpportunityContactRole record and the First and Last Name fields from our Contact record
  4. Save all 3 of our Models in sequence, starting with the Contact model (VERY important) so that the later models can use the newly-created Contact’s Id to populate their new records.
  5. Once the save is done, we reload our parent window. This is necessary because this Skuid page will be stuck into an iFrame by Chatter.

Thorny Gotchas, and the undocumented Chatter Publisher API

The basic buildout of this took less than 30 minutes — seriously! However, we spent some time trying to work out how best to “talk” to the Chatter Publisher using the undocumented Chatter Publisher API — an effort which ended in nothing but frustration.

The thorny gotchas to avoid are mostly related to the fact that custom Publisher Actions are implemented using iFrames:

  1. Any  JavaScript you write in your child Visualforce Page will be unable to talk to the parent page UNLESS both your page and the page containing the Chatter component are in the same domain, due to cross-domain script issues — which is impossible unless BOTH pages are Visualforce. Consider these scenarios:
    1. Chatter Feed in “na15.salesforce.com”, Custom Action VF Page in “c.na15.salesforce.com” — BLOCKED.
    2. Chatter Feed in “c.na15.visual.force.com”, Custom Action VF Page in “skuid.na15.visual.force.com” — BLOCKED.
    3. Chatter Feed in “c.na15.visual.force.com”, Custom Action VF Page in “c.na15.visual.force.com” — SUCCEEDS.
    4. Chatter Feed in “skuid.na15.visual.force.com”, Custom Action VF Page in “skuid.na15.visual.force.com” — SUCCEEDS.
  2. We couldn’t find a way, using the undocumented Chatter API, to “refresh” the Chatter Feed to show our newly-created Feed Item, so we had to do a full page refresh to get the Chatter Feed to show this. We tried the following methods, none of which worked:
    1. chatter.getPublisher().resetPublisher()
    2. chatter.getPublisher().submit()
    3. chatter.getFeed().refresh()
    4. chatter.getFeed().showNewUpdates()

We would have LOVED to use one of these methods in our Skuid “saveAll” Snippet, but we’ll have to wait for Chatter to document its API.

Screen shot 2013-05-15 at 1.04.16 PM