[SalesForce] How to add and query custom buttons/links to a visualforce page

So, in the salesforce UI, users can add buttons and links to objects. But I have an app in a visualforce page that I want users to be able to add the same buttons to.

If this is possible, where do you go to add them, then how do you query them and their executable url so I can create the buttons on my page based on what they added?

Is such a thing possible?

Best Answer

I didn't know if this was possible or not, so I took a look into it.

Starting off, you should take a look at the $Action global variable. It allows you to, in visualforce (most often in conjunction with something like <apex:outputLink> or <apex:commandButton>), reference buttons, links, and actions on a given object.

The documentation doesn't do a great job of explaining everything you can do with $Action. The typical syntax you'll see is URLFOR($Action.<object name>.<action name>) like URLFOR($Action.Opportunity.New). You can, however, also use array notation to use string literals to specify the object name and action name. Custom buttons/links/actions can also be referenced with $Action

public class TestControllerExtension{
    public String getObjectName(){
        return 'Opportunity';
    }

    public String getActionName(){
        return 'New';
    }
}
<apex:page standardController="Opportunity" extensions="TestControllerExtension">
    <apex:form>
        <!-- Both of these commandButtons are perfectly valid -->
        <apex:commandButton action="{!URLFOR($Action[objectName][actionName])}" />
        <!-- The second parameter to URLFOR is a recordId, and is optional (though
          buttons/links/actions probably won't work correctly without it) -->
        <apex:commandButton action="{!URLFOR($Action['Opportunity']['edit'], Opportunity.Id)}" />
    </apex:form>
</apex:page>

The next step is to figure out how to get a list of button names. It turns out that you can query the Weblink SObject to do this.

For example, to fetch all of the buttons you have on the Opportunity SObject...

[SELECT Id, Name, MasterLabel FROM WebLink WHERE PageOrSobjectName = 'Opportunity']

With the query above, you can present your users with a configuration page where they can select which buttons they want to have included on your App's main visualforce page. You'll need to store user elections somewhere (like a Custom Metadata Type, a Custom Setting, or a full-blown SObject).

At this point, you have all the information you need to display your user-selected buttons in your app's main visualforce page.

The example that I cooked up as a proof of concept doesn't take care of storing/recalling user-selected buttons, but it should serve as a good base for you to work off of.

Controller Extension:

public class TestReassignOLIExtension {
    // Basic setup, declare a variable to store the object instance contained
    //   in the standard controller, and populate it in the constructor
    public Opportunity opp;
    public TestReassignOLIExtension(ApexPages.StandardController con){
        opp = (Opportunity)con.getRecord();
    }

    // I'm returning a List<Weblink> here, and am querying a few extra fields.
    // You don't _need_ to do things this way (and you probably won't), it was
    //   simply convenient for my proof-of-concept.
    // Basically, all we really need is the button name and the MasterLabel
    public List<Weblink> getButtons(){
        // There may be other things that you would want to concern yourself with.
        // DisplayType, for instance, tells you whether a weblink is a button, a link,
        //   or a list button.
        // You may run into issues if you tried to shove a list button into an <apex:outputLink>
        //   for example.
        return [SELECT Id, Name, PageOrSobjectType, LinkType, DisplayType, MasterLabel FROM Weblink WHERE PageOrSobjectType = 'Opportunity'];
    }
}

Visualforce page:

<apex:page standardController="Opportunity" extensions="TestReassignOLIExtension">
<apex:form >
    <!-- apex:repeat allows us to create multiple buttons without repeating markup -->
    <apex:repeat var="button" value="{!buttons}">
        <!-- $Action really isn't useful outside of URLFOR, so far as I can tell.
          The 'Name' field of Weblink is what we use with $Action.
          The 'MasterLabel' contains the 'display name' (as opposed to an API Name)
            and is what normally appears in the buttons when they are included
            in object page layouts -->
        <apex:commandButton action="{!URLFOR($Action.Opportunity[button.Name], Opportunity.Id)}" value="{!buttonName.MasterLabel}"/>
    </apex:repeat>
</apex:form>
</apex:page>