Saturday, May 16, 2009

Assignment Rules Using APEX

Did you know you can use the assignment rules functionality that is provided to you with the standard Salesforce.com interface with Apex code?

This code creates a Case, sets the owner using the standard assignment rules, and sends the email to the new Case owner. It could also be used for any other object that support assignment rules.

Apex Code:

// Instantiate a new Case
Case newCase = new Case();

// Put here the code to initialize the fields with the information...

// Instantiate the database options
Database.DMLOptions dlo = new Database.DMLOptions();

// This line assigns the ID for the assignment rule you want to use
// dlo.assignmentRuleHeader.assignmentRuleId = getAssignmentRuleId();

// This line assigns the active assignment rule
dlo.assignmentRuleHeader. useDefaultRule = true;

// This line sends email to the related contact.
dlo.EmailHeader.triggerAutoResponseEmail = true;

// This line sends email to the new case owner
dlo.EmailHeader.triggerUserEmail = true;

// Set the options just defined
newCase.setOptions(dlo);

// Create case.
insert newCase;

API access with no password expiration

When you create an API integration package, you may want to have it run by an account whose password never expires.

By default the applications that connect to Salesforce.com via the API require a username, a password and a token. How would you set this up, so that the credentials do not expire?

  1. Create a profile for your API user
    1. Ensure the API checkbox is checked.
    2. Ensure password not expire checkbox is checked.
    3. Set other security options required by your application.
  2. Create a user you will use for the API logins and assign it to this profile.
  3. The application should run on a computer in a trusted IP address range (it could be located inside your network, on a trusted ISP, or it may connect to the network using a VPN).
    1. Login in via the API from the computer where the application is installed
    2. Check the IP address used here (Setup > Personal Setup > My Personal Information > Personal Information > Login History (Related List))
    3. Ensure that IP address is in a range defined here (Setup > Administration Setup > Security Controls> Network Access > Trusted IP Range Edit)

By executing the step #3, we are avoiding the use of a security token. This is still secure, because we are specifying the IP range of the computer where we know the application will always execute.

Saturday, May 9, 2009

What should go in the List parameter for a <apex:relatedList List=""> tag?

To answer this question, I decided to build a VisualForce page that would be as close as possible to the pages created using the standard page layouts.

For this task, I created two custom objects to represent a flight. One custom object would contain information about the flight and the second custom object would have information for the city. This picture ilustrates how I set up the relationships between them.

This is how I defined each of my objects:

This is the definition for "City":

Nothing special here, but note there is a field called "ABC" (it will be important when I explain the rules for naming child relationships)

This is the definition for "Flight":

There are some basic fields ("Arrival","Departure", ...) and there are two lookup fields to "City". One for the "Origin" and one for the "Destination".

This is how I defined the relationships:

This image illustrates how the "Origin" field is defined:

Note there is an error in the definition of the "Child Relationship Name".

This entry is very important to answer this post's question. These are the rules to the values you can enter:

  1. Only alphanumeric characters (Letters, numbers and underscore) are allowed
  2. Must begin with a letter
  3. Can’t end with an underscore
  4. Can’t contain two consecutive underscore characters.
  5. Must be unique across all city fields
    1. Can not be "ABC", because there is one field named "ABC" in City. I defined this field, to explain this point.
    2. Can not be the name of a different relationship… Although it may not be technically accurate, it helps if you think these relationships create a field in the destination object (City in this case).
With these rules, I used these "child relationship names":

  • Origin_Flights
  • Destination_Flights

Now I can answer this blog's question:

<apex:relatedList list="Origin_Flights__r" />
<apex:relatedList list="Destination_Flights__r" />
Simulating a standard page layout in VisualForce

My next step, create a page that looked as closed as possible to the ones using standard page layouts. Basically, this was my goal:

As you can see, there are a lot of standard related lists. How could I get their names? Eclipse will tell you:

With that information, I created a page that looked as close to the standard page layout as I could.

Before I show you the results, let me tell you there are some differences, but I will leave them for a future post. For example, I did not created the JavaScript to show the related lists Divs on mouse over. The section header is not identical (some links are missing)

This is what I got:

Finally, here is the code...

VisualForce Page:

<apex:page standardController="City__c" showHeader="true" tabStyle="City__c">
    <apex:sectionHeader title="City" subtitle="{!City__c.Name}" help="/help/doc/user_ed.jsp?loc=help&target=getstart_help.htm§ion=Getting_Started" />
    This page is made in visualFroce simulating a Page Layout Page... This is the description... blah... blah... blah<br/><br/>
    <apex:detail relatedList="false" title="false" />
    <apex:relatedList list="OpenActivities"/>
    <apex:relatedList list="ActivityHistories"/>
    <apex:relatedList list="NotesAndAttachments"/>
    <apex:relatedList list="Origin_Flights__r" />
    <apex:relatedList list="Destination_Flights__r" />
    <apex:relatedList list="ProcessSteps"/>
    
<H1>Other Related Lists Available from VisualForce</H1>

    <apex:pageblock title="City History">
        <apex:pageBlockTable value="{!City__c.Histories}" var="h">
            <!-- h.ID, h.ParentId -->
            <apex:column headerValue="Date">
                <apex:outputField value="{!h.CreatedDate}"/>
            </apex:column>
            <apex:column headerValue="User">
                <apex:outputField value="{!h.CreatedById}"/>
            </apex:column>
            <apex:column headerValue="Field">
                <apex:outputText value="{!h.Field}"/> <!-- This is a picklist -->
            </apex:column>
            <apex:column headerValue="From">
                <apex:outputField value="{!h.OldValue}"/>
            </apex:column>
            <apex:column headerValue="To">
                <apex:outputField value="{!h.NewValue}"/>
            </apex:column>
            <apex:column headerValue="IsDeleted">
                <apex:outputField value="{!h.IsDeleted}"/>
            </apex:column>
        </apex:pageBlockTable>
    </apex:pageblock>
    <apex:pageblock title="City Tags">
        <apex:pageBlockTable value="{!City__c.Tags}" var="t">
            <!-- t.Id, t.ItemId-->
            <apex:column headerValue="TagDefinitionId">
                <apex:outputField value="{!t.TagDefinitionId}"/>
            </apex:column>
            <apex:column headerValue="CreatedDate">
                <apex:outputField value="{!t.CreatedDate}"/>
            </apex:column>
            <apex:column headerValue="SystemModstamp">
                <apex:outputField value="{!t.SystemModstamp}"/>
            </apex:column>
            <apex:column headerValue="IsDeleted">
                <apex:outputField value="{!t.IsDeleted}"/>
            </apex:column>
            <apex:column headerValue="Name">
                <apex:outputField value="{!t.Name}"/>
            </apex:column>
            <apex:column headerValue="Type">
                <apex:outputField value="{!t.Type}"/>
            </apex:column>
        </apex:pageBlockTable>
    </apex:pageblock>
</apex:page>

Thursday, January 29, 2009

Inter-window communication (Pop-up & IFrames)

This post explains a Javascript technique called "Inter-window communication". You could use this technique in VisualForce pages to open a pop-up window and have both windows talk among them.

For this post I'll have two HTML windows: A opener window and a pop-up window.

Opener.html:

<html>
<head>
    <title>*** OPENER ***</title>
</head>
<body>
    <form id="form1" runat="server">
    <table border="1">
        <tr>
            <th>
                Counter:
            </th>
            <td>
                <input type="button" onclick="Count();" value="Count" />
                <input type="text" id="ShowCounter" value="0" />
            </td>
        </tr>
        <tr>
            <th>
                Message:
            </th>
            <td>
                <input type="button" onclick="Write();" value="Write" />
                <span id="OutMsg">...</span>
                <input type="text" id="InMsg" value="..." />
            </td>
        </tr>
        <tr>
            <th>
                Other Window:
            </th>
            <td>
                <input type="button" onclick="OpenWindow();" value="Open Window" />
                <input type="button" onclick="UpdatePopPup();" value="Update PopUp" />
                <input type="button" onclick="UpdateIFrame();" value="Update IFrame" />
            </td>
        </tr>
    </table>
    </form>
    <iframe id="MyFrame" name="MyFrame" src="pop-up.html" width="600px" height="150px">
    </iframe>
</body>
</html>

<script type="text/javascript" language="javascript">
    // Local Scripts
    function Count() {
        var objCounter = document.getElementById('showCounter')
        objCounter.value = ++objCounter.value;
    }
    function Write() {
        WriteMessage(document.getElementById('InMsg').value);
    }
    function WriteMessage(inMsg) {
        var objMsg = document.getElementById('OutMsg');
        objMsg.innerHTML = inMsg;
    }
</script>

<script type="text/javascript" language="javascript">
    // Other window scripts
    var newWindow = null;
    function OpenWindow() {
        newWindow = window.open('pop-up.html');
    }
    function UpdateOpener(txtMsg, txtCounter) {
        document.getElementById('InMsg').value = txtMsg;
        document.getElementById('ShowCounter').value = txtCounter;
        Write();
    }
    function UpdatePopPup() {
        if (newWindow != null) {
            Write();
            var txtMsg = document.getElementById('InMsg').value;
            var txtCounter = document.getElementById('ShowCounter').value;
            newWindow.UpdatePopPup(txtMsg, txtCounter);
        }
    }
    function UpdateIFrame() {
        if (document.getElementById("myFrame") != null) {
            Write();
            var txtMsg = document.getElementById('InMsg').value;
            var txtCounter = document.getElementById('ShowCounter').value;
            document.getElementById("myFrame").contentWindow.UpdatePopPup(txtMsg, txtCounter);
        }
    }
</script>
pop-up.html:
<html>  
<head>  
    <title>*** POP-UP ***</title>  
</head>  
<body>  
    <form id="form1" runat="server">  
    <table border="1">  
        <tr>  
            <th>  
                Counter:   
            </th>  
            <td>  
                <input type="button" onclick="Count();" value="Count" />  
  
                <input type="text" id="ShowCounter" value="0" />  
            </td>  
        </tr>  
        <tr>  
            <th>  
                Message:   
            </th>  
            <td>  
                <input type="button" onclick="Write();" value="Write" />  
                <span id="OutMsg">...</span>  
  
                <input type="text" id="InMsg" value="..." />  
            </td>  
        </tr>  
        <tr>  
            <th>  
                Other Window:   
            </th>  
            <td>  
                <input type="button" onclick="CloseWindow();" value="Close Window" />  
  
                <input type="button" onclick="UpdateOpener();" value="Update Opener" />  
            </td>  
        </tr>  
    </table>  
    </form>  
</body>  
</html>  
  
<script type="text/javascript" language="javascript">  
    // Local Scripts   
    function Count() {   
        var objCounter = document.getElementById('showCounter')   
        objCounter.value = ++objCounter.value;   
    }   
    function Write() {   
        WriteMessage(document.getElementById('InMsg').value);   
    }   
    function WriteMessage(inMsg) {   
        var objMsg = document.getElementById('OutMsg');   
        objMsg.innerHTML = inMsg;   
    }   
</script>  
  
<script type="text/javascript" language="javascript">  
    // Other window scripts   
    function CloseWindow() {   
        window.top.close();   
        UpdateOpener();   
    }   
    function UpdateOpener() {   
        if (window.opener != null) {   
            Write();   
            var txtMsg = document.getElementById('InMsg').value;   
            var txtCounter = document.getElementById('ShowCounter').value;   
            window.opener.UpdateOpener(txtMsg, txtCounter);   
        }
        if (window.parent.frames.length>0) {
            Write();   
            var txtMsg = document.getElementById('InMsg').value;   
            var txtCounter = document.getElementById('ShowCounter').value;   
            window.parent.UpdateOpener(txtMsg, txtCounter);   
        }
    }   
    function UpdatePopPup(txtMsg, txtCounter) {   
        document.getElementById('InMsg').value = txtMsg;   
        document.getElementById('ShowCounter').value = txtCounter;   
        Write();   
    }   
</script>  
The pop-up works because:
  • When the window is opened (opener.html, line 63), the window is assigned to a variable "newWindow".
  • On the pop-up window, the javascript refers to the "window.opener" object (pop-up.html, line 70).
The IFrame works on a similar concept, but the syntax is a bit different.

How do I prevent duplicates based on more than one field?

Recently, I had the need to create a unique index based on more than one field.

I required a sObject whose name field is unique for each user. A user can not have two or more records with the same "name", but it is possible for different users to have records with the same name.

Let's suppose I have an sObject with this data:

Owner Name
user1 name1
user2 name2

If "user1" tries to add a row named "name1", the system should prevent him from doing so because "user1" already has a row with "name1". On the other hand if "user2" tries to add a row named "name1" the system should allow this, because "user2" does not have a row named "name1".

My first idea, was to have a formula field that merged the two values and make that formula field be a primary key. But formula fields can not keys.

So I decided on a trigger, and this worked fine...

trigger PreventDuplicateNameForUser on TPName__c (before insert, before update) {
    for (TPName__c TPName : Trigger.new) {
        List<TPName__c> listFound = [SELECT name
                                       FROM TPName__c
                                      WHERE ownerid = :UserInfo.getUserID()
                                        AND name = :TPName.name];
        if (listFound.size() > 0) {
            Trigger.new[0].addError('You already defined a Parameter Name called ['
                                     + TPName.name +']. Please use another name and try again');
        }
    }
}

How can the controller read the page?

Let's suppose you have a page (or part of a page) that you want to send via email. How can you get the contents of the page in the controller?

Since the contents of the page cannot be obtained by the controller, why not let the page send its contents to the controller?

VisualForce page:

<apex:page controller="MyEmail">
    <div ID="renderedPage">
        ... Put here the HTML content you want to get (it could be the entire page) ...
    </div>
    <apex:form >
        <apex:actionFunction name="setApexHTML" action="{!sendEmail}" rerender="AJAXSection">
            <apex:param name="strHTML" value="" />
        </apex:actionFunction>
    </apex:form>
    <apex:outputPanel id="AJAXSection"></apex:outputPanel>
    <script language="javascript">
        function getJSHTMLData() {
            var divPage = element = document.getElementById('renderedPage');
            var strHTML = divPage.innerHTML;
            setApexHTML(strHTML);
        }

        // Execute on load
        var previousOnload = window.onload;
        window.onload = function() {
            if (previousOnload) {
                previousOnload();
            }
            getJSHTMLData();
            alert('Email sent');
        }
    </script>
</apex:page>
Controller:
public class MyEmail {
    public void sendEmail() {
        List<String> emailAddress;
        String htmlContent = ApexPages.currentPage().getParameters().get('strHTML');

        // Send email
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

        // To
        emailAddress = new List<String>();
        emailAddress.add('user@acme.com');
        mail.setToAddresses(emailAddress);

        // Cc
        emailAddress = new List<String>();
        emailAddress.add('smith@gmail.com');
        mail.setCcAddresses(emailAddress);

        // Reply to
        mail.setReplyTo('support@acme.com');
        mail.setSenderDisplayName('Acme Support');

        // Bcc
        mail.setBccSender(true); // Set to True if you want to BCC yourself on the email.

        // Contents
        mail.setSubject('You have mail!');
        mail.setHtmlBody(htmlContent);
        mail.setUseSignature(false);

        // Send email
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }
}
How does this code works?
  1. When the VisualForce page loads, part of the JavaScript code (lines 19-25) gets executed.
  2. This calls the getJSHTMLData() method (lines 12-16) which
    1. Gets the contents of the <DIV> (lines 2-4)
    2. Calls the setApexHTML(strHTML) method with that information
  3. The setApexHTML(strHTML) method (lines 6-8) sends the data in the strHTML parameter to the SendEmail() method in the controller.
  4. The SendEmail() method
    1. Gets the data from the parameter (line 4)
    2. Creates and sends the email (line 6-32).
Althogh this code calls the Javascript when it loads, it could easily be changed to have the method called by JavaScript when an event happens (for example the onClick event of a HTML button)

Building a multi-page wizards

I recently tried to develop a set of related pages, something like a multi-page wizard where the context of the data would be preserved from one page to the next.

I started out with these ideas:

  • I tried sending the object as a parameter, but only strings (or numbers in string format) can be sent out as parameters.
  • I tried static objects but the state was not preserved.

I tried a few other tricks, but the system did not preserve the state... All the objects were being instatiated, rather than using the previous instance. I knew it could be done, because I had seen the documentation... And re-reading it I found the answer...

There is a comment for the wizzard's controller code sample in the Visualforce Developer's Guide, that reads:

... Note that the redirect attribute does not need to be set on the PageReference because the URL does not need to change when users move from page to page.

This information should be re-worded to something like this:

Note that the redirect attribute on the PageReference must not be set to true (set it to false or do not set it at all) because the URL must not change when users move from page to page.

Anyways, this is how it should be set up: VisualForce Page:

<apex:page controller="theData">
    <H1>Page1</H1>
    <apex:form >
        Data value: <apex:inputText value="{!DataText}" />
        <apex:commandButton action="{!Page1}" value="Page1" />
        <apex:commandButton action="{!Page2}" value="Page2" />
        <apex:commandButton action="{!Page3}" value="Page3" />
    </apex:form>
</apex:page>

Controller:

public class aaData {
// Data
    private String strText = null;
    public String getDataText() {
        return strText;
    }
    public void setDataText(String value) {
        strText = value;
    }

// Navigation
    public PageReference Page1() {
        return getPage('/apex/aaPage1');
    }
    public PageReference Page2() {
        return getPage('/apex/aaPage2');
    }
    public PageReference Page3() {
        return getPage('/apex/aaPage3');
    }
    private PageReference getPage(String URL) {
        PageReference page = new PageReference(URL);
        page.setRedirect(false);
        return page;
    }
}

I noticed that if the URL does not change, the state is preserved, but if the URL changes the state is lost. In addition to the "page.setRedirect(false)" (line 23 on the previous code), you must be careful as to the use of controllers and extensions... The only way I was able to not change the URL (and preserve the state) was when all the pages use exactly the same set of controllers and extensions... You may use a standard or custom controller and zero or more extensions, but every page in the wizard must use the same set of classes.

All the pages must have the same heading. Something like this:

<apex:page controller="theData">
Or this:
<apex:page controller="theData" extensions="ext1">
Or this:
<apex:page controller="theData" extensions="ext1,ext2">
Or this:
<apex:page standardController="Account" extensions="theData">
Or this:
<apex:page standardController="Account" extensions="theData,ext1">
The important thing is that all the pages in the wizard must use the same set of controllers and extensions, otherwise the state will be lost! I noticed that if I had something like this: Page 2:
<apex:page controller="theData" extensions="step2,step3">
Page 3:
<apex:page controller="theData" extensions="step3">
I was able to go from page 2 to page 3 preserving the state, but if I went back from page 3 to page 2, the state would be lost. Something simpler that you could try is not using extensions, but rather having an instance of the "extension class" in the controller "theData"... This would probably simplify your life :-)