Menu Bar

Friday 31 August 2012

Uploading a Document using Visualforce and a Custom Controller


Problem

Sometimes you may want to create a process where uploading a document is mandatory. You can create a single Visualforce page with all of the fields for the record plus the field required to upload a document. You could also develop a wizard interface that allows the user to walk through a series of steps in which one would be to upload a document.

Solution

Create a Visualforce page that references a controller (documented below). Note the enctype attribute on the <apex:form> tag. This Visualforce page uses only the fields necessary for uploading a document but can be expanded to include field for other records or documents in the process.
01<apex:page controller="FileUploadController">

02  <apex:sectionHeader title="Visualforce Example" subtitle="File Upload Example"/>

03  

04  <apex:form enctype="multipart/form-data">

05    <apex:pageMessages />

06    <apex:pageBlock title="Upload a File">

07  

08      <apex:pageBlockButtons >

09        <apex:commandButton action="{!upload}" value="Save"/>

10      </apex:pageBlockButtons>

11  

12      <apex:pageBlockSection showHeader="false" columns="2" id="block1">

13  

14        <apex:pageBlockSectionItem >

15          <apex:outputLabel value="File Name" for="fileName"/>

16          <apex:inputText value="{!document.name}" id="fileName"/>

17        </apex:pageBlockSectionItem>

18  

19        <apex:pageBlockSectionItem >

20          <apex:outputLabel value="File" for="file"/>

21          <apex:inputFile value="{!document.body}" filename="{!document.name}" id="file"/>

22        </apex:pageBlockSectionItem>

23  

24        <apex:pageBlockSectionItem >

25          <apex:outputLabel value="Description" for="description"/>

26          <apex:inputTextarea value="{!document.description}" id="description"/>

27        </apex:pageBlockSectionItem>

28  

29        <apex:pageBlockSectionItem >

30          <apex:outputLabel value="Keywords" for="keywords"/>

31          <apex:inputText value="{!document.keywords}" id="keywords"/>

32        </apex:pageBlockSectionItem>

33  

34      </apex:pageBlockSection>

35  

36    </apex:pageBlock>

37  </apex:form>

38</apex:page>

Create an Apex class that behaves as the controller in the Visualforce page. The upload() method is key.
view source
print?
01public with sharing class FileUploadController {

02  

03  public Document document {

04    get {

05      if (document == null)

06        document = new Document();

07      return document;

08    }

09    set;

10  }

11  

12  public PageReference upload() {

13  

14    document.AuthorId = UserInfo.getUserId();

15    document.FolderId = UserInfo.getUserId(); // put it in running user's folder

16  

17    try {

18      insert document;

19    } catch (DMLException e) {

20      ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,'Error uploading file'));

21      return null;

22    } finally {

23      document.body = null; // clears the viewstate

24      document = new Document();

25    }

26  

27    ApexPages.addMessage(new ApexPages.message(ApexPages.severity.INFO,'File uploaded successfully'));

28    return null;

29  }

30  

31}

Discussion

Visualforce takes care of all the hard work by updating the document from the file that's uploaded. All your controller class needs to do is store it. If you would like to attach the document to a specific record you will need to use the Attachment object instead of the Document object.

Notes

Make sure you take a look at the finally block in the controller code above. The finally block always executes when the try block exits regardless if an error occurs or not. You need to ensure you clear out document’s body (document.body = null) so that the blob is not automatically included in the serialized image of the controller. If you do not clear out the body, you’ll get the following view state error: "Maximum view state size limit (128K) exceeded. Actual viewstate size for this page was…"

Tuesday 28 August 2012

USING PROPERTIES IN APEX

Using Properties in Apex

Problem

You want to create a page that captures input from users, and that input spans multiple sObjects.

Solution

Use a Visualforce page with a custom Apex controller, and give it properties to represent the input fields from Accounts and Contacts.
  1. Create a custom controller with a simple getName method.
    1. Click Setup | Develop | Apex Classes.
    2. Click New.
    3. In the editor, add the following content:
      /*
       * This class is the controller for the 
       * NewCustomer VisualForce page.
       * It uses properties to hold values entered 
       * by the user.  These values
       * will used to construct multiple SObjects.
       */  
          
      public class Customer {
      
      // Add properties here  
          
      
          /* Required method in a VisualForce controller */  
          
          public String getName() {
              return 'Customer';
          }
      // Add methods here  
          
      // Add queries here  
          
      }
    4. Click Quick Save.
  2. Update the controller by replacing the line // Add properties here with the following Apex properties:
        public String companyName {get; set;}
        public Integer numEmployees {get; set;}
        public String streetAddress {get; set;}
        public String cityAddress {get; set;}
        public String stateAddress {get; set;}
        public String postalCodeAddress {get; set;}
        public String countryAddress {get; set;}
        public String department {get; set;}
        public String email {get; set;}
        public String phone {get; set;}
        public String firstName {get; set;}
        public String lastName {get; set;}
        public String title {get; set;}
    
  3. Click Quick Save.
  4. Update the controller by replacing the line // Add methods here with the following method for saving the property values to a new pair of Account and Contact objects:
        /*
         * Takes the values entered by the user in the VisualForce 
         * page and constructs Account and Contact sObjects.
         */  
        
        public void save() {
            Account a = new Account(
                Name = companyName,
                NumberOfEmployees = numEmployees,
                ShippingStreet = streetAddress,
                ShippingCity = cityAddress,
                ShippingState = stateAddress,
                ShippingPostalCode = postalCodeAddress,
                ShippingCountry = countryAddress);
    
            insert a;
    
            Contact c = new Contact(
                FirstName = firstName,
                LastName = lastName,
                Account = a,
                Department = department,
                Email = email,
                Phone = phone,
                Title = title,
                MailingStreet = streetAddress,
                MailingCity = cityAddress,
                MailingState = stateAddress,
                MailingPostalCode = postalCodeAddress,
                MailingCountry = countryAddress);
    
            insert c;
        }
    
    
  5. Click Quick Save.
  6. Update the controller by replacing the line // Add queries here with queries for displaying Accounts and Contacts related lists:
        /* Used for the Account list at the end of the 
           VisualForce page 
        */  
        
        public List<Account> getAccountList() {
            return [select name, numberofemployees from account];
        }
    
        /* Used for the Contact list at the end of the 
           VisualForce page 
        */  
        
        public List<Contact> getContactList() {
            return [select name, title, department, email, phone 
                    from contact];
        }
    
  7. Click Save.
  8. Click SetupDevelop | Pages.
  9. Click New.
  10. In the name field, enter newCustomerEntry.
  11. Optionally enter a label and description.
  12. In the editor, enter the following markup:
    <apex:page controller="Customer">
        <apex:form >
            <apex:pageBlock title="New Customer Entry">
                <p>First Name: 
                   <apex:inputText value="{!firstName}"/></p>
                <p>Last Name: 
                   <apex:inputText value="{!lastName}"/></p>
                <p>Company Name: 
                   <apex:inputText value="{!companyName}"/></p>
                <p># Employees: 
                   <apex:inputText value="{!numEmployees}"/></p>
                <p>Department: 
                   <apex:inputText value="{!department}"/></p>
                <p>Email: 
                   <apex:inputText value="{!email}"/></p>
                <p>Phone: 
                   <apex:inputText value="{!phone}"/></p>
                <p>Title: 
                   <apex:inputText value="{!title}"/></p>
                <p>Address</p>
                <p>Street: 
                   <apex:inputText value="{!streetAddress}"/></p>
                <p>City: 
                   <apex:inputText value="{!cityAddress}"/></p>
                <p>State: 
                   <apex:inputText value="{!stateAddress}"/></p>
                <p>Zip: 
                   <apex:inputText 
                    value="{!postalCodeAddress}"/></p>
                <p>Country: 
                   <apex:inputText value="{!countryAddress}"/></p>
    
                <p><apex:commandButton action="{!save}" 
                   value="Save New Customer"/></p>
            </apex:pageBlock>
        </apex:form>
       <!-- Add related lists here -->  
        
    </apex:page>
    
    
  13. Click Quick Save.
  14. Update the page by replacing <!-- Add related lists here --> with the following markup to displays the related lists from the queries:
        <apex:pageBlock title="Accounts">
            <apex:pageBlockTable value="{!accountList}" var="acct">
                <apex:column value="{!acct.Name}"/>
                <apex:column value="{!acct.NumberOfEmployees}"/>
            </apex:pageBlockTable>
        </apex:pageBlock>
        <apex:pageBlock title="Contacts">
            <apex:pageBlockTable value="{!contactList}" var="item">
                <apex:column value="{!item.Name}"/>
                <apex:column value="{!item.Phone}"/>
                <apex:column value="{!item.Title}"/>
                <apex:column value="{!item.Department}"/>
                <apex:column value="{!item.Email}"/>
            </apex:pageBlockTable>
        </apex:pageBlock>
    
  15. Click Save.
  16. Call the page by using the following URL: https://salesforce_instance/apex/newCustomerEntry.

Discussion

You want to ask the user to enter information about a new customer. The fields are used to create both a new account and a new contact associated with that account. Using a Visualforce page lets you present whatever user interface you want, using HTML and Visualforce markup; however, each standard controller inVisualforce corresponds to a single sObject type, such as Account or Contact. To work with more than on sObject type, you need to use a custom controller.
When using a Apex custom controller, the easiest way to do to access data on exposed by the controller is to use Apex properties. The syntax for Apexproperties is similar to C# properties. Java-style bean properties (with getters and setters that you create for each property) also work; however, the property syntax used above is much more readable, and makes it easier to distinguish the controller's properties from its actions.
Queries in a custom controller can be used to present data to the user. In this example, queries are used to create two tables that mimic related lists.
Since the form is simple HTML, you can modify it to your style either using HTML or Visualforce components.
Note that when you add a new customer and click Save, the account and contact information is displayed in the related lists on the page.