In the previous post, we looked at using the standard (button-click) way to create a new child record using Publisher Actions. Pretty basic stuff.
Using Visualforce to create a custom action is a bit harder. Let's start with the documentation.
- The PDF provided by clicking on the link in the Actions screen (Account action is an example) has nothing about Visualforce.
- Searching in the Help & Training section yields a page Creating Visualforce Pages to Use as Custom Publisher Actions, but I can't see where that page is found in the help tree on the left side of the page.
- Interestingly, to find correct code (because the item above has a misprint) I had to go to Customizing Case Feed with Visualforce (PDF).
Let's set up our use-case and see what we can do with a Visualforce publisher.
As before, we want to create a Task from a Case. The options when using the CaR publisher are, to review:
- Global action, which auto-fills zero fields
- Case action, which auto-fills the "What" field but doesn't fill the "Who" field.
We want to fill both the polymorphic lookup fields. We could insert default values for the other fields, but that can be done with the standard publisher and we'll see where default values can be determined in the code.
First, we need a Visualforce page. There are differences between this one and the one in the Help & Training area. Yes, that one creates a Case from an Account, but it also has some differences in its JavaScript. Check closely. Also, I changed the html somewhat so a little CSS would make things a bit nicer than the ugly bare div tags in the example.
<apex:page standardcontroller="Case" extensions="CreateCaseTaskExtension" showHeader="false"> <script type='text/javascript' src='/canvas/sdk/js/28.0/publisher.js'/> <style> .requiredInput .requiredBlock, .requiredBlock { background-color: white; } .custompubblock div { display: inline-block; } .custompublabel { width:100px; } </style> <script> function refreshFeed() { Sfdc.canvas.publisher.publish({name : 'publisher.refresh', payload : {feed: true}}); } </script> <apex:form > <apex:actionFunction action="{!createTask}" name="createTask" rerender="out" oncomplete="refreshFeed();"/> <apex:outputPanel id="out" > <div class="custompubblock"> <div class="custompublabel">What:</div> <apex:inputField value="{!theTask.WhatId}" style="margin-left:0;"/><br /> <div class="custompublabel">Who:</div> <apex:inputField value="{!theTask.WhoId}" /><br /> <div class="custompublabel">Subject:</div> <apex:inputField value="{!theTask.Subject}" /><br /> </div> <div class="custompublabel">Description:</div> <apex:inputField value="{!theTask.description}" style="width:500px; height:92px; margin-top:4px;" /><br /> <div class="custompubblock" style="margin-top:5px;"> <div class="custompublabel">Status:</div> <apex:inputField value="{!theTask.status}" /><br /> <div class="custompublabel">Priority:</div> <apex:inputField value="{!theTask.priority}" /><br /> <div class="custompublabel">Type:</div> <apex:inputField value="{!theTask.Type}" /> </div> <div style="color:red;">{!lastError}</div> </apex:outputPanel> </apex:form> <br/> <button type="button" onclick="createTask();" style="position:fixed; bottom:0px; right:0px; padding:5px 10px; font-size:13px; font-weight:bold; line-height:18px; background-color:#0271BF; background-image:-moz-linear-gradient(#2DADDC, #0271BF); background-repeat:repeat-x; border-color:#096EB3;" id="addcasebutton"> Create Task </button> <apex:outputField value="{!case.ContactId}" rendered="false" /> </apex:page>
And here's the controller extension:
public with sharing class CreateCaseTaskExtension { private Case c; public CreateCaseTaskExtension(ApexPages.StandardController theController) { this.c = (Case)theController.getRecord(); defineNewTask(); } public Task theTask {get; set;} public String lastError {get; set;} public void defineNewTask(){ theTask = new Task(); theTask.WhatId = c.id; theTask.WhoId = c.ContactId; theTask.Priority = 'Normal'; lastError = ''; } public PageReference createTask() { createNewTask(); defineNewTask(); return null; } private void createNewTask() { try { insert theTask; FeedItem post = new FeedItem(); post.ParentId = c.id; post.Body = 'created a task'; post.type = 'LinkPost'; post.LinkUrl = '/' + theTask.id; post.Title = theTask.Subject; insert post; } catch(System.Exception ex){ system.debug('ERROR: ' + ex.getMessage()); lastError = ex.getMessage(); } } }
Next, create a new Case Action and set the type to Custom Action:
Only Visualforce pages using the Case standard controller are available here. The height of the frame must be specified (which will be a problem). The custom icon must be in a static resource; I haven't found anything about specifics/limits on those image files, but I don't see how those images can be put in a zip file, as the dialog box seems to say that the icons must each be an individual static resource.
This is the output:
That's it. These are some things I recommend when doing something like this:
- Make sure that you are using CSS because you don't have access to any standard stylesheets
- Don't use dynamic rerendering unless you can guarantee that the page size won't change
- Make one CSS static resource for every similar action in your org, to maintain a consistent look-and-feel
- If you do try to use the standard styles that you can find when using standard CaR actions, be careful, as those may change without notice
- Enjoy! What use-cases do you have for this feature? I can think of a few, including a custom record approval screen.
William Moxley (@wjmoxley) says
Nice post David.
Dhaval says
In this post its clear how to create an action. But how to use it? you have not mentioned any step after creating action. I tried with creating page and action but then what i need to do?
David Schach says
Go to the chatter editor and choose the action from the available ones. (Be sure to add it to the list there first.)