0 Comments

In a previous post (Showing System Fields in a Custom Visualforce Page) I talked about how to show system information (Created By, Last Modified By, and Owner) in a custom visualforce page. The approach I presented works but it has some shortcomings:

  • Doesn’t work as well when the object is a detail object in a Master-Detail relationship (there is no owner here for the detail object, as the owner is the one from the master object)
  • It requires to put some fields (as hidden) in the visualforce page.

I present here a better approach that takes into consideration the above points.

Let’s start by the component controller:

public class ComponentControllerSystemInformation {
    public sObject recordValue {        
           get {
               return recordValue;
           }
           set {
               recordValue = value;
               
               Schema.DescribeSObjectResult d = recordValue.getSObjectType().getDescribe();
            String soql = 'select CreatedById,CreatedDate,LastModifiedById,LastModifiedDate' + (isMaster?',OwnerId':'') + ' from ' + d.Name + ' where Id = \'' + recordValue.Id + '\'';
            recordValue = Database.query(soql);
           }
    }
     
    public Id recordId {
        get {
            return recordValue.Id;
        }
    } 
        
    public Id createdById {
        get {
            return (Id)recordValue.get('CreatedById');
        }
    } 
     
    public String createdByName {
        get {
            User createdByUser = [select name from user where id = :createdById limit 1];
            return createdByUser == null ? null : createdByUser.Name;
        }
    }
         
    public String convertedCreatedDate {
        get {
            DateTime createdDate = (DateTime)recordValue.get('CreatedDate');
            return createdDate.format();
        }
    }
     
    public Id lastModifiedById {
        get {
            return (Id)recordValue.get('LastModifiedById');
        }
    } 
     
    public String lastModifiedByName {
        get {
            User lastModifiedByUser = [select name from user where id = :lastModifiedById limit 1];
            return lastModifiedByUser == null ? null : lastModifiedByUser.Name;
        }
    }
         
    public String convertedLastModifiedDate {
        get {
            DateTime lastModifiedDate = (DateTime)recordValue.get('LastModifiedDate');
            return lastModifiedDate.format();
        }
    }
         
    public String ownerName {
        get {
            User ownerUser = [select name from user where id = :ownerId limit 1];
            return ownerUser == null ? null : ownerUser.Name;
        }
    }
     
    public String ownerPhoto {
        get {
            try {
                Id ownerId = (Id)recordValue.get('ownerId');
                User owner = [select smallphotourl from user where id = :ownerId limit 1];
                return owner == null ? null : owner.SmallPhotoUrl;
            } catch(System.RequiredFeatureMissingException e) {
                System.debug('Chatter not enabled in organization:' + e.getMessage());
                return null;
            }
        }
    }
    
    public Boolean isMaster {
      get {
        Schema.DescribeSObjectResult d = recordValue.getSObjectType().getDescribe();
        Map<String, Schema.SObjectField> fields = d.fields.getMap();
        Schema.SObjectField field = fields.get('OwnerId');
        return field != null;
      }
    }
    
    public Id ownerId {
      get {
        if(!isMaster) return null;
        
        return (Id)recordValue.get('OwnerId');
      }
    }             
}

Notice lines 9-11 where I query the fields that are required by this component to work, removing the requirement I had in the previous solution to include those fields in the visualforce markup. Notice also in line 81 a new property isMaster which we will use to show or hide the owner information in the visualforce page.

The component can now be written as follows:

<apex:component controller="ComponentControllerSystemInformation">
    <apex:attribute name="record" assignTo="{!recordValue}"
        type="sObject" description="The object for which to display system information" required="true"/>
     
    <apex:pageBlockSection title="{!$Label.SystemInformation}" columns="2">
        <apex:pageBlockSectionItem >
            <apex:outputLabel value="{!$Label.LastModifiedBy}" />
            <apex:outputPanel >
                <apex:outputLink id="lastModifiedBy"
                    onblur="LookupHoverDetail.getHover('{!$Component.lastModifiedBy}').hide();"
                    onfocus="LookupHoverDetail.getHover('{!$Component.lastModifiedBy}', '/{!lastModifiedById}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();"
                    onmouseout="LookupHoverDetail.getHover('{!$Component.lastModifiedBy}').hide();"
                    onmouseover="LookupHoverDetail.getHover('{!$Component.lastModifiedBy}', '/{!lastModifiedById}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();"                      
                 value="{!URLFOR('/' + lastModifiedById)}">{!lastModifiedByName}</apex:outputLink>&nbsp;
                <apex:outputText value="{!convertedLastModifiedDate}" />                                                                        
            </apex:outputPanel>
        </apex:pageBlockSectionItem>
        <apex:pageBlockSectionItem rendered="{!NOT(isMaster)}" />    
        <apex:pageBlockSectionItem rendered="{!isMaster}">
            <apex:outputLabel for="owner" value="{!$Label.Owner}" />
            <apex:outputPanel >
                <apex:image value="{!ownerPhoto}" width="16" height="16"/>&nbsp;
                <apex:outputLink id="owner"
                    onblur="LookupHoverDetail.getHover('{!$Component.owner}').hide();"
                    onfocus="LookupHoverDetail.getHover('{!$Component.owner}', '/{!ownerId}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();"
                    onmouseout="LookupHoverDetail.getHover('{!$Component.owner}').hide();"
                    onmouseover="LookupHoverDetail.getHover('{!$Component.owner}', '/{!ownerId}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();"
                 value="{!URLFOR('/' + ownerId)}">{!ownerName}</apex:outputLink>&nbsp;
                <apex:outputLink value="{!URLFOR('/' + recordId + '/a?retURL=' + URLENCODE($CurrentPage.Url))}">[Change]</apex:outputLink>
            </apex:outputPanel>
        </apex:pageBlockSectionItem>       
        <apex:pageBlockSectionItem >
            <apex:outputLabel value="{!$Label.CreatedBy}" />
            <apex:outputPanel >
                <apex:outputLink id="createdBy"
                    onblur="LookupHoverDetail.getHover('{!$Component.createdBy}').hide();"
                    onfocus="LookupHoverDetail.getHover('{!$Component.createdBy}', '/{!createdById}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();"
                    onmouseout="LookupHoverDetail.getHover('{!$Component.createdBy}').hide();"
                    onmouseover="LookupHoverDetail.getHover('{!$Component.createdBy}', '/{!createdById}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();"                      
                 value="{!URLFOR('/' + createdById)}">{!createdByName}</apex:outputLink>&nbsp;
                <apex:outputText value="{!convertedCreatedDate}" />                                                                        
            </apex:outputPanel>
        </apex:pageBlockSectionItem>
    </apex:pageBlockSection>
</apex:component>

Notice lines 18 and 19, where we use the new property isMaster to show or hide the owner information based on whether this is a detail object in a Master-Detail relationship or not.

With this, the visualforce page is simplified as follows:

<apex:page standardController="Contact">
  <apex:pageBlock mode="mainDetail" >
    <c:ComponentControllerSystemInformation record="{!record}" />
  </apex:pageBlock>
</apex:page>

We include the component in line 3 inside a pageBlock tag. Our page will show the system information just as in a standard page:

systemInformation

And finally, for completeness, here is the test for the component:

@isTest
public class SystemInformationComponentTest{
  
    @isTest public static void TestComponent() {
         
        Account record = new Account(Name = 'Test');
        insert record;
        record = [select ownerId,createdById,lastModifiedById,createdDate,lastModifiedDate from account where id = :record.id];
        User owner = [select name,smallPhotoUrl from user where id = :record.ownerId];
             
        ComponentControllerSystemInformation controller = new SystemInformationComponentController();
        controller.recordValue = record;
         
        System.assertEquals(record.Id, controller.recordId);
        System.assertEquals(record.CreatedById, controller.createdById);
        System.assertEquals(record.LastModifiedById, controller.lastModifiedById);
        System.assertEquals(record.OwnerId, controller.ownerId);
        System.assertEquals(record.CreatedDate.format(), controller.convertedCreatedDate);
        System.assertEquals(record.LastModifiedDate.format(), controller.convertedLastMOdifiedDate);
        System.assertEquals(owner.Name, controller.ownerName);
        System.assertEquals(owner.SmallPhotoUrl, controller.ownerPhoto); 
        System.assertEquals(owner.Name, controller.lastModifiedByName);
        System.assertEquals(owner.Name, controller.createdByName);
        System.assertEquals(true, controller.isMaster);
    }
}

Now, our custom pages have the same functionality as a standard one.